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