Stroika Library 3.0d23x
 
Loading...
Searching...
No Matches
Variant/JSON/Writer.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#include "Stroika/Foundation/StroikaPreComp.h"
5
6#include "Stroika/Foundation/Characters/FloatConversion.h"
9#include "Stroika/Foundation/Streams/TextToBinary.h"
10
11#include "Writer.h"
12
13using std::byte;
14
15using namespace Stroika::Foundation;
18using namespace Stroika::Foundation::Streams;
19
21using Memory::MakeSharedPtr;
22
23/*
24 * TODO:
25 * No known issues
26 */
27
28namespace {
29 // Just like Writer::Options but without optionals... fill those in
30 struct OptionValues_ final {
31 OptionValues_ (const Variant::JSON::Writer::Options& o)
32 : fFloatOptions{o.fFloatOptions.value_or (Characters::FloatConversion::ToStringOptions{})}
33 , fJSONPrettyPrint{o.fJSONPrettyPrint.value_or (true)}
34 , fSpacesPerIndent{o.fSpacesPerIndent.value_or (4)}
35 , fAllowNANInf{o.fAllowNANInf.value_or (true)}
36 , fLineTermination{o.fLineTermination.value_or (Characters::kEOL<char>)}
37 {
38 if (not fJSONPrettyPrint) {
39 fSpacesPerIndent = 0;
40 }
41 if (fSpacesPerIndent != 0) {
42 fIndentSpace = String{" "sv}.Repeat (fSpacesPerIndent);
43 }
44 }
46 bool fJSONPrettyPrint;
47 unsigned int fSpacesPerIndent;
48 String fIndentSpace;
49 bool fAllowNANInf;
50 String fLineTermination;
51 };
52}
53
54/*
55 ********************************************************************************
56 ********************* DataExchange::JSON::PrettyPrint **************************
57 ********************************************************************************
58 */
59namespace {
60 void Indent_ (const OptionValues_& options, const OutputStream::Ptr<Character>& out, int indentLevel)
61 {
62 // if test not needed, but speed tweak (especially since incorporates options.fJSONPrettyPrint check
63 if (options.fSpacesPerIndent != 0) {
64 constexpr bool kSpeedTweak_ = true;
65 if constexpr (kSpeedTweak_) {
66 for (int i = 0; i < indentLevel; ++i) {
67 out.Write (options.fIndentSpace);
68 }
69 }
70 else {
71 out.Write (options.fIndentSpace.Repeat (indentLevel));
72 }
73 }
74 }
75}
76namespace {
77 void PrettyPrint_ (const OptionValues_& options, const VariantValue& v, const OutputStream::Ptr<Character>& out, int indentLevel);
78 void PrettyPrint_Null_ (const OptionValues_& /*options*/, const OutputStream::Ptr<Character>& out)
79 {
80 out.Write ("null"sv);
81 }
82 void PrettyPrint_ (const OptionValues_& /*options*/, bool v, const OutputStream::Ptr<Character>& out)
83 {
84 if (v) {
85 out.Write ("true"sv);
86 }
87 else {
88 out.Write ("false"sv);
89 }
90 }
91 void PrettyPrint_ (const OptionValues_& /*options*/, long long int v, const OutputStream::Ptr<Character>& out)
92 {
93 wchar_t buf[1024];
94 (void)::swprintf (buf, std::size (buf), L"%lld", v);
95 out.Write (buf);
96 }
97 void PrettyPrint_ (const OptionValues_& /*options*/, unsigned long long int v, const OutputStream::Ptr<Character>& out)
98 {
99 wchar_t buf[1024];
100 (void)::swprintf (buf, std::size (buf), L"%llu", v);
101 out.Write (buf);
102 }
103 void PrettyPrint_ (const OptionValues_& options, const String& v, const OutputStream::Ptr<Character>& out);
104 void PrettyPrint_ (const OptionValues_& options, long double v, const OutputStream::Ptr<Character>& out)
105 {
106 String tmp{Characters::FloatConversion::ToString (v, options.fFloatOptions)};
107 if (isnan (v) or isinf (v)) {
108 Require (options.fAllowNANInf);
109 PrettyPrint_ (options, tmp, out);
110 }
111 else {
112 out.Write (tmp);
113 }
114 }
115 template <typename CHARITERATOR>
116 void PrettyPrint_ (const OptionValues_& /*options*/, CHARITERATOR start, CHARITERATOR end, const OutputStream::Ptr<Character>& out)
117 {
118 // A backslash can be followed by "\/bfnrtu (@ see http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf)
120 sb.Append ('\"');
121 for (auto i = start; i != end; ++i) {
122 switch (*i) {
123 case '\"':
124 sb.Append ("\\\""sv);
125 break;
126 case '\b':
127 sb.Append ("\\b"sv);
128 break;
129 case '\f':
130 sb.Append ("\\f"sv);
131 break;
132 case '\n':
133 sb.Append ("\\n"sv);
134 break;
135 case '\r':
136 sb.Append ("\\r"sv);
137 break;
138 case '\t':
139 sb.Append ("\\t"sv);
140 break;
141 case '\\':
142 sb.Append ("\\\\"sv);
143 break;
144 default:
145 // JSON rule is Any code point except " or \ or control character. So OK to emit most large unicode chars - just not control chars
146 Character c = *i;
147 if (c.IsSurrogatePair ()) [[unlikely]] {
148 wchar_t buf[20];
149 (void)::swprintf (buf, std::size (buf), L"\\u%04x\\u%04x", c.GetSurrogatePair ().first, c.GetSurrogatePair ().second);
150 sb.Append (buf);
151 }
152 else if (c.IsControl ()) [[unlikely]] {
153 wchar_t buf[10];
154 (void)::swprintf (buf, std::size (buf), L"\\u%04x", static_cast<char16_t> (c.GetCharacterCode ()));
155 sb.Append (buf);
156 }
157 else {
158 sb.Append (c);
159 }
160 break;
161 }
162 }
163 sb.Append ('\"');
164 Memory::StackBuffer<wchar_t> probablyIgnoredBuf;
165 out.Write (sb.GetData (&probablyIgnoredBuf));
166 }
167 void PrettyPrint_ (const OptionValues_& options, const wstring& v, const OutputStream::Ptr<Character>& out)
168 {
169 PrettyPrint_ (options, v.begin (), v.end (), out);
170 }
171 void PrettyPrint_ (const OptionValues_& options, const String& v, const OutputStream::Ptr<Character>& out)
172 {
174 span<const char32_t> p = v.GetData (&ignored);
175 PrettyPrint_ (options, p.data (), p.data () + p.size (), out);
176 }
177 void PrettyPrint_ (const OptionValues_& options, const vector<VariantValue>& v, const OutputStream::Ptr<Character>& out, int indentLevel)
178 {
179 out.Write ('[');
180 if (options.fJSONPrettyPrint) {
181 out.Write (options.fLineTermination);
182 }
183 for (auto i = v.begin (); i != v.end (); ++i) {
184 Indent_ (options, out, indentLevel + 1);
185 PrettyPrint_ (options, *i, out, indentLevel + 1);
186 if (i + 1 != v.end ()) {
187 out.Write (',');
188 }
189 if (options.fJSONPrettyPrint) {
190 out.Write (options.fLineTermination);
191 }
192 }
193 Indent_ (options, out, indentLevel);
194 out.Write (']');
195 }
196 void PrettyPrint_ (const OptionValues_& options, const Mapping<String, VariantValue>& v, const OutputStream::Ptr<Character>& out, int indentLevel)
197 {
198 out.Write ('{');
199 if (options.fJSONPrettyPrint) {
200 out.Write (options.fLineTermination);
201 }
202 for (auto i = v.begin (); i != v.end ();) {
203 Indent_ (options, out, indentLevel + 1);
204 PrettyPrint_ (options, i->fKey, out, indentLevel + 1);
205 out.Write (" : "sv);
206 PrettyPrint_ (options, i->fValue, out, indentLevel + 1);
207 ++i;
208 if (i != v.end ()) {
209 out.Write (',');
210 }
211 if (options.fJSONPrettyPrint) {
212 out.Write (options.fLineTermination);
213 }
214 }
215 Indent_ (options, out, indentLevel);
216 out.Write ('}');
217 }
218 void PrettyPrint_ (const OptionValues_& options, const VariantValue& v, const OutputStream::Ptr<Character>& out, int indentLevel)
219 {
220 switch (v.GetType ()) {
221 case VariantValue::eNull:
222 PrettyPrint_Null_ (options, out);
223 break;
224 case VariantValue::eBoolean:
225 PrettyPrint_ (options, v.As<bool> (), out);
226 break;
227 case VariantValue::eInteger:
228 PrettyPrint_ (options, v.As<long long int> (), out);
229 break;
230 case VariantValue::eUnsignedInteger:
231 PrettyPrint_ (options, v.As<unsigned long long int> (), out);
232 break;
233 case VariantValue::eFloat:
234 PrettyPrint_ (options, v.As<long double> (), out);
235 break;
236 case VariantValue::eMap:
237 PrettyPrint_ (options, v.As<Mapping<String, VariantValue>> (), out, indentLevel);
238 break;
239 case VariantValue::eArray:
240 PrettyPrint_ (options, v.As<vector<VariantValue>> (), out, indentLevel);
241 break;
242 default:
243 PrettyPrint_ (options, v.As<String> (), out);
244 }
245 }
246}
247
248/*
249 ********************************************************************************
250 ************************** DataExchange::JSON::Writer **************************
251 ********************************************************************************
252 */
253class Variant::JSON::Writer::Rep_ final : public Variant::Writer::_IRep, Memory::UseBlockAllocationIfAppropriate<Rep_> {
254public:
255 OptionValues_ fOptions_;
256 Rep_ (const Options& options)
257 : fOptions_{options}
258 {
259 }
260 Rep_ (const OptionValues_& options)
261 : fOptions_{options}
262 {
263 }
264 virtual _SharedPtrIRep Clone () const override
265 {
266 return MakeSharedPtr<Rep_> (fOptions_); // no instance data
267 }
268 virtual optional<filesystem::path> GetDefaultFileSuffix () const override
269 {
270 return ".json"sv;
271 }
272 virtual void Write (const VariantValue& v, const OutputStream::Ptr<byte>& out) const override
273 {
274 OutputStream::Ptr<Character> textOut = TextToBinary::Writer::New (out, UnicodeExternalEncodings::eUTF8, ByteOrderMark::eDontInclude);
275 PrettyPrint_ (fOptions_, v, textOut, 0);
276 if (fOptions_.fJSONPrettyPrint) {
277 textOut.Write (fOptions_.fLineTermination); // a single elt not LF terminated, but the entire doc should be.
278 }
279 }
280 virtual void Write (const VariantValue& v, const OutputStream::Ptr<Character>& out) const override
281 {
282 PrettyPrint_ (fOptions_, v, out, 0);
283 }
284};
285
286Variant::JSON::Writer::Writer (const Options& options)
287 : inherited{MakeSharedPtr<Rep_> (options)}
288{
289}
auto MakeSharedPtr(ARGS_TYPE &&... args) -> shared_ptr< T >
same as make_shared, but if type T has block allocation, then use block allocation for the 'shared pa...
conditional_t< qStroika_Foundation_Memory_PreferBlockAllocation and andTrueCheck, BlockAllocationUseHelper< T >, Common::Empty > UseBlockAllocationIfAppropriate
Use this to enable block allocation for a particular class. Beware of subclassing.
constexpr bool IsControl() const noexcept
constexpr char32_t GetCharacterCode() const noexcept
Return the char32_t UNICODE code-point associated with this character.
constexpr pair< char16_t, char16_t > GetSurrogatePair() const
Similar to String, but intended to more efficiently construct a String. Mutable type (String is large...
nonvirtual span< const CHAR_T > GetData(Memory::StackBuffer< CHAR_T > *probablyIgnoredBuf) const
access a span of data located inside the StringBuilder. Return internal pointer, or pointer internal ...
nonvirtual void Append(span< const CHAR_T > s)
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
nonvirtual String Repeat(unsigned int count) const
Definition String.cpp:1425
static span< const CHAR_TYPE > GetData(const PeekSpanData &pds, Memory::StackBuffer< CHAR_TYPE, STACK_BUFFER_SZ > *possiblyUsedBuffer)
return the constant character data inside the string (rep) in the form of a span, possibly quickly an...
Definition String.inl:967
Simple variant-value (case variant union) object, with (variant) basic types analogous to a value in ...
Logically halfway between std::array and std::vector; Smart 'direct memory array' - which when needed...
OutputStream<>::Ptr is Smart pointer to a stream-based sink of data.
nonvirtual void Write(span< ELEMENT_TYPE2, EXTENT_2 > elts) const
nonvirtual Iterator< T > begin() const
Support for ranged for, and STL syntax in general.
static constexpr default_sentinel_t end() noexcept
Support for ranged for, and STL syntax in general.
STRING_TYPE ToString(FLOAT_TYPE f, const ToStringOptions &options={})
static constexpr T kEOL[]
null-terminated String constant for current compiled platform - Windows (CRLF) or POSIX (NL) - macos ...
Definition LineEndings.h:20
Ptr New(const Streams::OutputStream::Ptr< byte > &src, const Characters::CodeCvt<> &char2OutputConverter)