1 //
2 // json_util.cpp
3 // util
4 //
5 // Copyright © 2024 Apple Inc. All rights reserved.
6 //
7 // Please refer to the license found in the LICENSE file in the root directory of the source tree.
8
9 #include "json_util.hpp"
10
11 #include <string>
12 #include <vector>
13
14 namespace {
15
16 struct JSONParseState {
17 int n_open_braces = 0;
18 std::string json_object;
19 bool reading_string = false;
20 };
21
is_quote_escaped(const std::vector<char> & buffer,size_t offset)22 bool is_quote_escaped(const std::vector<char>& buffer, size_t offset) {
23 return offset >= 1 && buffer[offset - 1] == '\\';
24 }
25
process_quotes(const std::vector<char> & buffer,JSONParseState & state,size_t offset)26 void process_quotes(const std::vector<char>& buffer, JSONParseState& state, size_t offset) {
27 // If we are reading a string and the quote is escaped then we don't mark it as the end of string.
28 if (state.reading_string && is_quote_escaped(buffer, offset)) {
29 return;
30 }
31 state.reading_string = !state.reading_string;
32 }
33
34 // Foundation has no streaming parser, so we try to read the top json object.
35 // At this stage we don't care about the validity of json, the parser would
36 // anyways fail if the json object is not valid.
read_object_from_buffer(std::vector<char> & buffer,JSONParseState & state)37 void read_object_from_buffer(std::vector<char>& buffer, JSONParseState& state) {
38 size_t offset = 0;
39 for (; offset < buffer.size() && state.n_open_braces > 0; offset++) {
40 switch (buffer[offset]) {
41 case '}': {
42 // If the close brace is inside a string then then ignore it.
43 state.n_open_braces -= (state.reading_string ? 0 : 1);
44 break;
45 }
46 case '{': {
47 // If the open brace is inside a string then ignore it.
48 state.n_open_braces += (state.reading_string ? 0 : 1);
49 break;
50 }
51 case '"': {
52 process_quotes(buffer, state, offset);
53 break;
54 }
55 default: {
56 break;
57 }
58 }
59 }
60
61 if (offset > 0) {
62 state.json_object.append(buffer.begin(), buffer.begin() + offset);
63 }
64 }
65 }
66
67 namespace executorchcoreml {
68 namespace json {
69
read_object_from_stream(std::istream & stream,size_t max_bytes_to_read)70 std::optional<std::string> read_object_from_stream(std::istream& stream, size_t max_bytes_to_read) {
71 static constexpr size_t buffer_size = 512;
72 JSONParseState state;
73 char ch;
74 // Ignore 0, 0 is added for padding.
75 do {
76 stream >> ch;
77 } while (stream.good() && static_cast<uint8_t>(ch) == 0);
78 // The first character must be an opening brace.
79 if (ch != '{') {
80 return std::optional<std::string>();
81 }
82 state.json_object += ch;
83 state.n_open_braces = 1;
84
85 std::vector<char> buffer;
86 while (stream.good() && state.n_open_braces > 0 && state.json_object.size() < max_bytes_to_read) {
87 buffer.resize(buffer_size, '\0');
88 stream.read(buffer.data(), buffer_size);
89 read_object_from_buffer(buffer, state);
90 buffer.clear();
91 }
92
93 return state.n_open_braces == 0 ? state.json_object : std::optional<std::string>();
94 }
95
96 } // namespace json
97 } // namespace executorchcoreml
98