4#include "Stroika/Foundation/StroikaPreComp.h"
9#if __has_include("boost/json.hpp")
10#include <boost/json.hpp>
11#include <boost/json/basic_parser_impl.hpp>
14#include "Stroika/Foundation/Characters/FloatConversion.h"
16#include "Stroika/Foundation/Characters/String2Int.h"
18#include "Stroika/Foundation/Containers/Concrete/Mapping_HashTable.h"
19#include "Stroika/Foundation/Containers/Support/ReserveTweaks.h"
20#include "Stroika/Foundation/DataExchange/BadFormatException.h"
34using namespace Stroika::Foundation::Memory;
35using namespace Stroika::Foundation::Streams;
36using namespace Stroika::Foundation::Traversal;
39using Memory::MakeSharedPtr;
62 struct MyBufferedStreamReader_ final :
StreamReader<Character> {
67 [[nodiscard]]
inline char32_t NextChar ()
69 Require (not IsAtEOF ());
72 inline void AdvanceOne ()
74 Require (not IsAtEOF ());
75 Seek (eFromCurrent, 1);
77 inline void BackupOne ()
79 Seek (eFromCurrent, -1);
83 VariantValue Reader_value_ (MyBufferedStreamReader_& in);
87 inline bool IsJSONSpace_ (
char32_t c)
110 inline bool IsJSONDigit_ (
char32_t c)
135 uint8_t HexChar2Num_ (
char c)
137 if (
'0' <= c and c <=
'9') [[likely]] {
138 return static_cast<uint8_t
> (c -
'0');
140 if (
'a' <= c and c <=
'f') [[likely]] {
141 return static_cast<uint8_t
> ((c -
'a') + 10);
143 if (
'A' <= c and c <=
'F') [[likely]] {
144 return static_cast<uint8_t
> ((c -
'A') + 10);
151 String Reader_String_ (MyBufferedStreamReader_& in)
153 Require (not in.IsAtEOF ());
154 char32_t c = in.NextChar ();
155 if (c !=
'\"') [[unlikely]] {
164 if (in.IsAtEOF ()) [[unlikely]] {
165 static const auto kException_{
BadFormatException{
"JSON: Unexpected EOF reading string (looking for close quote)"sv}};
169 if (c ==
'\"') [[unlikely]] {
170 return result.
str ();
172 else if (c ==
'\\') [[unlikely]] {
174 if (in.IsAtEOF ()) [[unlikely]] {
175 static const auto kException_{
BadFormatException{
"JSON: Unexpected EOF reading string (looking for close quote)"sv}};
197 char32_t newC =
'\0';
198 for (
int n = 0; n < 4; ++n) {
199 if (in.IsAtEOF ()) [[unlikely]] {
200 static const auto kException_{
BadFormatException{
"JSON: Unexpected EOF reading string (looking for close quote)"sv}};
203 newC += HexChar2Num_ (
static_cast<char> (in.NextChar ()));
221 VariantValue Reader_Number_ (
char32_t initialChar, MyBufferedStreamReader_& in)
223 Require (initialChar ==
'-' or IsJSONDigit_ (initialChar));
225 bool containsDot =
false;
229 for (
char32_t c = initialChar; c !=
'\0'; c = in.ReadBlocking ().value_or (
'\0').As<char32_t> ()) {
230 if (IsJSONDigit_ (c) or c ==
'.' or c ==
'e' or c ==
'E' or c ==
'+' or c ==
'-') [[likely]] {
232 if (c ==
'.') [[unlikely]] {
239 Assert (not tmp.
empty ());
244 Assert (not tmp.
empty ());
246 span<const char32_t> tmpData = tmp.
GetData (&ignoreBuf);
248 return VariantValue{FloatConversion::ToFloat<long double> (tmpData)};
252 return (initialChar == kDash_) ?
VariantValue{String2Int<long long int> (tmpData)}
253 :
VariantValue{String2Int<unsigned long long int> (tmpData)};
258 VariantValue Reader_Object_ (MyBufferedStreamReader_& in)
269 LookingFor lf = eName;
270 optional<String> curName;
272 optional<Character> oNextChar = in.Peek ();
273 if (not oNextChar.has_value ()) [[unlikely]] {
274 static const auto kException_{
BadFormatException{
"JSON: Unexpected EOF reading object (looking for '}')"sv}};
277 char32_t nextChar = oNextChar->As<
char32_t> ();
278 if (IsJSONSpace_ (nextChar)) [[likely]] {
284 Assert (curName == nullopt);
285 if (nextChar ==
'}') {
289 else if (nextChar ==
'\"') [[likely]] {
290 curName = Reader_String_ (in);
294 static const auto kException_{
295 BadFormatException{
"JSON: Reading object, looking for a name, didn't find a close brace or open quote"sv}};
300 Assert (curName == nullopt);
301 if (nextChar ==
'}') {
305 else if (nextChar ==
',') [[likely]] {
310 static const auto kException_{
BadFormatException{
"JSON: Reading object, looking for a comma, but found something else"sv}};
316 if (nextChar ==
':') [[likely]] {
321 static const auto kException_{
BadFormatException{
"JSON: Reading object, looking for a colon, but found something else"sv}};
337 VariantValue Reader_Array_ (MyBufferedStreamReader_& in)
339 vector<VariantValue> result;
342 bool lookingForElt =
true;
344 if (in.IsAtEOF ()) [[unlikely]] {
345 static const auto kException_{
BadFormatException{
"JSON: Unexpected EOF reading array (looking for ']')"sv}};
348 char32_t peekedChar = in.Peek ()->As<
char32_t> ();
349 if (peekedChar ==
']') {
356 else if (peekedChar ==
',') {
357 if (lookingForElt) [[unlikely]] {
358 static const auto kException_{
BadFormatException{
"JSON: Unexpected second ',' in reading array"sv}};
362 lookingForElt =
true;
366 else if (IsJSONSpace_ (peekedChar)) {
371 if (lookingForElt) [[likely]] {
372 Containers::Support::ReserveTweaks::Reserve4Add1 (result);
373 result.push_back (Reader_value_ (in));
374 lookingForElt =
false;
377 static const auto kException_{
BadFormatException{
"JSON: Unexpected character (missing ',' ?) in reading array"sv}};
384 VariantValue Reader_SpecialToken_ (
char32_t initialChar, MyBufferedStreamReader_& in)
386 switch (initialChar) {
389 if (in.ReadAll (begin (buf), end (buf)) == 4 and buf[0] ==
'a' and buf[1] ==
'l' and buf[2] ==
's' and buf[3] ==
'e') {
395 if (in.ReadAll (begin (buf), end (buf)) == 3 and buf[0] ==
'r' and buf[1] ==
'u' and buf[2] ==
'e') {
401 if (in.ReadAll (begin (buf), end (buf)) == 3 and buf[0] ==
'u' and buf[1] ==
'l' and buf[2] ==
'l') {
410 VariantValue Reader_value_ (MyBufferedStreamReader_& in)
420 for (optional<Character> oc = in.ReadBlocking (); oc; oc = in.ReadBlocking ()) {
421 switch (oc->As<
char32_t> ()) {
424 return Reader_String_ (in);
437 return Reader_Number_ (oc->As<
char32_t> (), in);
440 return Reader_Object_ (in);
442 return Reader_Array_ (in);
447 return Reader_SpecialToken_ (oc->As<
char32_t> (), in);
450 if (IsJSONSpace_ (oc->As<
char32_t> ())) [[likely]] {
454 static const auto kException_{
BadFormatException{
"JSON: Unexpected character looking for start of value"sv}};
461 static const auto kException_{
BadFormatException{
"JSON: Unexpected EOF looking for value"sv}};
471class Variant::JSON::Reader::NativeRep_ :
public Variant::Reader::_IRep {
473 NativeRep_ () =
default;
474 NativeRep_ (
const NativeRep_&) =
default;
475 virtual _SharedPtrIRep Clone ()
const override
477 return MakeSharedPtr<NativeRep_> (*
this);
479 virtual optional<filesystem::path> GetDefaultFileSuffix ()
const override
489#if USE_NOISY_TRACE_IN_THIS_MODULE_
492 MyBufferedStreamReader_ reader{in.
IsSeekable () ? in : BufferedInputStream::New (in, SeekableFlag::eSeekable)};
493 return Reader_value_ (reader);
496#if __has_include("boost/json.hpp")
500 struct BoostSAXHandler_ {
502 static constexpr std::size_t max_array_size =
static_cast<size_t> (-1);
505 static constexpr std::size_t max_object_size =
static_cast<size_t> (-1);
508 static constexpr std::size_t max_string_size =
static_cast<size_t> (-1);
511 static constexpr std::size_t max_key_size =
static_cast<size_t> (-1);
513 BoostSAXHandler_ () =
default;
514 BoostSAXHandler_ (
const BoostSAXHandler_&) =
delete;
516 bool on_document_begin ([[maybe_unused]] error_code& ec)
518 Assert (fStack_.empty ());
519 fStack_.emplace (Context_::eSimple);
522 bool on_document_end ([[maybe_unused]] error_code& ec)
524 Assert (fStack_.size () == 1);
525 this->PopContext_ ();
526 Assert (fStack_.empty ());
530 bool on_array_begin ([[maybe_unused]] error_code& ec)
532 fStack_.emplace (Context_::eArray);
535 bool on_array_end ([[maybe_unused]] std::size_t n, [[maybe_unused]] error_code& ec)
537 Assert (fStack_.top ().GetContextType () == Context_::eArray);
538 Assert (fStack_.top ().PeekAccumVector_ ().size () == n);
543 bool on_object_begin ([[maybe_unused]] error_code& ec)
545 fStack_.emplace (Context_::eMap);
548 bool on_object_end ([[maybe_unused]] std::size_t n, [[maybe_unused]] error_code& ec)
550 Assert (fStack_.top ().GetContextType () == Context_::eMap);
551 Assert (fStack_.top ().PeekAccumObj_ ().size () == n);
556 bool on_string_part (string_view s, [[maybe_unused]] std::size_t n, [[maybe_unused]] error_code& ec)
558 fPartSaver_.push_back (span<const char>{s});
561 bool on_string (string_view s, [[maybe_unused]] std::size_t n, [[maybe_unused]] error_code& ec)
564 if (s.size () == n) {
568 fPartSaver_.push_back (span<const char>{s});
569 String res = toStroikaString_ (fPartSaver_);
570 fPartSaver_.clear ();
576 bool on_key_part (string_view s, [[maybe_unused]] std::size_t n, [[maybe_unused]] error_code& ec)
579 Assert (fStack_.top ().GetContextType () == Context_::eMap);
580 fPartSaver_.push_back (span<const char>{s});
583 bool on_key (string_view s, [[maybe_unused]] std::size_t n, [[maybe_unused]] error_code& ec)
585 Assert (fStack_.top ().GetContextType () == Context_::eMap);
586 Assert (s.size () <= n);
588 if (s.size () == n) {
589 fStack_.top ().fKey = toStroikaString_ (s);
592 fPartSaver_.push_back (span<const char>{s});
593 fStack_.top ().fKey = toStroikaString_ (fPartSaver_);
594 fPartSaver_.clear ();
599 bool on_number_part ([[maybe_unused]] string_view s, [[maybe_unused]] error_code& ec)
604 bool on_int64 (int64_t i, [[maybe_unused]] string_view s, [[maybe_unused]] error_code& ec)
609 bool on_uint64 (uint64_t u, [[maybe_unused]] string_view s, [[maybe_unused]] error_code& ec)
614 bool on_double (
double d, [[maybe_unused]] string_view s, [[maybe_unused]] error_code& ec)
620 bool on_bool (
bool b, [[maybe_unused]] error_code& ec)
626 bool on_null ([[maybe_unused]] error_code& ec)
632 bool on_comment_part ([[maybe_unused]] string_view s, [[maybe_unused]] error_code& ec)
637 bool on_comment ([[maybe_unused]] string_view s, [[maybe_unused]] error_code& ec)
645 template <
typename CONTAINER_OF_CHAR_BUT_REALLY_UTF8>
646 static String toStroikaString_ (CONTAINER_OF_CHAR_BUT_REALLY_UTF8 sv)
647 requires requires (CONTAINER_OF_CHAR_BUT_REALLY_UTF8 t) {
648 { span<const char>{t} };
651 return String{SpanBytesCast<span<const char8_t>> (span<const char>{sv})};
656 return fCompletedFinalValue_;
660 Assert (not fStack_.empty ());
661 Context_& t = fStack_.top ();
662 switch (t.GetContextType ()) {
663 case Context_::eArray:
664 t.PeekAccumVector_ ().push_back (v);
667 t.PeekAccumObj_ ().insert ({t.fKey, v});
669 case Context_::eSimple:
670 t.PeekSimpleValue_ () = v;
678 Assert (not fStack_.empty ());
680 Context_& t = fStack_.top ();
682 switch (t.GetContextType ()) {
683 case Context_::eArray:
687 case Context_::eSimple:
688 return t.PeekSimpleValue_ ();
695 if (fStack_.empty ()) {
696 fCompletedFinalValue_ = vv;
699 AddCompleteValue_ (vv);
715 Context_ () =
delete;
716 Context_ (ContextType_ ct)
723 fVV_ = vector<VariantValue>{};
729 Ensure (ct == GetContextType ());
731 ~Context_ () =
default;
732 Context_ (
const Context_&) =
delete;
733 Context_ (Context_&&) =
default;
735 ContextType_ GetContextType ()
const
737 return static_cast<ContextType_
> (fVV_.index ());
740 vector<VariantValue>& PeekAccumVector_ ()
742 Require (GetContextType () == eArray);
743 return get<vector<VariantValue>> (fVV_);
747 Require (GetContextType () == eMap);
748 return get<Mapping_HashTable<String, VariantValue>::DEFAULT_HASHTABLE<>> (fVV_);
752 Require (GetContextType () == eSimple);
753 return get<VariantValue> (fVV_);
762 std::stack<Context_> fStack_;
770class Variant::JSON::Reader::BoostRep_ :
public Variant::Reader::_IRep {
772 BoostRep_ () =
default;
773 BoostRep_ (
const BoostRep_&) =
default;
774 virtual _SharedPtrIRep Clone ()
const override
776 return MakeSharedPtr<BoostRep_> (*
this);
778 virtual optional<filesystem::path> GetDefaultFileSuffix ()
const override
784#if USE_NOISY_TRACE_IN_THIS_MODULE_
787 using namespace boost;
791 constexpr bool kUseSAX_ =
true;
793 if constexpr (kUseSAX_) {
794 json::basic_parser<BoostSAXHandler_> p{json::parse_options{}};
795 const size_t targetChunkSize = std::size (buf);
796 size_t actualChunkSize;
797 boost::system::error_code ec;
798 while ((actualChunkSize = useInStream.
ReadBlocking (span{buf, targetChunkSize}).size ()) != 0) {
800 size_t nParsed = p.write_some (
true,
reinterpret_cast<const char*
> (begin (buf)), actualChunkSize, ec);
801 Assert (nParsed <= actualChunkSize);
802 if (nParsed < actualChunkSize) {
814 p.write_some (
false,
nullptr, 0, ec);
815 if (ec and ec != boost::json::error::extra_data ) {
819 return p.handler ().GetConstructedValue ();
822 json::stream_parser p;
823 const size_t targetChunkSize = std::size (buf);
824 size_t actualChunkSize;
825 while ((actualChunkSize = useInStream.
ReadBlocking (span{buf, targetChunkSize}).size ()) != 0) {
826 boost::system::error_code ec;
827 size_t nParsed = p.write_some (
reinterpret_cast<const char*
> (begin (buf)), actualChunkSize, ec);
828 Assert (nParsed <= actualChunkSize);
829 if (nParsed < actualChunkSize) {
853#if USE_NOISY_TRACE_IN_THIS_MODULE_
857 return Read (_ToByteReader (in));
862inline auto Variant::JSON::Reader::mk_ (
const ReaderOptions& options) -> shared_ptr<_IRep>
864 switch (options.fPreferredAlgorithm.value_or (ReaderOptions::Algorithm::eDEFAULT)) {
865 case ReaderOptions::Algorithm::eStroikaNative:
866 return MakeSharedPtr<NativeRep_> ();
867#if __has_include("boost/json.hpp")
868 case ReaderOptions::Algorithm::eBoost:
869 return MakeSharedPtr<BoostRep_> ();
877Variant::JSON::Reader::Reader (
const ReaderOptions& options)
878 : inherited{mk_ (options)}
#define AssertNotReached()
constexpr const T & ValueOf(const optional< T > &t)
Same as *t, but Requires that 't' is engaged.
int64_t SignedSeekOffsetType
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 bool empty() const noexcept
nonvirtual String str() const
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Mapping_HashTable<KEY_TYPE, MAPPED_VALUE_TYPE, TRAITS> is a HashTable based concrete implementation o...
Sequence_stdvector<T> is an std::vector-based concrete implementation of the Sequence<T> container pa...
nonvirtual void insert(ArgByValueType< value_type > kvp)
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...
nonvirtual bool IsSeekable() const
Returns true iff this object was constructed with a seekable input stream rep.
void Throw(T &&e2Throw)
identical to builtin C++ 'throw' except that it does helpful, type dependent DbgTrace() messages firs...
Ptr New(const InputStream::Ptr< byte > &src, optional< AutomaticCodeCvtFlags > codeCvtFlags={}, optional< SeekableFlag > seekable={}, ReadAhead readAhead=eReadAheadAllowed)
Create an InputStream::Ptr<Character> from the arguments (usually binary source) - which can be used ...
StreamReader is an non-essential Stream utility, adding simplicity of use for a common use case,...
nonvirtual SeekOffsetType Seek(SeekOffsetType offset)
Logically the same as InputStream::Ptr<ELEMENT_TYPE>::Seek () - but without being 'synchronized' it m...
nonvirtual optional< ElementType > ReadBlocking()
ReadBlocking () reads either a single element, or fills in argument intoBuffer - but never blocks (no...