simdjson 5.0.1
Ridiculously Fast JSON
Loading...
Searching...
No Matches
padded_string-inl.h
1#ifndef SIMDJSON_PADDED_STRING_INL_H
2#define SIMDJSON_PADDED_STRING_INL_H
3
4#include "simdjson/padded_string.h"
5#include "simdjson/padded_string_view.h"
6
7#include "simdjson/error-inl.h"
8#include "simdjson/padded_string_view-inl.h"
9
10#include <climits>
11#include <cwchar>
12
13#if SIMDJSON_HAS_UNISTD_H
14#include <fcntl.h>
15#include <stdio.h>
16#include <sys/mman.h>
17#include <sys/stat.h>
18#include <unistd.h>
19#endif
20// On Windows, `padded_memory_map` (when it is enabled) depends on types and
21// functions declared in <windows.h>. We deliberately do NOT include that
22// header here: users of simdjson who want `padded_memory_map` on Windows
23// must include <windows.h> themselves *before* including this header. See
24// padded_string.h for the detection logic.
25
26namespace simdjson {
27namespace internal {
28
29// The allocate_padded_buffer function is a low-level function to allocate memory
30// with padding so we can read past the "length" bytes safely. It is used by
31// the padded_string class automatically. It returns nullptr in case
32// of error: the caller should check for a null pointer.
33// The length parameter is the maximum size in bytes of the string.
34// The caller is responsible to free the memory (e.g., delete[] (...)).
35inline char *allocate_padded_buffer(size_t length) noexcept {
36 const size_t totalpaddedlength = length + SIMDJSON_PADDING;
37 if(totalpaddedlength<length) {
38 // overflow
39 return nullptr;
40 }
41#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
42 // avoid getting out of memory
43 if (totalpaddedlength>(1UL<<20)) {
44 return nullptr;
45 }
46#endif
47
48 char *padded_buffer = new (std::nothrow) char[totalpaddedlength];
49 if (padded_buffer == nullptr) {
50 return nullptr;
51 }
52 // We write nulls in the padded region to avoid having uninitialized
53 // content which may trigger warning for some sanitizers
54 std::memset(padded_buffer + length, 0, totalpaddedlength - length);
55 return padded_buffer;
56} // allocate_padded_buffer()
57
58} // namespace internal
59
60
61inline padded_string::padded_string() noexcept = default;
62inline padded_string::padded_string(size_t length) noexcept
63 : viable_size(length), data_ptr(internal::allocate_padded_buffer(length)) {
64 if (data_ptr == nullptr) { viable_size = 0; }
65}
66inline padded_string::padded_string(const char *data, size_t length) noexcept
67 : viable_size(length), data_ptr(internal::allocate_padded_buffer(length)) {
68 if ((data != nullptr) && (data_ptr != nullptr)) {
69 std::memcpy(data_ptr, data, length);
70 }
71 if (data_ptr == nullptr) {
72 viable_size = 0;
73 }
74}
75#ifdef __cpp_char8_t
76inline padded_string::padded_string(const char8_t *data, size_t length) noexcept
77 : viable_size(length), data_ptr(internal::allocate_padded_buffer(length)) {
78 if ((data != nullptr) && (data_ptr != nullptr)) {
79 std::memcpy(data_ptr, reinterpret_cast<const char *>(data), length);
80 }
81 if (data_ptr == nullptr) {
82 viable_size = 0;
83 }
84}
85#endif
86// note: do not pass std::string arguments by value
87inline padded_string::padded_string(const std::string & str_ ) noexcept
88 : viable_size(str_.size()), data_ptr(internal::allocate_padded_buffer(str_.size())) {
89 if (data_ptr == nullptr) {
90 viable_size = 0;
91 } else {
92 std::memcpy(data_ptr, str_.data(), str_.size());
93 }
94}
95// note: do pass std::string_view arguments by value
96inline padded_string::padded_string(std::string_view sv_) noexcept
97 : viable_size(sv_.size()), data_ptr(internal::allocate_padded_buffer(sv_.size())) {
98 if(simdjson_unlikely(!data_ptr)) {
99 //allocation failed or zero size
100 viable_size = 0;
101 return;
102 }
103 if (sv_.size()) {
104 std::memcpy(data_ptr, sv_.data(), sv_.size());
105 }
106}
108 : viable_size(o.viable_size), data_ptr(o.data_ptr) {
109 o.data_ptr = nullptr; // we take ownership
110 o.viable_size = 0;
111}
112
114 delete[] data_ptr;
115 data_ptr = o.data_ptr;
116 viable_size = o.viable_size;
117 o.data_ptr = nullptr; // we take ownership
118 o.viable_size = 0;
119 return *this;
120}
121
122inline void padded_string::swap(padded_string &o) noexcept {
123 size_t tmp_viable_size = viable_size;
124 char *tmp_data_ptr = data_ptr;
125 viable_size = o.viable_size;
126 data_ptr = o.data_ptr;
127 o.data_ptr = tmp_data_ptr;
128 o.viable_size = tmp_viable_size;
129}
130
131inline padded_string::~padded_string() noexcept {
132 delete[] data_ptr;
133}
134
135inline size_t padded_string::size() const noexcept { return viable_size; }
136
137inline size_t padded_string::length() const noexcept { return viable_size; }
138
139inline const char *padded_string::data() const noexcept { return data_ptr; }
140
141inline char *padded_string::data() noexcept { return data_ptr; }
142
143inline bool padded_string::append(const char *data, size_t length) noexcept {
144 if (length == 0) {
145 return true; // Nothing to append
146 }
147 size_t new_size = viable_size + length;
148 if (new_size < viable_size) {
149 // Overflow, cannot append
150 return false;
151 }
152 char *new_data_ptr = internal::allocate_padded_buffer(new_size);
153 if (new_data_ptr == nullptr) {
154 // Allocation failed, cannot append
155 return false;
156 }
157 // Copy existing data
158 if (viable_size > 0) {
159 std::memcpy(new_data_ptr, data_ptr, viable_size);
160 }
161 // Copy new data
162 std::memcpy(new_data_ptr + viable_size, data, length);
163 // Update
164 delete[] data_ptr;
165 data_ptr = new_data_ptr;
166 viable_size = new_size;
167 return true;
168}
169
170inline padded_string::operator std::string_view() const simdjson_lifetime_bound { return std::string_view(data(), length()); }
171
172inline padded_string::operator padded_string_view() const noexcept simdjson_lifetime_bound {
173 return padded_string_view(data(), length(), length() + SIMDJSON_PADDING);
174}
175
176inline simdjson_result<padded_string> padded_string::load(std::string_view filename) noexcept {
177 // std::string_view is not guaranteed to be null-terminated, but std::fopen requires
178 // a null-terminated C string. Construct a temporary std::string to ensure null-termination.
179 const std::string null_terminated_filename(filename);
180 // Open the file
181 SIMDJSON_PUSH_DISABLE_WARNINGS
182 SIMDJSON_DISABLE_DEPRECATED_WARNING // Disable CRT_SECURE warning on MSVC: manually verified this is safe
183 std::FILE *fp = std::fopen(null_terminated_filename.c_str(), "rb");
184 SIMDJSON_POP_DISABLE_WARNINGS
185
186 if (fp == nullptr) {
187 return IO_ERROR;
188 }
189
190 // Get the file size
191 int ret;
192#if SIMDJSON_VISUAL_STUDIO && !SIMDJSON_IS_32BITS
193 ret = _fseeki64(fp, 0, SEEK_END);
194#else
195 ret = std::fseek(fp, 0, SEEK_END);
196#endif // _WIN64
197 if(ret < 0) {
198 std::fclose(fp);
199 return IO_ERROR;
200 }
201#if SIMDJSON_VISUAL_STUDIO && !SIMDJSON_IS_32BITS
202 __int64 llen = _ftelli64(fp);
203 if(llen == -1L) {
204 std::fclose(fp);
205 return IO_ERROR;
206 }
207#else
208 long llen = std::ftell(fp);
209 if((llen < 0) || (llen == LONG_MAX)) {
210 std::fclose(fp);
211 return IO_ERROR;
212 }
213#endif
214
215 // Allocate the padded_string
216 size_t len = static_cast<size_t>(llen);
217 padded_string s(len);
218 if (s.data() == nullptr) {
219 std::fclose(fp);
220 return MEMALLOC;
221 }
222
223 // Read the padded_string
224 std::rewind(fp);
225 size_t bytes_read = std::fread(s.data(), 1, len, fp);
226 if (std::fclose(fp) != 0 || bytes_read != len) {
227 return IO_ERROR;
228 }
229
230 return s;
231}
232
233#if defined(_WIN32) && SIMDJSON_CPLUSPLUS17
234inline simdjson_result<padded_string> padded_string::load(std::wstring_view filename) noexcept {
235 // std::wstring_view is not guaranteed to be null-terminated, but _wfopen requires
236 // a null-terminated wide C string. Construct a temporary std::wstring to ensure null-termination.
237 const std::wstring null_terminated_filename(filename);
238 // Open the file using the wide characters
239 SIMDJSON_PUSH_DISABLE_WARNINGS
240 SIMDJSON_DISABLE_DEPRECATED_WARNING // Disable CRT_SECURE warning on MSVC: manually verified this is safe
241 std::FILE *fp = _wfopen(null_terminated_filename.c_str(), L"rb");
242 SIMDJSON_POP_DISABLE_WARNINGS
243
244 if (fp == nullptr) {
245 return IO_ERROR;
246 }
247
248 // Get the file size
249 int ret;
250#if SIMDJSON_VISUAL_STUDIO && !SIMDJSON_IS_32BITS
251 ret = _fseeki64(fp, 0, SEEK_END);
252#else
253 ret = std::fseek(fp, 0, SEEK_END);
254#endif // _WIN64
255 if(ret < 0) {
256 std::fclose(fp);
257 return IO_ERROR;
258 }
259#if SIMDJSON_VISUAL_STUDIO && !SIMDJSON_IS_32BITS
260 __int64 llen = _ftelli64(fp);
261 if(llen == -1L) {
262 std::fclose(fp);
263 return IO_ERROR;
264 }
265#else
266 long llen = std::ftell(fp);
267 if((llen < 0) || (llen == LONG_MAX)) {
268 std::fclose(fp);
269 return IO_ERROR;
270 }
271#endif
272
273 // Allocate the padded_string
274 size_t len = static_cast<size_t>(llen);
275 padded_string s(len);
276 if (s.data() == nullptr) {
277 std::fclose(fp);
278 return MEMALLOC;
279 }
280
281 // Read the padded_string
282 std::rewind(fp);
283 size_t bytes_read = std::fread(s.data(), 1, len, fp);
284 if (std::fclose(fp) != 0 || bytes_read != len) {
285 return IO_ERROR;
286 }
287
288 return s;
289}
290#endif
291
292// padded_string_builder implementations
293
294inline padded_string_builder::padded_string_builder() noexcept = default;
295
296inline padded_string_builder::padded_string_builder(size_t new_capacity) noexcept {
297 if (new_capacity > 0) {
298 data = internal::allocate_padded_buffer(new_capacity);
299 if (data != nullptr) {
300 this->capacity = new_capacity;
301 }
302 }
303}
304
306 : size(o.size), capacity(o.capacity), data(o.data) {
307 o.size = 0;
308 o.capacity = 0;
309 o.data = nullptr;
310}
311
313 if (this != &o) {
314 delete[] data;
315 size = o.size;
316 capacity = o.capacity;
317 data = o.data;
318 o.size = 0;
319 o.capacity = 0;
320 o.data = nullptr;
321 }
322 return *this;
323}
324
326 delete[] data;
327}
328
329inline bool padded_string_builder::append(const char *newdata, size_t length) noexcept {
330 if (length == 0) {
331 return true;
332 }
333 if (!reserve(length)) {
334 return false;
335 }
336 std::memcpy(data + size, newdata, length);
337 size += length;
338 return true;
339}
340
341inline bool padded_string_builder::append(std::string_view sv) noexcept {
342 return append(sv.data(), sv.size());
343}
344
345inline size_t padded_string_builder::length() const noexcept {
346 return size;
347}
348
350 return padded_string(data, size);
351}
352
354 padded_string result{};
355 result.data_ptr = data;
356 result.viable_size = size;
357 data = nullptr;
358 size = 0;
359 capacity = 0;
360 return result;
361}
362
363inline bool padded_string_builder::reserve(size_t additional) noexcept {
364 if (simdjson_unlikely(additional + size < size)) {
365 return false; // overflow: cannot satisfy request
366 }
367 size_t needed = size + additional;
368 if (needed <= capacity) {
369 return true;
370 }
371 size_t new_capacity = needed;
372 // We are going to grow the capacity exponentially to avoid
373 // repeated allocations.
374 if (new_capacity < 4096) {
375 new_capacity *= 2;
376 // overflow guard: ensure new_capacity + new_capacity/2 does not overflow
377 } else if (new_capacity + new_capacity / 2 > new_capacity) {
378 new_capacity += new_capacity / 2; // grow by 1.5x
379 }
380 char *new_data = internal::allocate_padded_buffer(new_capacity);
381 if (new_data == nullptr) {
382 return false; // Allocation failed
383 }
384 if (size > 0) {
385 std::memcpy(new_data, data, size);
386 }
387 delete[] data;
388 data = new_data;
389 capacity = new_capacity;
390 return true;
391}
392
393
394#if SIMDJSON_HAS_PADDED_MEMORY_MAP
395
396#if SIMDJSON_HAS_UNISTD_H
397simdjson_inline padded_memory_map::padded_memory_map(const char *filename) noexcept {
398
399 int fd = open(filename, O_RDONLY);
400 if (fd == -1) {
401 return; // file not found or cannot be opened, data will be nullptr
402 }
403 struct stat st;
404 if (fstat(fd, &st) == -1) {
405 close(fd);
406 return; // failed to get file size, data will be nullptr
407 }
408 size = static_cast<size_t>(st.st_size);
409 size_t total_size = size + simdjson::SIMDJSON_PADDING;
410 void *anon_map =
411 mmap(NULL, total_size, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
412 if (anon_map == MAP_FAILED) {
413 close(fd);
414 return; // failed to create anonymous mapping, data will be nullptr
415 }
416 void *file_map =
417 mmap(anon_map, size, PROT_READ, MAP_SHARED | MAP_FIXED, fd, 0);
418 if (file_map == MAP_FAILED) {
419 munmap(anon_map, total_size);
420 close(fd);
421 return; // failed to mmap file, data will be nullptr
422 }
423 data = static_cast<const char *>(file_map);
424 close(fd); // no longer needed after mapping
425}
426
427simdjson_inline padded_memory_map::~padded_memory_map() noexcept {
428 if (data != nullptr) {
429 munmap(const_cast<char *>(data), size + simdjson::SIMDJSON_PADDING);
430 }
431}
432#elif defined(_WIN32)
433// Windows zero-copy implementation using placeholder virtual memory.
434//
435// We use the modern Windows memory APIs (VirtualAlloc2, CreateFileMapping2,
436// MapViewOfFile3 -- available since Windows 10 1803) to map the file into a
437// contiguous virtual address range that includes at least SIMDJSON_PADDING
438// zero bytes after the file content, with no data copies.
439//
440// Strategy:
441// 1. If rounding the file size up to the allocation granularity already
442// exceeds file_size + SIMDJSON_PADDING, the OS page zero-fill provides
443// the padding and we use a simple MapViewOfFile3 call.
444// 2. Otherwise we reserve a contiguous placeholder region via VirtualAlloc2,
445// split it at the granularity-aligned file boundary, map the file into
446// the first part, and commit zero pages for the second part (padding).
447simdjson_inline padded_memory_map::padded_memory_map(const char *filename) noexcept {
448 HANDLE file_handle = ::CreateFileA(
449 filename, GENERIC_READ,
450 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
451 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
452 if (file_handle == INVALID_HANDLE_VALUE) {
453 return;
454 }
455 LARGE_INTEGER file_size_li;
456 if (!::GetFileSizeEx(file_handle, &file_size_li) || file_size_li.QuadPart < 0) {
457 ::CloseHandle(file_handle);
458 return;
459 }
460#if SIMDJSON_IS_32BITS
461 if (static_cast<unsigned long long>(file_size_li.QuadPart) >
462 static_cast<unsigned long long>(SIZE_MAX - simdjson::SIMDJSON_PADDING)) {
463 ::CloseHandle(file_handle);
464 return;
465 }
466#endif
467 size = static_cast<size_t>(file_size_li.QuadPart);
468 if (size == 0) {
469 ::CloseHandle(file_handle);
470 return;
471 }
472
473 HANDLE section = ::CreateFileMapping2(
474 file_handle, NULL, FILE_MAP_READ, PAGE_READONLY,
475 0, 0, NULL, NULL, 0);
476 ::CloseHandle(file_handle);
477 if (section == NULL) {
478 return;
479 }
480
481 SYSTEM_INFO si;
482 ::GetSystemInfo(&si);
483 const size_t granularity = static_cast<size_t>(si.dwAllocationGranularity);
484 const size_t file_region = (size + granularity - 1) & ~(granularity - 1);
485 const size_t total_needed = size + simdjson::SIMDJSON_PADDING;
486
487 if (file_region >= total_needed) {
488 // The zero-fill in the last page already covers the padding.
489 PVOID view = ::MapViewOfFile3(
490 section, ::GetCurrentProcess(), NULL, 0, 0,
491 0, PAGE_READONLY, NULL, 0);
492 ::CloseHandle(section);
493 if (view != NULL) {
494 data = static_cast<const char *>(view);
495 }
496 return;
497 }
498
499 // We need extra zero pages beyond the file region. Use the placeholder API
500 // to get a contiguous virtual address range spanning both the file mapping
501 // and the zero-filled padding.
502 const size_t padding_region =
503 ((total_needed - file_region) + granularity - 1) & ~(granularity - 1);
504 const size_t reserve_size = file_region + padding_region;
505
506 // Reserve a contiguous placeholder.
507 PVOID placeholder = ::VirtualAlloc2(
508 ::GetCurrentProcess(), NULL, reserve_size,
509 MEM_RESERVE | MEM_RESERVE_PLACEHOLDER, PAGE_NOACCESS, NULL, 0);
510 if (placeholder == NULL) {
511 ::CloseHandle(section);
512 return;
513 }
514
515 // Split into two placeholders at the file_region boundary.
516 if (!::VirtualFree(placeholder, file_region,
517 MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER)) {
518 ::VirtualFree(placeholder, 0, MEM_RELEASE);
519 ::CloseHandle(section);
520 return;
521 }
522
523 // Map the file into the first placeholder.
524 PVOID file_view = ::MapViewOfFile3(
525 section, ::GetCurrentProcess(), placeholder, 0, file_region,
526 MEM_REPLACE_PLACEHOLDER, PAGE_READONLY, NULL, 0);
527 ::CloseHandle(section);
528 if (file_view == NULL) {
529 ::VirtualFree(placeholder, 0, MEM_RELEASE);
530 ::VirtualFree(static_cast<char *>(placeholder) + file_region,
531 0, MEM_RELEASE);
532 return;
533 }
534
535 // Commit zero pages in the second placeholder (the padding).
536 void *pad = static_cast<char *>(placeholder) + file_region;
537 PVOID padding_ptr = ::VirtualAlloc2(
538 ::GetCurrentProcess(), pad, padding_region,
539 MEM_REPLACE_PLACEHOLDER | MEM_COMMIT, PAGE_READONLY, NULL, 0);
540 if (padding_ptr == NULL) {
541 ::UnmapViewOfFile(file_view);
542 ::VirtualFree(pad, 0, MEM_RELEASE);
543 return;
544 }
545
546 data = static_cast<const char *>(file_view);
547 padding_view_ = padding_ptr;
548}
549
550simdjson_inline padded_memory_map::~padded_memory_map() noexcept {
551 if (data == nullptr) { return; }
552 ::UnmapViewOfFile(data);
553 if (padding_view_ != nullptr) {
554 ::VirtualFree(padding_view_, 0, MEM_RELEASE);
555 }
556}
557#endif // POSIX or _WIN32
558
559simdjson_inline simdjson::padded_string_view padded_memory_map::view() const noexcept simdjson_lifetime_bound {
560 if(!is_valid()) {
561 return simdjson::padded_string_view(); // return an empty view if mapping failed
562 }
564}
565
566simdjson_inline bool padded_memory_map::is_valid() const noexcept {
567 return data != nullptr;
568}
569
570#endif // SIMDJSON_HAS_PADDED_MEMORY_MAP
571
572} // namespace simdjson
573
574inline simdjson::padded_string operator ""_padded(const char *str, size_t len) {
575 return simdjson::padded_string(str, len);
576}
577#ifdef __cpp_char8_t
578inline simdjson::padded_string operator ""_padded(const char8_t *str, size_t len) {
579 return simdjson::padded_string(reinterpret_cast<const char *>(str), len);
580}
581#endif
582
583#endif // SIMDJSON_PADDED_STRING_INL_H
Builder for constructing padded_string incrementally.
padded_string convert() noexcept
Convert the current content into a padded_string.
padded_string_builder & operator=(padded_string_builder &&o) noexcept
Move assignment.
~padded_string_builder() noexcept
Destructor.
size_t length() const noexcept
Get the current length of the built string.
padded_string build() const noexcept
Build a padded_string from the current content.
bool append(const char *newdata, size_t length) noexcept
Append data to the builder.
padded_string_builder() noexcept
Create a new, empty padded string builder.
User-provided string that promises it has extra padded bytes at the end for use with parser::parse().
The top level simdjson namespace, containing everything the library provides.
Definition base.h:8
@ MEMALLOC
Error allocating memory, most likely out of memory.
Definition error.h:22
@ IO_ERROR
Error reading a file.
Definition error.h:41
padded_string_view pad(std::string &s) noexcept
Create a padded_string_view from a string.
constexpr size_t SIMDJSON_PADDING
The amount of padding needed in a buffer to parse JSON.
Definition base.h:42
String with extra allocation for ease of use with parser::parse()
size_t size() const noexcept
The length of the string.
bool append(const char *data, size_t length) noexcept
Append data to the padded string.
size_t length() const noexcept
The length of the string.
padded_string() noexcept
Create a new, empty padded string.
padded_string & operator=(padded_string &&o) noexcept
Move one padded string into another.
const char * data() const noexcept
The string data.
static simdjson_result< padded_string > load(std::string_view path) noexcept
Load this padded string from a file.
The result of a simdjson operation that could fail.
Definition error.h:281