FastMM documentation
FastMM C++ API 0.6.0
Public headers, tier 1 and tier 2
Loading...
Searching...
No Matches
json_writer.hpp
Go to the documentation of this file.
1#pragma once
2// JsonWriter: append-only JSON builder over a caller-provided fixed buffer (no heap). Used by
3// the order encoders on the hot path. Overflow is sticky (ok() == false, size() frozen), so
4// a truncated request can never be sent. Strings are escaped minimally (quote, backslash,
5// control chars) - venue symbols and ids are plain ASCII anyway.
6#include "fastmm/core/config_macros.hpp"
7
8#include <cstddef>
9#include <cstdint>
10#include <cstring>
11#include <span>
12#include <string_view>
13
14namespace fastmm::venues {
15
16class JsonWriter {
17 public:
18 explicit JsonWriter(std::span<char> buf) noexcept : buf_(buf) {}
19
20 JsonWriter& begin_object() noexcept { return sep_value().raw_char('{').open(); }
21 JsonWriter& end_object() noexcept { return raw_char('}').close(); }
22 JsonWriter& begin_array() noexcept { return sep_value().raw_char('[').open(); }
23 JsonWriter& end_array() noexcept { return raw_char(']').close(); }
25 JsonWriter& key(std::string_view k) noexcept {
26 sep_value();
27 put_string(k);
28 raw_char(':');
29 pending_key_ = true; // the value that follows must not be preceded by a comma
30 return *this;
31 }
32 JsonWriter& string(std::string_view v) noexcept {
33 sep_value();
34 put_string(v);
35 return *this;
36 }
37 JsonWriter& integer(std::int64_t v) noexcept {
38 sep_value();
39 char tmp[21];
40 std::size_t n = 0;
41 std::uint64_t mag = v < 0 ? 0 - static_cast<std::uint64_t>(v) : static_cast<std::uint64_t>(v);
42 char d[20];
43 std::size_t k = 0;
44 do {
45 d[k++] = static_cast<char>('0' + mag % 10);
46 mag /= 10;
47 } while (mag != 0);
48 if (v < 0) tmp[n++] = '-';
49 while (k > 0) tmp[n++] = d[--k];
50 return raw(std::string_view(tmp, n));
51 }
52 JsonWriter& boolean(bool v) noexcept {
53 sep_value();
54 return raw(v ? "true" : "false");
55 }
56 JsonWriter& null() noexcept {
57 sep_value();
58 return raw("null");
59 }
60 // Pre-formatted JSON fragment (e.g. a nested object built elsewhere).
61 JsonWriter& raw_value(std::string_view v) noexcept {
62 sep_value();
63 return raw(v);
64 }
65
66 [[nodiscard]] std::string_view view() const noexcept { return {buf_.data(), len_}; }
67 [[nodiscard]] std::size_t size() const noexcept { return len_; }
68 [[nodiscard]] bool ok() const noexcept { return ok_; }
69 void clear() noexcept {
70 len_ = 0;
71 ok_ = true;
72 first_ = true;
73 depth_ = 0;
74 pending_key_ = false;
75 }
76
77 private:
78 JsonWriter& sep_value() noexcept {
79 if (pending_key_) {
80 pending_key_ = false;
81 return *this;
82 }
83 if (!first_) raw_char(',');
84 first_ = false;
85 return *this;
86 }
87 JsonWriter& open() noexcept {
88 first_ = true;
89 ++depth_;
90 return *this;
91 }
92 JsonWriter& close() noexcept {
93 first_ = false;
94 if (depth_ > 0) --depth_;
95 return *this;
96 }
97 // Any byte < 0x20, '"' or '\\'; 8 bytes per step (SWAR "has zero byte" tests).
98 [[nodiscard]] static bool needs_escape(std::string_view s) noexcept {
99 constexpr std::uint64_t kOnes = 0x0101'0101'0101'0101ULL;
100 constexpr std::uint64_t kHigh = 0x8080'8080'8080'8080ULL;
101 std::size_t i = 0;
102 std::uint64_t hit = 0;
103 for (; i + 8 <= s.size(); i += 8) {
104 std::uint64_t x = 0;
105 std::memcpy(&x, s.data() + i, 8);
106 const std::uint64_t quote = x ^ (kOnes * 0x22U); // zero byte where x has '"'
107 const std::uint64_t slash = x ^ (kOnes * 0x5CU); // zero byte where x has a backslash
108 hit |= (x - kOnes * 0x20) & ~x & kHigh; // some byte < 0x20
109 hit |= (quote - kOnes) & ~quote & kHigh;
110 hit |= (slash - kOnes) & ~slash & kHigh;
111 }
112 bool any = hit != 0;
113 for (; i < s.size(); ++i) {
114 const char c = s[i];
115 any |= static_cast<unsigned char>(c) < 0x20 || c == '"' || c == '\\';
116 }
117 return any;
118 }
119 void put_string(std::string_view s) noexcept {
120 if (FASTMM_LIKELY(!needs_escape(s))) { // ids, symbols, decimals: one copy
121 raw_char('"');
122 raw(s);
123 raw_char('"');
124 return;
125 }
126 raw_char('"');
127 for (char c : s) {
128 switch (c) {
129 case '"':
130 raw("\\\"");
131 break;
132 case '\\':
133 raw("\\\\");
134 break;
135 case '\n':
136 raw("\\n");
137 break;
138 case '\r':
139 raw("\\r");
140 break;
141 case '\t':
142 raw("\\t");
143 break;
144 default:
145 if (static_cast<unsigned char>(c) < 0x20) {
146 static constexpr char kHex[] = "0123456789abcdef";
147 raw("\\u00");
148 raw_char(kHex[(static_cast<unsigned char>(c) >> 4) & 0xF]);
149 raw_char(kHex[static_cast<unsigned char>(c) & 0xF]);
150 } else {
151 raw_char(c);
152 }
153 }
154 }
155 raw_char('"');
156 }
157 JsonWriter& raw(std::string_view s) noexcept {
158 if (!ok_) return *this;
159 if (s.size() > buf_.size() - len_) {
160 ok_ = false;
161 return *this;
162 }
163 if (!s.empty()) std::memcpy(buf_.data() + len_, s.data(), s.size());
164 len_ += s.size();
165 return *this;
166 }
167 JsonWriter& raw_char(char c) noexcept {
168 if (!ok_) return *this;
169 if (len_ >= buf_.size()) {
170 ok_ = false;
171 return *this;
172 }
173 buf_[len_++] = c;
174 return *this;
175 }
176
177 std::span<char> buf_;
178 std::size_t len_ = 0;
179 bool ok_ = true;
180 bool first_ = true;
181 bool pending_key_ = false;
182 int depth_ = 0;
183};
184
185} // namespace fastmm::venues
Definition json_writer.hpp:18
JsonWriter & begin_array() noexcept
Definition json_writer.hpp:24
JsonWriter & raw_value(std::string_view v) noexcept
Pre-formatted JSON fragment (e.g. a nested object built elsewhere).
Definition json_writer.hpp:63
JsonWriter & boolean(bool v) noexcept
Definition json_writer.hpp:54
JsonWriter(std::span< char > buf) noexcept
Definition json_writer.hpp:20
JsonWriter & end_object() noexcept
Definition json_writer.hpp:23
std::string_view view() const noexcept
Definition json_writer.hpp:68
JsonWriter & integer(std::int64_t v) noexcept
Definition json_writer.hpp:39
JsonWriter & end_array() noexcept
Definition json_writer.hpp:25
JsonWriter & string(std::string_view v) noexcept
Definition json_writer.hpp:34
JsonWriter & key(std::string_view k) noexcept
Definition json_writer.hpp:27
std::size_t size() const noexcept
Definition json_writer.hpp:69
JsonWriter & null() noexcept
Definition json_writer.hpp:58
JsonWriter & begin_object() noexcept
Definition json_writer.hpp:22
void clear() noexcept
Definition json_writer.hpp:71
bool ok() const noexcept
Definition json_writer.hpp:70
Definition venue.hpp:38