simdjson 5.0.2
Ridiculously Fast JSON
Loading...
Searching...
No Matches
parser-inl.h
1#ifndef SIMDJSON_PARSER_INL_H
2#define SIMDJSON_PARSER_INL_H
3
4#include "simdjson/dom/base.h"
5#include "simdjson/dom/document_stream.h"
6#include "simdjson/implementation.h"
7#include "simdjson/internal/dom_parser_implementation.h"
8
9#include "simdjson/error-inl.h"
10#include "simdjson/padded_string-inl.h"
11#include "simdjson/dom/document_stream-inl.h"
12#include "simdjson/dom/element-inl.h"
13
14#include <climits>
15#include <cstring> /* memcmp */
16
17namespace simdjson {
18namespace dom {
19
20//
21// parser inline implementation
22//
23simdjson_inline parser::parser(size_t max_capacity) noexcept
24 : _max_capacity{max_capacity},
25 loaded_bytes(nullptr) {
26}
27simdjson_inline parser::parser(parser &&other) noexcept = default;
28simdjson_inline parser &parser::operator=(parser &&other) noexcept = default;
29
30inline bool parser::is_valid() const noexcept { return valid; }
31inline int parser::get_error_code() const noexcept { return error; }
32inline std::string parser::get_error_message() const noexcept { return error_message(error); }
33
34inline bool parser::dump_raw_tape(std::ostream &os) const noexcept {
35 return valid ? doc.dump_raw_tape(os) : false;
36}
37
38inline simdjson_result<size_t> parser::read_file(std::string_view path) noexcept {
39 const std::string path_copy(path);
40 // Open the file
41 SIMDJSON_PUSH_DISABLE_WARNINGS
42 SIMDJSON_DISABLE_DEPRECATED_WARNING // Disable CRT_SECURE warning on MSVC: manually verified this is safe
43 std::FILE *fp = std::fopen(path_copy.c_str(), "rb");
44 SIMDJSON_POP_DISABLE_WARNINGS
45
46 if (fp == nullptr) {
47 return IO_ERROR;
48 }
49
50 // Get the file size
51 int ret;
52#if SIMDJSON_VISUAL_STUDIO && !SIMDJSON_IS_32BITS
53 ret = _fseeki64(fp, 0, SEEK_END);
54#else
55 ret = std::fseek(fp, 0, SEEK_END);
56#endif // _WIN64
57 if(ret < 0) {
58 std::fclose(fp);
59 return IO_ERROR;
60 }
61#if SIMDJSON_VISUAL_STUDIO && !SIMDJSON_IS_32BITS
62 __int64 len = _ftelli64(fp);
63 if(len == -1L) {
64 std::fclose(fp);
65 return IO_ERROR;
66 }
67#else
68 long len = std::ftell(fp);
69 if((len < 0) || (len == LONG_MAX)) {
70 std::fclose(fp);
71 return IO_ERROR;
72 }
73#endif
74
75 // Make sure we have enough capacity to load the file
76 if (_loaded_bytes_capacity < size_t(len)) {
77 loaded_bytes.reset( internal::allocate_padded_buffer(len) );
78 if (!loaded_bytes) {
79 std::fclose(fp);
80 return MEMALLOC;
81 }
82 _loaded_bytes_capacity = len;
83 }
84
85 // Read the string
86 std::rewind(fp);
87 size_t bytes_read = std::fread(loaded_bytes.get(), 1, len, fp);
88 if (std::fclose(fp) != 0 || bytes_read != size_t(len)) {
89 return IO_ERROR;
90 }
91
92 return bytes_read;
93}
94
95inline simdjson_result<element> parser::load(std::string_view path) & noexcept {
96 return load_into_document(doc, path);
97}
98
99inline simdjson_result<element> parser::load_into_document(document& provided_doc, std::string_view path) & noexcept {
100 size_t len;
101 auto _error = read_file(path).get(len);
102 if (_error) { return _error; }
103 return parse_into_document(provided_doc, loaded_bytes.get(), len, false);
104}
105
106inline simdjson_result<document_stream> parser::load_many(std::string_view path, size_t batch_size) noexcept {
107 size_t len;
108 auto _error = read_file(path).get(len);
109 if (_error) { return _error; }
110 if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; }
111 return document_stream(*this, reinterpret_cast<const uint8_t*>(loaded_bytes.get()), len, batch_size);
112}
113
114inline simdjson_result<element> parser::parse_into_document(document& provided_doc, const uint8_t *buf, size_t len, bool realloc_if_needed) & noexcept {
115 // Important: we need to ensure that document has enough capacity.
116 // Important: It is possible that provided_doc is actually the internal 'doc' within the parser!!!
117 error_code _error = ensure_capacity(provided_doc, len);
118 if (_error) { return _error; }
119 if (realloc_if_needed) {
120 // Make sure we have enough capacity to copy len bytes
121 if (!loaded_bytes || _loaded_bytes_capacity < len) {
122 loaded_bytes.reset( internal::allocate_padded_buffer(len) );
123 if (!loaded_bytes) {
124 return MEMALLOC;
125 }
126 _loaded_bytes_capacity = len;
127 }
128 std::memcpy(static_cast<void *>(loaded_bytes.get()), buf, len);
129 buf = reinterpret_cast<const uint8_t*>(loaded_bytes.get());
130 }
131
132 if((len >= 3) && (std::memcmp(buf, "\xEF\xBB\xBF", 3) == 0)) {
133 buf += 3;
134 len -= 3;
135 }
136 implementation->_number_as_string = _number_as_string;
137 _error = implementation->parse(buf, len, provided_doc);
138
139 if (_error) { return _error; }
140
141 return provided_doc.root();
142}
143
144inline simdjson_result<element> parser::parse_into_document_unpadded(document& provided_doc, const uint8_t *buf, size_t len) & noexcept {
145 // Like parse_into_document with realloc_if_needed=false (no copy, parse in
146 // place), but we tell the implementation the buffer is not padded so stage 2
147 // avoids reading past buf+len: string unescaping is bounded, near-the-end
148 // numbers are parsed from a padded copy, and atoms use length-aware
149 // validators (see tape_builder). Stage 1 is already safe for unpadded input.
150 error_code _error = ensure_capacity(provided_doc, len);
151 if (_error) { return _error; }
152
153 if((len >= 3) && (std::memcmp(buf, "\xEF\xBB\xBF", 3) == 0)) {
154 buf += 3;
155 len -= 3;
156 }
157 implementation->_number_as_string = _number_as_string;
158 implementation->_unpadded = true;
159 _error = implementation->parse(buf, len, provided_doc);
160 implementation->_unpadded = false; // restore so later padded parses use the fast path
161
162 if (_error) { return _error; }
163
164 return provided_doc.root();
165}
166
167simdjson_inline simdjson_result<element> parser::parse_into_document(document& provided_doc, const char *buf, size_t len, bool realloc_if_needed) & noexcept {
168 return parse_into_document(provided_doc, reinterpret_cast<const uint8_t *>(buf), len, realloc_if_needed);
169}
170simdjson_inline simdjson_result<element> parser::parse_into_document(document& provided_doc, const std::string &s) & noexcept {
171 return parse_into_document(provided_doc, s.data(), s.length(), s.capacity() - s.length() < SIMDJSON_PADDING);
172}
173simdjson_inline simdjson_result<element> parser::parse_into_document(document& provided_doc, const padded_string &s) & noexcept {
174 return parse_into_document(provided_doc, s.data(), s.length(), false);
175}
176
177
178inline simdjson_result<element> parser::parse(const uint8_t *buf, size_t len, bool realloc_if_needed) & noexcept {
179 return parse_into_document(doc, buf, len, realloc_if_needed);
180}
181
182simdjson_inline simdjson_result<element> parser::parse(const char *buf, size_t len, bool realloc_if_needed) & noexcept {
183 return parse(reinterpret_cast<const uint8_t *>(buf), len, realloc_if_needed);
184}
185simdjson_inline simdjson_result<element> parser::parse(const std::string &s) & noexcept {
186 return parse(s.data(), s.length(), s.capacity() - s.length() < SIMDJSON_PADDING);
187}
188simdjson_inline simdjson_result<element> parser::parse(const padded_string &s) & noexcept {
189 return parse(s.data(), s.length(), false);
190}
191simdjson_inline simdjson_result<element> parser::parse(const padded_string_view &v) & noexcept {
192 return parse(v.data(), v.length(), false);
193}
194
195inline simdjson_result<element> parser::parse_unpadded(const uint8_t *buf, size_t len) & noexcept {
196 return parse_into_document_unpadded(doc, buf, len);
197}
198simdjson_inline simdjson_result<element> parser::parse_unpadded(const char *buf, size_t len) & noexcept {
199 return parse_unpadded(reinterpret_cast<const uint8_t *>(buf), len);
200}
201simdjson_inline simdjson_result<element> parser::parse_unpadded(std::string_view s) & noexcept {
202 return parse_unpadded(reinterpret_cast<const uint8_t *>(s.data()), s.size());
203}
204
205inline simdjson_result<document_stream> parser::parse_many(const uint8_t *buf, size_t len, size_t batch_size) noexcept {
206 return parse_many(buf, len, batch_size, stream_format::whitespace_delimited);
207}
208inline simdjson_result<document_stream> parser::parse_many(const char *buf, size_t len, size_t batch_size) noexcept {
209 return parse_many(reinterpret_cast<const uint8_t *>(buf), len, batch_size);
210}
211inline simdjson_result<document_stream> parser::parse_many(const std::string &s, size_t batch_size) noexcept {
212 return parse_many(s.data(), s.length(), batch_size);
213}
214inline simdjson_result<document_stream> parser::parse_many(const padded_string &s, size_t batch_size) noexcept {
215 return parse_many(s.data(), s.length(), batch_size);
216}
217inline simdjson_result<document_stream> parser::parse_many(const padded_string_view &v, size_t batch_size) noexcept {
218 return parse_many(v.data(), v.length(), batch_size);
219}
220
221inline simdjson_result<document_stream> parser::parse_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) noexcept {
222 if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; }
223 if((len >= 3) && (std::memcmp(buf, "\xEF\xBB\xBF", 3) == 0)) {
224 buf += 3;
225 len -= 3;
226 }
228 // Strip leading JSON whitespace.
229 while (len > 0 && (buf[0] == ' ' || buf[0] == '\t' || buf[0] == '\n' || buf[0] == '\r')) {
230 buf++; len--;
231 }
232 // Expect the opening '['.
233 if (len == 0 || buf[0] != '[') { return TAPE_ERROR; }
234 buf++; len--;
235 // Strip trailing JSON whitespace.
236 while (len > 0 && (buf[len-1] == ' ' || buf[len-1] == '\t' || buf[len-1] == '\n' || buf[len-1] == '\r')) {
237 len--;
238 }
239 // Expect the closing ']'.
240 if (len == 0 || buf[len-1] != ']') { return TAPE_ERROR; }
241 len--;
242 // Fall through to comma_delimited over the array contents.
244 }
245 return document_stream(*this, buf, len, batch_size, format);
246}
247inline simdjson_result<document_stream> parser::parse_many(const char *buf, size_t len, size_t batch_size, stream_format format) noexcept {
248 return parse_many(reinterpret_cast<const uint8_t *>(buf), len, batch_size, format);
249}
250inline simdjson_result<document_stream> parser::parse_many(const std::string &s, size_t batch_size, stream_format format) noexcept {
251 return parse_many(s.data(), s.length(), batch_size, format);
252}
253inline simdjson_result<document_stream> parser::parse_many(const padded_string &s, size_t batch_size, stream_format format) noexcept {
254 return parse_many(s.data(), s.length(), batch_size, format);
255}
256inline simdjson_result<document_stream> parser::parse_many(const padded_string_view &v, size_t batch_size, stream_format format) noexcept {
257 return parse_many(v.data(), v.length(), batch_size, format);
258}
259
260simdjson_inline size_t parser::capacity() const noexcept {
261 return implementation ? implementation->capacity() : 0;
262}
263simdjson_inline size_t parser::max_capacity() const noexcept {
264 return _max_capacity;
265}
266simdjson_pure simdjson_inline size_t parser::max_depth() const noexcept {
267 return implementation ? implementation->max_depth() : DEFAULT_MAX_DEPTH;
268}
269
270simdjson_warn_unused
271inline error_code parser::allocate(size_t capacity, size_t max_depth) noexcept {
272 //
273 // Reallocate implementation if needed
274 //
275 error_code err;
276 if (implementation) {
277 err = implementation->allocate(capacity, max_depth);
278 } else {
279 err = simdjson::get_active_implementation()->create_dom_parser_implementation(capacity, max_depth, implementation);
280 }
281 if (err) { return err; }
282 return SUCCESS;
283}
284
285#ifndef SIMDJSON_DISABLE_DEPRECATED_API
286simdjson_warn_unused
287inline bool parser::allocate_capacity(size_t capacity, size_t max_depth) noexcept {
288 return !allocate(capacity, max_depth);
289}
290#endif // SIMDJSON_DISABLE_DEPRECATED_API
291
292inline error_code parser::ensure_capacity(size_t desired_capacity) noexcept {
293 return ensure_capacity(doc, desired_capacity);
294}
295
296
297inline error_code parser::ensure_capacity(document& target_document, size_t desired_capacity) noexcept {
298 // 1. It is wasteful to allocate a document and a parser for documents spanning less than MINIMAL_DOCUMENT_CAPACITY bytes.
299 // 2. If we allow desired_capacity = 0 then it is possible to exit this function with implementation == nullptr.
300 if(desired_capacity < MINIMAL_DOCUMENT_CAPACITY) { desired_capacity = MINIMAL_DOCUMENT_CAPACITY; }
301 // If we don't have enough capacity, (try to) automatically bump it.
302 // If the document needs allocation, do it too.
303 // Both in one if statement to minimize unlikely branching.
304 //
305 // Note: we must make sure that this function is called if capacity() == 0. We do so because we
306 // ensure that desired_capacity > 0.
307 if (simdjson_unlikely(capacity() < desired_capacity || target_document.capacity() < desired_capacity)) {
308 if (desired_capacity > max_capacity()) {
309 return error = CAPACITY;
310 }
311 error_code err1 = target_document.capacity() < desired_capacity ? target_document.allocate(desired_capacity) : SUCCESS;
312 error_code err2 = capacity() < desired_capacity ? allocate(desired_capacity, max_depth()) : SUCCESS;
313 if(err1 != SUCCESS) { return error = err1; }
314 if(err2 != SUCCESS) { return error = err2; }
315 }
316 return SUCCESS;
317}
318
319simdjson_inline void parser::set_max_capacity(size_t max_capacity) noexcept {
320 if(max_capacity > MINIMAL_DOCUMENT_CAPACITY) {
321 _max_capacity = max_capacity;
322 } else {
323 _max_capacity = MINIMAL_DOCUMENT_CAPACITY;
324 }
325}
326
327} // namespace dom
328} // namespace simdjson
329
330#endif // SIMDJSON_PARSER_INL_H
A forward-only stream of documents.
A parsed JSON document.
Definition document.h:16
simdjson_inline parser(size_t max_capacity=SIMDJSON_MAXSIZE_BYTES) noexcept
Create a JSON parser.
Definition parser-inl.h:23
simdjson_result< element > parse(const uint8_t *buf, size_t len, bool realloc_if_needed=true) &noexcept
Parse a JSON document and return a temporary reference to it.
Definition parser-inl.h:178
simdjson_result< element > parse_into_document(document &doc, const uint8_t *buf, size_t len, bool realloc_if_needed=true) &noexcept
Parse a JSON document into a provide document instance and return a temporary reference to it.
Definition parser-inl.h:114
simdjson_inline void set_max_capacity(size_t max_capacity) noexcept
Set max_capacity.
Definition parser-inl.h:319
simdjson_result< element > parse_unpadded(const uint8_t *buf, size_t len) &noexcept
Parse a JSON document whose buffer is not padded, in place and without copying it.
Definition parser-inl.h:195
simdjson_result< element > parse_into_document_unpadded(document &doc, const uint8_t *buf, size_t len) &noexcept
Parse a non-padded JSON document into a caller-provided document instance, in place and without copyi...
Definition parser-inl.h:144
simdjson_inline size_t max_capacity() const noexcept
The largest document this parser can automatically support.
Definition parser-inl.h:263
simdjson_result< document_stream > load_many(std::string_view path, size_t batch_size=dom::DEFAULT_BATCH_SIZE) noexcept
Load a file containing many JSON documents.
Definition parser-inl.h:106
simdjson_result< element > load_into_document(document &doc, std::string_view path) &noexcept
Load a JSON document from a file into a provide document instance and return a temporary reference to...
Definition parser-inl.h:99
simdjson_pure simdjson_inline size_t max_depth() const noexcept
The maximum level of nested object and arrays supported by this parser.
Definition parser-inl.h:266
simdjson_result< element > load(std::string_view path) &noexcept
Load a JSON document from a file and return a reference to it.
Definition parser-inl.h:95
simdjson_inline size_t capacity() const noexcept
The largest document this parser can support without reallocating.
Definition parser-inl.h:260
simdjson_inline parser & operator=(parser &&other) noexcept
Take another parser's buffers and state.
simdjson_warn_unused error_code allocate(size_t capacity, size_t max_depth=DEFAULT_MAX_DEPTH) noexcept
Ensure this parser has enough memory to process JSON documents up to capacity bytes in length and max...
Definition parser-inl.h:271
simdjson_result< document_stream > parse_many(const uint8_t *buf, size_t len, size_t batch_size=dom::DEFAULT_BATCH_SIZE) noexcept
Parse a buffer containing many JSON documents.
Definition parser-inl.h:205
An implementation of simdjson for a particular CPU architecture.
The top level simdjson namespace, containing everything the library provides.
Definition base.h:8
SIMDJSON_DLLIMPORTEXPORT internal::atomic_ptr< const implementation > & get_active_implementation()
The active implementation.
stream_format
Stream format for parse_many/iterate_many.
Definition base.h:61
@ comma_delimited
Comma-separated JSON documents (e.g., {...},{...},{...})
@ whitespace_delimited
Whitespace-delimited JSON documents (default, includes NDJSON/JSONL)
@ comma_delimited_array
A single JSON array whose elements are iterated as comma-separated documents (e.g....
const char * error_message(error_code error) noexcept
It is the convention throughout the code that the macro SIMDJSON_DEVELOPMENT_CHECKS determines whethe...
Definition error-inl.h:25
constexpr size_t DEFAULT_MAX_DEPTH
By default, simdjson supports this many nested objects and arrays.
Definition base.h:49
error_code
All possible errors returned by simdjson.
Definition error.h:19
@ CAPACITY
This parser can't support a document that big.
Definition error.h:21
@ TAPE_ERROR
Something went wrong, this is a generic error. Fatal/unrecoverable error.
Definition error.h:23
@ MEMALLOC
Error allocating memory, most likely out of memory.
Definition error.h:22
@ SUCCESS
No error.
Definition error.h:20
@ IO_ERROR
Error reading a file.
Definition error.h:41
constexpr size_t SIMDJSON_PADDING
The amount of padding needed in a buffer to parse JSON.
Definition base.h:42
The result of a simdjson operation that could fail.
Definition error.h:281
simdjson_warn_unused simdjson_inline error_code get(T &value) &&noexcept
Move the value to the provided variable.
Definition error-inl.h:163