Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
JSON/Writer.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#include "Stroika/Foundation/StroikaPreComp.h"
5
6#include "Stroika/Foundation/Characters/FloatConversion.h"
8#include "Stroika/Foundation/Streams/TextToBinary.h"
9
10#include "Writer.h"
11
12using std::byte;
13
14using namespace Stroika::Foundation;
17using namespace Stroika::Foundation::Streams;
18
20
21/*
22 * TODO:
23 * No known issues
24 */
25
26namespace {
27 // Just like Writer::Options but without optionals... fill those in
28 struct OptionValues_ {
29 OptionValues_ (const Variant::JSON::Writer::Options& o)
30 : fFloatOptions{o.fFloatOptions.value_or (Characters::FloatConversion::ToStringOptions{})}
31 , fJSONPrettyPrint{o.fJSONPrettyPrint.value_or (true)}
32 , fSpacesPerIndent{o.fSpacesPerIndent.value_or (4)}
33 , fAllowNANInf{o.fAllowNANInf.value_or (true)}
34 , fLineTermination{o.fLineTermination.value_or (Characters::kEOL<char>)}
35 {
36 if (not fJSONPrettyPrint) {
37 fSpacesPerIndent = 0;
38 }
39 if (fSpacesPerIndent != 0) {
40 fIndentSpace = String{" "sv}.Repeat (fSpacesPerIndent);
41 }
42 }
44 bool fJSONPrettyPrint;
45 unsigned int fSpacesPerIndent;
46 String fIndentSpace;
47 bool fAllowNANInf;
48 String fLineTermination;
49 };
50}
51
52/*
53 ********************************************************************************
54 ********************* DataExchange::JSON::PrettyPrint **************************
55 ********************************************************************************
56 */
57namespace {
58 void Indent_ (const OptionValues_& options, const OutputStream::Ptr<Character>& out, int indentLevel)
59 {
60 // if test not needed, but speed tweak (especially since incorporates options.fJSONPrettyPrint check
61 if (options.fSpacesPerIndent != 0) {
62 constexpr bool kSpeedTweak_ = true;
63 if constexpr (kSpeedTweak_) {
64 for (int i = 0; i < indentLevel; ++i) {
65 out.Write (options.fIndentSpace);
66 }
67 }
68 else {
69 out.Write (options.fIndentSpace.Repeat (indentLevel));
70 }
71 }
72 }
73}
74namespace {
75 void PrettyPrint_ (const OptionValues_& options, const VariantValue& v, const OutputStream::Ptr<Character>& out, int indentLevel);
76 void PrettyPrint_Null_ (const OptionValues_& /*options*/, const OutputStream::Ptr<Character>& out)
77 {
78 out.Write ("null"sv);
79 }
80 void PrettyPrint_ (const OptionValues_& /*options*/, bool v, const OutputStream::Ptr<Character>& out)
81 {
82 if (v) {
83 out.Write ("true"sv);
84 }
85 else {
86 out.Write ("false"sv);
87 }
88 }
89 void PrettyPrint_ (const OptionValues_& /*options*/, long long int v, const OutputStream::Ptr<Character>& out)
90 {
91 wchar_t buf[1024];
92 (void)::swprintf (buf, Memory::NEltsOf (buf), L"%lld", v);
93 out.Write (buf);
94 }
95 void PrettyPrint_ (const OptionValues_& /*options*/, unsigned long long int v, const OutputStream::Ptr<Character>& out)
96 {
97 wchar_t buf[1024];
98 (void)::swprintf (buf, Memory::NEltsOf (buf), L"%llu", v);
99 out.Write (buf);
100 }
101 void PrettyPrint_ (const OptionValues_& options, const String& v, const OutputStream::Ptr<Character>& out);
102 void PrettyPrint_ (const OptionValues_& options, long double v, const OutputStream::Ptr<Character>& out)
103 {
104 String tmp{Characters::FloatConversion::ToString (v, options.fFloatOptions)};
105 if (isnan (v) or isinf (v)) {
106 Require (options.fAllowNANInf);
107 PrettyPrint_ (options, tmp, out);
108 }
109 else {
110 out.Write (tmp);
111 }
112 }
113 template <typename CHARITERATOR>
114 void PrettyPrint_ (const OptionValues_& /*options*/, CHARITERATOR start, CHARITERATOR end, const OutputStream::Ptr<Character>& out)
115 {
116 // A backslash can be followed by "\/bfnrtu (@ see http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf)
118 sb.Append ('\"');
119 for (auto i = start; i != end; ++i) {
120 switch (*i) {
121 case '\"':
122 sb.Append ("\\\""sv);
123 break;
124 case '\b':
125 sb.Append ("\\b"sv);
126 break;
127 case '\f':
128 sb.Append ("\\f"sv);
129 break;
130 case '\n':
131 sb.Append ("\\n"sv);
132 break;
133 case '\r':
134 sb.Append ("\\r"sv);
135 break;
136 case '\t':
137 sb.Append ("\\t"sv);
138 break;
139 case '\\':
140 sb.Append ("\\\\"sv);
141 break;
142 default:
143 // JSON rule is Any code point except " or \ or control character. So OK to emit most large unicode chars - just not control chars
144 Character c = *i;
145 if (c.IsSurrogatePair ()) [[unlikely]] {
146 wchar_t buf[20];
147 (void)::swprintf (buf, Memory::NEltsOf (buf), L"\\u%04x\\u%04x", c.GetSurrogatePair ().first, c.GetSurrogatePair ().second);
148 sb.Append (buf);
149 }
150 else if (c.IsControl ()) [[unlikely]] {
151 wchar_t buf[10];
152 (void)::swprintf (buf, Memory::NEltsOf (buf), L"\\u%04x", static_cast<char16_t> (c.GetCharacterCode ()));
153 sb.Append (buf);
154 }
155 else {
156 sb.Append (c);
157 }
158 break;
159 }
160 }
161 sb.Append ('\"');
162 Memory::StackBuffer<wchar_t> probablyIgnoredBuf;
163 out.Write (sb.GetData (&probablyIgnoredBuf));
164 }
165 void PrettyPrint_ (const OptionValues_& options, const wstring& v, const OutputStream::Ptr<Character>& out)
166 {
167 PrettyPrint_ (options, v.begin (), v.end (), out);
168 }
169 void PrettyPrint_ (const OptionValues_& options, const String& v, const OutputStream::Ptr<Character>& out)
170 {
172 span<const char32_t> p = v.GetData (&ignored);
173 PrettyPrint_ (options, p.data (), p.data () + p.size (), out);
174 }
175 void PrettyPrint_ (const OptionValues_& options, const vector<VariantValue>& v, const OutputStream::Ptr<Character>& out, int indentLevel)
176 {
177 out.Write ('[');
178 if (options.fJSONPrettyPrint) {
179 out.Write (options.fLineTermination);
180 }
181 for (auto i = v.begin (); i != v.end (); ++i) {
182 Indent_ (options, out, indentLevel + 1);
183 PrettyPrint_ (options, *i, out, indentLevel + 1);
184 if (i + 1 != v.end ()) {
185 out.Write (',');
186 }
187 if (options.fJSONPrettyPrint) {
188 out.Write (options.fLineTermination);
189 }
190 }
191 Indent_ (options, out, indentLevel);
192 out.Write (']');
193 }
194 void PrettyPrint_ (const OptionValues_& options, const Mapping<String, VariantValue>& v, const OutputStream::Ptr<Character>& out, int indentLevel)
195 {
196 out.Write ('{');
197 if (options.fJSONPrettyPrint) {
198 out.Write (options.fLineTermination);
199 }
200 for (auto i = v.begin (); i != v.end ();) {
201 Indent_ (options, out, indentLevel + 1);
202 PrettyPrint_ (options, i->fKey, out, indentLevel + 1);
203 out.Write (" : "sv);
204 PrettyPrint_ (options, i->fValue, out, indentLevel + 1);
205 ++i;
206 if (i != v.end ()) {
207 out.Write (',');
208 }
209 if (options.fJSONPrettyPrint) {
210 out.Write (options.fLineTermination);
211 }
212 }
213 Indent_ (options, out, indentLevel);
214 out.Write ('}');
215 }
216 void PrettyPrint_ (const OptionValues_& options, const VariantValue& v, const OutputStream::Ptr<Character>& out, int indentLevel)
217 {
218 switch (v.GetType ()) {
219 case VariantValue::eNull:
220 PrettyPrint_Null_ (options, out);
221 break;
222 case VariantValue::eBoolean:
223 PrettyPrint_ (options, v.As<bool> (), out);
224 break;
225 case VariantValue::eInteger:
226 PrettyPrint_ (options, v.As<long long int> (), out);
227 break;
228 case VariantValue::eUnsignedInteger:
229 PrettyPrint_ (options, v.As<unsigned long long int> (), out);
230 break;
231 case VariantValue::eFloat:
232 PrettyPrint_ (options, v.As<long double> (), out);
233 break;
234 case VariantValue::eMap:
235 PrettyPrint_ (options, v.As<Mapping<String, VariantValue>> (), out, indentLevel);
236 break;
237 case VariantValue::eArray:
238 PrettyPrint_ (options, v.As<vector<VariantValue>> (), out, indentLevel);
239 break;
240 default:
241 PrettyPrint_ (options, v.As<String> (), out);
242 }
243 }
244}
245
246/*
247 ********************************************************************************
248 ************************** DataExchange::JSON::Writer **************************
249 ********************************************************************************
250 */
251class Variant::JSON::Writer::Rep_ : public Variant::Writer::_IRep {
252public:
253 OptionValues_ fOptions_;
254 Rep_ (const Options& options)
255 : fOptions_{options}
256 {
257 }
258 Rep_ (const OptionValues_& options)
259 : fOptions_{options}
260 {
261 }
262 virtual _SharedPtrIRep Clone () const override
263 {
264 return make_shared<Rep_> (fOptions_); // no instance data
265 }
266 virtual optional<filesystem::path> GetDefaultFileSuffix () const override
267 {
268 return ".json"sv;
269 }
270 virtual void Write (const VariantValue& v, const OutputStream::Ptr<byte>& out) const override
271 {
272 OutputStream::Ptr<Character> textOut = TextToBinary::Writer::New (out, UnicodeExternalEncodings::eUTF8, ByteOrderMark::eDontInclude);
273 PrettyPrint_ (fOptions_, v, textOut, 0);
274 if (fOptions_.fJSONPrettyPrint) {
275 textOut.Write (fOptions_.fLineTermination); // a single elt not LF terminated, but the entire doc should be.
276 }
277 }
278 virtual void Write (const VariantValue& v, const OutputStream::Ptr<Character>& out) const override
279 {
280 PrettyPrint_ (fOptions_, v, out, 0);
281 }
282};
283
284Variant::JSON::Writer::Writer (const Options& options)
285 : inherited{make_shared<Rep_> (options)}
286{
287}
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:1424
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:961
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