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