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"
33using namespace Stroika::Foundation::Memory;
34using namespace Stroika::Foundation::Streams;
35using namespace Stroika::Foundation::Traversal;
60 struct MyBufferedStreamReader_ :
StreamReader<Character> {
65 [[nodiscard]]
inline char32_t NextChar ()
67 Require (not IsAtEOF ());
70 inline void AdvanceOne ()
72 Require (not IsAtEOF ());
73 Seek (eFromCurrent, 1);
75 inline void BackupOne ()
77 Seek (eFromCurrent, -1);
81 VariantValue Reader_value_ (MyBufferedStreamReader_& in);
85 inline bool IsJSONSpace_ (
char32_t c)
108 inline bool IsJSONDigit_ (
char32_t c)
133 uint8_t HexChar2Num_ (
char c)
135 if (
'0' <= c and c <=
'9') [[likely]] {
136 return static_cast<uint8_t
> (c -
'0');
138 if (
'a' <= c and c <=
'f') [[likely]] {
139 return static_cast<uint8_t
> ((c -
'a') + 10);
141 if (
'A' <= c and c <=
'F') [[likely]] {
142 return static_cast<uint8_t
> ((c -
'A') + 10);
149 String Reader_String_ (MyBufferedStreamReader_& in)
151 Require (not in.IsAtEOF ());
152 char32_t c = in.NextChar ();
153 if (c !=
'\"') [[unlikely]] {
162 if (in.IsAtEOF ()) [[unlikely]] {
163 static const auto kException_{
BadFormatException{
"JSON: Unexpected EOF reading string (looking for close quote)"sv}};
167 if (c ==
'\"') [[unlikely]] {
168 return result.
str ();
170 else if (c ==
'\\') [[unlikely]] {
172 if (in.IsAtEOF ()) [[unlikely]] {
173 static const auto kException_{
BadFormatException{
"JSON: Unexpected EOF reading string (looking for close quote)"sv}};
195 char32_t newC =
'\0';
196 for (
int n = 0; n < 4; ++n) {
197 if (in.IsAtEOF ()) [[unlikely]] {
198 static const auto kException_{
BadFormatException{
"JSON: Unexpected EOF reading string (looking for close quote)"sv}};
201 newC += HexChar2Num_ (
static_cast<char> (in.NextChar ()));
219 VariantValue Reader_Number_ (
char32_t initialChar, MyBufferedStreamReader_& in)
221 Require (initialChar ==
'-' or IsJSONDigit_ (initialChar));
223 bool containsDot =
false;
227 for (
char32_t c = initialChar; c !=
'\0'; c = in.ReadBlocking ().value_or (
'\0').As<char32_t> ()) {
228 if (IsJSONDigit_ (c) or c ==
'.' or c ==
'e' or c ==
'E' or c ==
'+' or c ==
'-') [[likely]] {
230 if (c ==
'.') [[unlikely]] {
237 Assert (not tmp.
empty ());
242 Assert (not tmp.
empty ());
244 span<const char32_t> tmpData = tmp.
GetData (&ignoreBuf);
246 return VariantValue{FloatConversion::ToFloat<long double> (tmpData)};
250 return (initialChar == kDash_) ?
VariantValue{String2Int<long long int> (tmpData)}
251 :
VariantValue{String2Int<unsigned long long int> (tmpData)};
256 VariantValue Reader_Object_ (MyBufferedStreamReader_& in)
267 LookingFor lf = eName;
268 optional<String> curName;
270 optional<Character> oNextChar = in.Peek ();
271 if (not oNextChar.has_value ()) [[unlikely]] {
272 static const auto kException_{
BadFormatException{
"JSON: Unexpected EOF reading object (looking for '}')"sv}};
275 char32_t nextChar = oNextChar->As<
char32_t> ();
276 if (IsJSONSpace_ (nextChar)) [[likely]] {
282 Assert (curName == nullopt);
283 if (nextChar ==
'}') {
287 else if (nextChar ==
'\"') [[likely]] {
288 curName = Reader_String_ (in);
292 static const auto kException_{
293 BadFormatException{
"JSON: Reading object, looking for a name, didn't find a close brace or open quote"sv}};
298 Assert (curName == nullopt);
299 if (nextChar ==
'}') {
303 else if (nextChar ==
',') [[likely]] {
308 static const auto kException_{
BadFormatException{
"JSON: Reading object, looking for a comma, but found something else"sv}};
314 if (nextChar ==
':') [[likely]] {
319 static const auto kException_{
BadFormatException{
"JSON: Reading object, looking for a colon, but found something else"sv}};
335 VariantValue Reader_Array_ (MyBufferedStreamReader_& in)
337 vector<VariantValue> result;
340 bool lookingForElt =
true;
342 if (in.IsAtEOF ()) [[unlikely]] {
343 static const auto kException_{
BadFormatException{
"JSON: Unexpected EOF reading array (looking for ']')"sv}};
346 char32_t peekedChar = in.Peek ()->As<
char32_t> ();
347 if (peekedChar ==
']') {
354 else if (peekedChar ==
',') {
355 if (lookingForElt) [[unlikely]] {
356 static const auto kException_{
BadFormatException{
"JSON: Unexpected second ',' in reading array"sv}};
360 lookingForElt =
true;
364 else if (IsJSONSpace_ (peekedChar)) {
369 if (lookingForElt) [[likely]] {
370 Containers::Support::ReserveTweaks::Reserve4Add1 (result);
371 result.push_back (Reader_value_ (in));
372 lookingForElt =
false;
375 static const auto kException_{
BadFormatException{
"JSON: Unexpected character (missing ',' ?) in reading array"sv}};
382 VariantValue Reader_SpecialToken_ (
char32_t initialChar, MyBufferedStreamReader_& in)
384 switch (initialChar) {
387 if (in.ReadAll (begin (buf), end (buf)) == 4 and buf[0] ==
'a' and buf[1] ==
'l' and buf[2] ==
's' and buf[3] ==
'e') {
393 if (in.ReadAll (begin (buf), end (buf)) == 3 and buf[0] ==
'r' and buf[1] ==
'u' and buf[2] ==
'e') {
399 if (in.ReadAll (begin (buf), end (buf)) == 3 and buf[0] ==
'u' and buf[1] ==
'l' and buf[2] ==
'l') {
408 VariantValue Reader_value_ (MyBufferedStreamReader_& in)
418 for (optional<Character> oc = in.ReadBlocking (); oc; oc = in.ReadBlocking ()) {
419 switch (oc->As<
char32_t> ()) {
422 return Reader_String_ (in);
435 return Reader_Number_ (oc->As<
char32_t> (), in);
438 return Reader_Object_ (in);
440 return Reader_Array_ (in);
445 return Reader_SpecialToken_ (oc->As<
char32_t> (), in);
448 if (IsJSONSpace_ (oc->As<
char32_t> ())) [[likely]] {
452 static const auto kException_{
BadFormatException{
"JSON: Unexpected character looking for start of value"sv}};
459 static const auto kException_{
BadFormatException{
"JSON: Unexpected EOF looking for value"sv}};
469class Variant::JSON::Reader::NativeRep_ :
public Variant::Reader::_IRep {
471 NativeRep_ () =
default;
472 NativeRep_ (
const NativeRep_&) =
default;
473 virtual _SharedPtrIRep Clone ()
const override
475 return make_shared<NativeRep_> (*
this);
477 virtual optional<filesystem::path> GetDefaultFileSuffix ()
const override
487#if USE_NOISY_TRACE_IN_THIS_MODULE_
490 MyBufferedStreamReader_ reader{in.
IsSeekable () ? in : BufferedInputStream::New (in, SeekableFlag::eSeekable)};
491 return Reader_value_ (reader);
494#if __has_include("boost/json.hpp")
498 struct BoostSAXHandler_ {
500 static constexpr std::size_t max_array_size =
static_cast<size_t> (-1);
503 static constexpr std::size_t max_object_size =
static_cast<size_t> (-1);
506 static constexpr std::size_t max_string_size =
static_cast<size_t> (-1);
509 static constexpr std::size_t max_key_size =
static_cast<size_t> (-1);
511 BoostSAXHandler_ () =
default;
512 BoostSAXHandler_ (
const BoostSAXHandler_&) =
delete;
514 bool on_document_begin ([[maybe_unused]] error_code& ec)
516 Assert (fStack_.empty ());
517 fStack_.emplace (Context_::eSimple);
520 bool on_document_end ([[maybe_unused]] error_code& ec)
522 Assert (fStack_.size () == 1);
523 this->PopContext_ ();
524 Assert (fStack_.empty ());
528 bool on_array_begin ([[maybe_unused]] error_code& ec)
530 fStack_.emplace (Context_::eArray);
533 bool on_array_end ([[maybe_unused]] std::size_t n, [[maybe_unused]] error_code& ec)
535 Assert (fStack_.top ().GetContextType () == Context_::eArray);
536 Assert (fStack_.top ().PeekAccumVector_ ().size () == n);
541 bool on_object_begin ([[maybe_unused]] error_code& ec)
543 fStack_.emplace (Context_::eMap);
546 bool on_object_end ([[maybe_unused]] std::size_t n, [[maybe_unused]] error_code& ec)
548 Assert (fStack_.top ().GetContextType () == Context_::eMap);
549 Assert (fStack_.top ().PeekAccumObj_ ().size () == n);
554 bool on_string_part (string_view s, [[maybe_unused]] std::size_t n, [[maybe_unused]] error_code& ec)
556 fPartSaver_.push_back (span<const char>{s});
559 bool on_string (string_view s, [[maybe_unused]] std::size_t n, [[maybe_unused]] error_code& ec)
562 if (s.size () == n) {
566 fPartSaver_.push_back (span<const char>{s});
567 String res = toStroikaString_ (fPartSaver_);
568 fPartSaver_.clear ();
574 bool on_key_part (string_view s, [[maybe_unused]] std::size_t n, [[maybe_unused]] error_code& ec)
577 Assert (fStack_.top ().GetContextType () == Context_::eMap);
578 fPartSaver_.push_back (span<const char>{s});
581 bool on_key (string_view s, [[maybe_unused]] std::size_t n, [[maybe_unused]] error_code& ec)
583 Assert (fStack_.top ().GetContextType () == Context_::eMap);
584 Assert (s.size () <= n);
586 if (s.size () == n) {
587 fStack_.top ().fKey = toStroikaString_ (s);
590 fPartSaver_.push_back (span<const char>{s});
591 fStack_.top ().fKey = toStroikaString_ (fPartSaver_);
592 fPartSaver_.clear ();
597 bool on_number_part ([[maybe_unused]] string_view s, [[maybe_unused]] error_code& ec)
602 bool on_int64 (int64_t i, [[maybe_unused]] string_view s, [[maybe_unused]] error_code& ec)
607 bool on_uint64 (uint64_t u, [[maybe_unused]] string_view s, [[maybe_unused]] error_code& ec)
612 bool on_double (
double d, [[maybe_unused]] string_view s, [[maybe_unused]] error_code& ec)
618 bool on_bool (
bool b, [[maybe_unused]] error_code& ec)
624 bool on_null ([[maybe_unused]] error_code& ec)
630 bool on_comment_part ([[maybe_unused]] string_view s, [[maybe_unused]] error_code& ec)
635 bool on_comment ([[maybe_unused]] string_view s, [[maybe_unused]] error_code& ec)
643 template <
typename CONTAINER_OF_CHAR_BUT_REALLY_UTF8>
644 static String toStroikaString_ (CONTAINER_OF_CHAR_BUT_REALLY_UTF8 sv)
645 requires requires (CONTAINER_OF_CHAR_BUT_REALLY_UTF8 t) {
646 { span<const char>{t} };
649 return String{SpanBytesCast<span<const char8_t>> (span<const char>{sv})};
654 return fCompletedFinalValue_;
658 Assert (not fStack_.empty ());
659 Context_& t = fStack_.top ();
660 switch (t.GetContextType ()) {
661 case Context_::eArray:
662 t.PeekAccumVector_ ().push_back (v);
665 t.PeekAccumObj_ ().insert ({t.fKey, v});
667 case Context_::eSimple:
668 t.PeekSimpleValue_ () = v;
676 Assert (not fStack_.empty ());
678 Context_& t = fStack_.top ();
680 switch (t.GetContextType ()) {
681 case Context_::eArray:
685 case Context_::eSimple:
686 return t.PeekSimpleValue_ ();
693 if (fStack_.empty ()) {
694 fCompletedFinalValue_ = vv;
697 AddCompleteValue_ (vv);
713 Context_ () =
delete;
714 Context_ (ContextType_ ct)
721 fVV_ = vector<VariantValue>{};
727 Ensure (ct == GetContextType ());
729 ~Context_ () =
default;
730 Context_ (
const Context_&) =
delete;
731 Context_ (Context_&&) =
default;
733 ContextType_ GetContextType ()
const
735 return static_cast<ContextType_
> (fVV_.index ());
738 vector<VariantValue>& PeekAccumVector_ ()
740 Require (GetContextType () == eArray);
741 return get<vector<VariantValue>> (fVV_);
745 Require (GetContextType () == eMap);
746 return get<Mapping_HashTable<String, VariantValue>::DEFAULT_HASHTABLE<>> (fVV_);
750 Require (GetContextType () == eSimple);
751 return get<VariantValue> (fVV_);
760 std::stack<Context_> fStack_;
768class Variant::JSON::Reader::BoostRep_ :
public Variant::Reader::_IRep {
770 BoostRep_ () =
default;
771 BoostRep_ (
const BoostRep_&) =
default;
772 virtual _SharedPtrIRep Clone ()
const override
774 return make_shared<BoostRep_> (*
this);
776 virtual optional<filesystem::path> GetDefaultFileSuffix ()
const override
782#if USE_NOISY_TRACE_IN_THIS_MODULE_
785 using namespace boost;
789 constexpr bool kUseSAX_ =
true;
791 if constexpr (kUseSAX_) {
792 json::basic_parser<BoostSAXHandler_> p{json::parse_options{}};
793 const size_t targetChunkSize = std::size (buf);
794 size_t actualChunkSize;
795 boost::system::error_code ec;
796 while ((actualChunkSize = useInStream.
ReadBlocking (span{buf, targetChunkSize}).size ()) != 0) {
798 size_t nParsed = p.write_some (
true,
reinterpret_cast<const char*
> (begin (buf)), actualChunkSize, ec);
799 Assert (nParsed <= actualChunkSize);
800 if (nParsed < actualChunkSize) {
812 p.write_some (
false,
nullptr, 0, ec);
813 if (ec and ec != boost::json::error::extra_data ) {
817 return p.handler ().GetConstructedValue ();
820 json::stream_parser p;
821 const size_t targetChunkSize = std::size (buf);
822 size_t actualChunkSize;
823 while ((actualChunkSize = useInStream.
ReadBlocking (span{buf, targetChunkSize}).size ()) != 0) {
824 boost::system::error_code ec;
825 size_t nParsed = p.write_some (
reinterpret_cast<const char*
> (begin (buf)), actualChunkSize, ec);
826 Assert (nParsed <= actualChunkSize);
827 if (nParsed < actualChunkSize) {
851#if USE_NOISY_TRACE_IN_THIS_MODULE_
855 return Read (_ToByteReader (in));
860inline auto Variant::JSON::Reader::mk_ (
const ReaderOptions& options) -> shared_ptr<_IRep>
862 switch (options.fPreferredAlgorithm.value_or (ReaderOptions::Algorithm::eDEFAULT)) {
863 case ReaderOptions::Algorithm::eStroikaNative:
864 return make_shared<NativeRep_> ();
865#if __has_include("boost/json.hpp")
866 case ReaderOptions::Algorithm::eBoost:
867 return make_shared<BoostRep_> ();
875Variant::JSON::Reader::Reader (
const ReaderOptions& options)
876 : 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...