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_stdhashmap.h"
19#include "Stroika/Foundation/Containers/Support/ReserveTweaks.h"
20#include "Stroika/Foundation/DataExchange/BadFormatException.h"
31using namespace Stroika::Foundation::Streams;
32using namespace Stroika::Foundation::Traversal;
57 struct MyBufferedStreamReader_ :
StreamReader<Character> {
62 [[nodiscard]]
inline char32_t NextChar ()
64 Require (not IsAtEOF ());
67 inline void AdvanceOne ()
69 Require (not IsAtEOF ());
70 Seek (eFromCurrent, 1);
72 inline void BackupOne ()
74 Seek (eFromCurrent, -1);
78 VariantValue Reader_value_ (MyBufferedStreamReader_& in);
82 inline bool IsJSONSpace_ (
char32_t c)
105 inline bool IsJSONDigit_ (
char32_t c)
130 uint8_t HexChar2Num_ (
char c)
132 if (
'0' <= c and c <=
'9') [[likely]] {
133 return static_cast<uint8_t
> (c -
'0');
135 if (
'a' <= c and c <=
'f') [[likely]] {
136 return static_cast<uint8_t
> ((c -
'a') + 10);
138 if (
'A' <= c and c <=
'F') [[likely]] {
139 return static_cast<uint8_t
> ((c -
'A') + 10);
146 String Reader_String_ (MyBufferedStreamReader_& in)
148 Require (not in.IsAtEOF ());
149 char32_t c = in.NextChar ();
150 if (c !=
'\"') [[unlikely]] {
159 if (in.IsAtEOF ()) [[unlikely]] {
160 static const auto kException_{
BadFormatException{
"JSON: Unexpected EOF reading string (looking for close quote)"sv}};
164 if (c ==
'\"') [[unlikely]] {
165 return result.
str ();
167 else if (c ==
'\\') [[unlikely]] {
169 if (in.IsAtEOF ()) [[unlikely]] {
170 static const auto kException_{
BadFormatException{
"JSON: Unexpected EOF reading string (looking for close quote)"sv}};
192 char32_t newC =
'\0';
193 for (
int n = 0; n < 4; ++n) {
194 if (in.IsAtEOF ()) [[unlikely]] {
195 static const auto kException_{
BadFormatException{
"JSON: Unexpected EOF reading string (looking for close quote)"sv}};
198 newC += HexChar2Num_ (
static_cast<char> (in.NextChar ()));
216 VariantValue Reader_Number_ (
char32_t initialChar, MyBufferedStreamReader_& in)
218 Require (initialChar ==
'-' or IsJSONDigit_ (initialChar));
220 bool containsDot =
false;
224 for (
char32_t c = initialChar; c !=
'\0'; c = in.ReadBlocking ().value_or (
'\0').As<char32_t> ()) {
225 if (IsJSONDigit_ (c) or c ==
'.' or c ==
'e' or c ==
'E' or c ==
'+' or c ==
'-') [[likely]] {
227 if (c ==
'.') [[unlikely]] {
234 Assert (not tmp.
empty ());
239 Assert (not tmp.
empty ());
241 span<const char32_t> tmpData = tmp.
GetData (&ignoreBuf);
243 return VariantValue{FloatConversion::ToFloat<long double> (tmpData)};
247 return (initialChar == kDash_) ?
VariantValue{String2Int<long long int> (tmpData)}
248 :
VariantValue{String2Int<unsigned long long int> (tmpData)};
253 VariantValue Reader_Object_ (MyBufferedStreamReader_& in)
264 LookingFor lf = eName;
265 optional<String> curName;
267 optional<Character> oNextChar = in.Peek ();
268 if (not oNextChar.has_value ()) [[unlikely]] {
269 static const auto kException_{
BadFormatException{
"JSON: Unexpected EOF reading object (looking for '}')"sv}};
272 char32_t nextChar = oNextChar->As<
char32_t> ();
273 if (IsJSONSpace_ (nextChar)) [[likely]] {
279 Assert (curName == nullopt);
280 if (nextChar ==
'}') {
284 else if (nextChar ==
'\"') [[likely]] {
285 curName = Reader_String_ (in);
289 static const auto kException_{
290 BadFormatException{
"JSON: Reading object, looking for a name, didn't find a close brace or open quote"sv}};
295 Assert (curName == nullopt);
296 if (nextChar ==
'}') {
300 else if (nextChar ==
',') [[likely]] {
305 static const auto kException_{
BadFormatException{
"JSON: Reading object, looking for a comma, but found something else"sv}};
311 if (nextChar ==
':') [[likely]] {
316 static const auto kException_{
BadFormatException{
"JSON: Reading object, looking for a colon, but found something else"sv}};
323 result.
insert ({Memory::ValueOf (curName), Reader_value_ (in)});
332 VariantValue Reader_Array_ (MyBufferedStreamReader_& in)
334 vector<VariantValue> result;
337 bool lookingForElt =
true;
339 if (in.IsAtEOF ()) [[unlikely]] {
340 static const auto kException_{
BadFormatException{
"JSON: Unexpected EOF reading array (looking for ']')"sv}};
343 char32_t peekedChar = in.Peek ()->As<
char32_t> ();
344 if (peekedChar ==
']') {
351 else if (peekedChar ==
',') {
352 if (lookingForElt) [[unlikely]] {
353 static const auto kException_{
BadFormatException{
"JSON: Unexpected second ',' in reading array"sv}};
357 lookingForElt =
true;
361 else if (IsJSONSpace_ (peekedChar)) {
366 if (lookingForElt) [[likely]] {
367 Containers::Support::ReserveTweaks::Reserve4Add1 (result);
368 result.push_back (Reader_value_ (in));
369 lookingForElt =
false;
372 static const auto kException_{
BadFormatException{
"JSON: Unexpected character (missing ',' ?) in reading array"sv}};
379 VariantValue Reader_SpecialToken_ (
char32_t initialChar, MyBufferedStreamReader_& in)
381 switch (initialChar) {
384 if (in.ReadAll (begin (buf), end (buf)) == 4 and buf[0] ==
'a' and buf[1] ==
'l' and buf[2] ==
's' and buf[3] ==
'e') {
390 if (in.ReadAll (begin (buf), end (buf)) == 3 and buf[0] ==
'r' and buf[1] ==
'u' and buf[2] ==
'e') {
396 if (in.ReadAll (begin (buf), end (buf)) == 3 and buf[0] ==
'u' and buf[1] ==
'l' and buf[2] ==
'l') {
405 VariantValue Reader_value_ (MyBufferedStreamReader_& in)
415 for (optional<Character> oc = in.ReadBlocking (); oc; oc = in.ReadBlocking ()) {
416 switch (oc->As<
char32_t> ()) {
419 return Reader_String_ (in);
432 return Reader_Number_ (oc->As<
char32_t> (), in);
435 return Reader_Object_ (in);
437 return Reader_Array_ (in);
442 return Reader_SpecialToken_ (oc->As<
char32_t> (), in);
445 if (IsJSONSpace_ (oc->As<
char32_t> ())) [[likely]] {
449 static const auto kException_{
BadFormatException{
"JSON: Unexpected character looking for start of value"sv}};
456 static const auto kException_{
BadFormatException{
"JSON: Unexpected EOF looking for value"sv}};
466class Variant::JSON::Reader::NativeRep_ :
public Variant::Reader::_IRep {
468 virtual _SharedPtrIRep Clone ()
const override
470 return make_shared<NativeRep_> ();
472 virtual optional<filesystem::path> GetDefaultFileSuffix ()
const override
478 using namespace Streams;
483#if USE_NOISY_TRACE_IN_THIS_MODULE_
487 MyBufferedStreamReader_ reader{in};
488 return Reader_value_ (reader);
491#if __has_include("boost/json.hpp")
495 struct BoostSAXHandler_ {
497 static constexpr std::size_t max_array_size =
static_cast<size_t> (-1);
500 static constexpr std::size_t max_object_size =
static_cast<size_t> (-1);
503 static constexpr std::size_t max_string_size =
static_cast<size_t> (-1);
506 static constexpr std::size_t max_key_size =
static_cast<size_t> (-1);
508 BoostSAXHandler_ () =
default;
509 BoostSAXHandler_ (
const BoostSAXHandler_&) =
delete;
511 bool on_document_begin ([[maybe_unused]] error_code& ec)
513 Assert (fStack_.empty ());
514 fStack_.emplace (Context_::eSimple);
517 bool on_document_end ([[maybe_unused]] error_code& ec)
519 Assert (fStack_.size () == 1);
520 this->PopContext_ ();
521 Assert (fStack_.empty ());
525 bool on_array_begin ([[maybe_unused]] error_code& ec)
527 fStack_.emplace (Context_::eArray);
530 bool on_array_end ([[maybe_unused]] std::size_t n, [[maybe_unused]] error_code& ec)
532 Assert (fStack_.top ().GetContextType () == Context_::eArray);
533 Assert (fStack_.top ().PeekAccumVector_ ().size () == n);
538 bool on_object_begin ([[maybe_unused]] error_code& ec)
540 fStack_.emplace (Context_::eMap);
543 bool on_object_end ([[maybe_unused]] std::size_t n, [[maybe_unused]] error_code& ec)
545 Assert (fStack_.top ().GetContextType () == Context_::eMap);
546 Assert (fStack_.top ().PeekAccumObj_ ().size () == n);
551 bool on_string_part (string_view s, [[maybe_unused]] std::size_t n, [[maybe_unused]] error_code& ec)
553 fPartSaver_.push_back (span<const char>{s});
556 bool on_string (string_view s, [[maybe_unused]] std::size_t n, [[maybe_unused]] error_code& ec)
559 if (s.size () == n) {
563 fPartSaver_.push_back (span<const char>{s});
564 String res = toStroikaString_ (fPartSaver_);
565 fPartSaver_.clear ();
571 bool on_key_part (string_view s, [[maybe_unused]] std::size_t n, [[maybe_unused]] error_code& ec)
574 Assert (fStack_.top ().GetContextType () == Context_::eMap);
575 fPartSaver_.push_back (span<const char>{s});
578 bool on_key (string_view s, [[maybe_unused]] std::size_t n, [[maybe_unused]] error_code& ec)
580 Assert (fStack_.top ().GetContextType () == Context_::eMap);
581 Assert (s.size () <= n);
583 if (s.size () == n) {
584 fStack_.top ().fKey = toStroikaString_ (s);
587 fPartSaver_.push_back (span<const char>{s});
588 fStack_.top ().fKey = toStroikaString_ (fPartSaver_);
589 fPartSaver_.clear ();
594 bool on_number_part ([[maybe_unused]] string_view s, [[maybe_unused]] error_code& ec)
599 bool on_int64 (int64_t i, [[maybe_unused]] string_view s, [[maybe_unused]] error_code& ec)
604 bool on_uint64 (uint64_t u, [[maybe_unused]] string_view s, [[maybe_unused]] error_code& ec)
609 bool on_double (
double d, [[maybe_unused]] string_view s, [[maybe_unused]] error_code& ec)
615 bool on_bool (
bool b, [[maybe_unused]] error_code& ec)
621 bool on_null ([[maybe_unused]] error_code& ec)
627 bool on_comment_part ([[maybe_unused]] string_view s, [[maybe_unused]] error_code& ec)
632 bool on_comment ([[maybe_unused]] string_view s, [[maybe_unused]] error_code& ec)
640 template <
typename CONTAINER_OF_CHAR_BUT_REALLY_UTF8>
641 static String toStroikaString_ (CONTAINER_OF_CHAR_BUT_REALLY_UTF8 sv)
642 requires requires (CONTAINER_OF_CHAR_BUT_REALLY_UTF8 t) {
643 { span<const char>{t} };
646 return String{Memory::SpanBytesCast<span<const char8_t>> (span<const char>{sv})};
651 return fCompletedFinalValue_;
655 Assert (not fStack_.empty ());
656 Context_& t = fStack_.top ();
657 switch (t.GetContextType ()) {
658 case Context_::eArray:
659 t.PeekAccumVector_ ().push_back (v);
662 t.PeekAccumObj_ ().insert ({t.fKey, v});
664 case Context_::eSimple:
665 t.PeekSimpleValue_ () = v;
673 Assert (not fStack_.empty ());
675 Context_& t = fStack_.top ();
677 switch (t.GetContextType ()) {
678 case Context_::eArray:
682 case Context_::eSimple:
683 return t.PeekSimpleValue_ ();
690 if (fStack_.empty ()) {
691 fCompletedFinalValue_ = vv;
694 AddCompleteValue_ (vv);
710 Context_ () =
delete;
711 Context_ (ContextType_ ct)
718 fVV_ = vector<VariantValue>{};
724 Ensure (ct == GetContextType ());
726 ~Context_ () =
default;
727 Context_ (
const Context_&) =
delete;
728 Context_ (Context_&&) =
default;
730 ContextType_ GetContextType ()
const
732 return static_cast<ContextType_
> (fVV_.index ());
735 vector<VariantValue>& PeekAccumVector_ ()
737 Require (GetContextType () == eArray);
738 return get<vector<VariantValue>> (fVV_);
742 Require (GetContextType () == eMap);
743 return get<Mapping_stdhashmap<String, VariantValue>::STDHASHMAP<>> (fVV_);
747 Require (GetContextType () == eSimple);
748 return get<VariantValue> (fVV_);
757 std::stack<Context_> fStack_;
765class Variant::JSON::Reader::BoostRep_ :
public Variant::Reader::_IRep {
767 virtual _SharedPtrIRep Clone ()
const override
769 return make_shared<BoostRep_> ();
771 virtual optional<filesystem::path> GetDefaultFileSuffix ()
const override
777#if USE_NOISY_TRACE_IN_THIS_MODULE_
780 using namespace Streams;
781 using namespace boost;
783 constexpr bool kUseSAX_ =
true;
785 if constexpr (kUseSAX_) {
786 json::basic_parser<BoostSAXHandler_> p{json::parse_options{}};
788 const size_t targetChunkSize = inSeekable ? Memory::NEltsOf (buf) : 1;
789 size_t actualChunkSize;
790 boost::system::error_code ec;
791 while ((actualChunkSize = in.
ReadBlocking (span{buf, targetChunkSize}).size ()) != 0) {
793 size_t nParsed = p.write_some (
true,
reinterpret_cast<const char*
> (begin (buf)), actualChunkSize, ec);
794 Assert (nParsed <= actualChunkSize);
795 if (nParsed < actualChunkSize) {
807 p.write_some (
false,
nullptr, 0, ec);
808 if (ec and ec != boost::json::error::extra_data ) {
812 return p.handler ().GetConstructedValue ();
815 json::stream_parser p;
817 const size_t targetChunkSize = inSeekable ? Memory::NEltsOf (buf) : 1;
818 size_t actualChunkSize;
819 while ((actualChunkSize = in.
ReadBlocking (span{buf, targetChunkSize}).size ()) != 0) {
820 boost::system::error_code ec;
821 size_t nParsed = p.write_some (
reinterpret_cast<const char*
> (begin (buf)), actualChunkSize, ec);
822 Assert (nParsed <= actualChunkSize);
823 if (nParsed < actualChunkSize) {
847#if USE_NOISY_TRACE_IN_THIS_MODULE_
851 using namespace Streams;
852 return Read (_ToByteReader (in));
857inline auto Variant::JSON::Reader::mk_ (
const ReaderOptions& options) -> shared_ptr<_IRep>
859 switch (options.fPreferredAlgorithm.value_or (ReaderOptions::Algorithm::eDEFAULT)) {
860 case ReaderOptions::Algorithm::eStroikaNative:
861 return make_shared<NativeRep_> ();
862#if __has_include("boost/json.hpp")
863 case ReaderOptions::Algorithm::eBoost:
864 return make_shared<BoostRep_> ();
872Variant::JSON::Reader::Reader (
const ReaderOptions& options)
873 : inherited{mk_ (options)}
#define AssertNotReached()
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_stdhashmap<KEY_TYPE, MAPPED_VALUE_TYPE, TRAITS> is an std::map-based concrete implementation ...
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...