Pigweed
Loading...
Searching...
No Matches
builder.h
Go to the documentation of this file.
1// Copyright 2024 The Pigweed Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may not
4// use this file except in compliance with the License. You may obtain a copy of
5// the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12// License for the specific language governing permissions and limitations under
13// the License.
14#pragma once
15
29
30#include <cstddef>
31#include <string_view>
32#include <type_traits>
33
34#include "pw_assert/assert.h"
35#include "pw_json/internal/nesting.h"
36#include "pw_span/span.h"
37#include "pw_status/status.h"
38#include "pw_status/status_with_size.h"
39
40namespace pw {
41
44
50class [[nodiscard]] NestedJsonArray {
51 public:
52 NestedJsonArray(const NestedJsonArray&) = delete;
53 NestedJsonArray& operator=(const NestedJsonArray&) = delete;
54
55 constexpr NestedJsonArray(NestedJsonArray&&) = default;
56 constexpr NestedJsonArray& operator=(NestedJsonArray&&) = default;
57
59 template <typename T>
60 constexpr NestedJsonArray& Append(const T& value);
61
64
67
68 private:
69 friend class JsonArray;
70 friend class JsonObject;
71 friend class NestedJsonObject;
72
73 constexpr NestedJsonArray(json_impl::NestedJson&& nested)
74 : json_(std::move(nested)) {}
75
76 json_impl::NestedJson json_;
77};
78
84class [[nodiscard]] NestedJsonObject {
85 public:
86 NestedJsonObject(const NestedJsonObject&) = delete;
87 NestedJsonObject& operator=(const NestedJsonObject&) = delete;
88
89 constexpr NestedJsonObject(NestedJsonObject&&) = default;
90 constexpr NestedJsonObject& operator=(NestedJsonObject&&) = default;
91
93 template <typename T>
94 constexpr NestedJsonObject& Add(std::string_view key, const T& value);
95
97 constexpr NestedJsonArray AddNestedArray(std::string_view key);
98
100 constexpr NestedJsonObject AddNestedObject(std::string_view key);
101
102 private:
103 friend class JsonArray;
104 friend class JsonObject;
105 friend class NestedJsonArray;
106
107 constexpr NestedJsonObject(json_impl::NestedJson&& nested)
108 : json_(std::move(nested)) {}
109
110 json_impl::NestedJson json_;
111};
112
117 public:
118 constexpr JsonValue(const JsonValue&) = delete;
119 constexpr JsonValue& operator=(const JsonValue&) = delete;
120
121 // Functions common to all JSON types.
122 [[nodiscard]] constexpr bool IsValue() const;
123 [[nodiscard]] constexpr bool IsArray() const;
124 [[nodiscard]] constexpr bool IsObject() const;
125
126 constexpr operator std::string_view() const;
127 constexpr const char* data() const;
128 constexpr size_t size() const;
129 constexpr size_t max_size() const;
130
131 [[nodiscard]] constexpr bool ok() const;
132 constexpr Status status() const;
133 constexpr Status last_status() const;
134 constexpr void clear();
135 constexpr void clear_status();
136
154 template <typename T>
155 constexpr Status Set(const T& value);
156
157 private:
158 friend class JsonBuilder;
159
160 constexpr JsonValue() = default;
161};
162
167 public:
168 constexpr JsonArray(const JsonArray&) = delete;
169 constexpr JsonArray& operator=(const JsonArray&) = delete;
170
171 // Functions common to all JSON types. See documentation for `JsonBuilder`.
172 [[nodiscard]] constexpr bool IsValue() const;
173 [[nodiscard]] constexpr bool IsArray() const;
174 [[nodiscard]] constexpr bool IsObject() const;
175
176 constexpr operator std::string_view() const;
177 constexpr const char* data() const;
178 constexpr size_t size() const;
179 constexpr size_t max_size() const;
180
181 [[nodiscard]] constexpr bool ok() const;
182 constexpr Status status() const;
183 constexpr Status last_status() const;
184 constexpr void clear();
185 constexpr void clear_status();
186
191 template <typename T>
192 constexpr JsonArray& Append(const T& value);
193
196
199
202 template <typename Iterable>
203 constexpr JsonArray& Extend(const Iterable& iterable);
204
207 template <typename T, size_t kSize>
208 constexpr JsonArray& Extend(const T (&iterable)[kSize]);
209
210 private:
211 friend class JsonBuilder;
212
213 constexpr JsonArray() = default;
214};
215
220 public:
221 constexpr JsonObject(const JsonObject&) = delete;
222 constexpr JsonObject& operator=(const JsonObject&) = delete;
223
224 // Functions common to all JSON types. See documentation for `JsonBuilder`.
225 [[nodiscard]] constexpr bool IsValue() const;
226 [[nodiscard]] constexpr bool IsArray() const;
227 [[nodiscard]] constexpr bool IsObject() const;
228
229 constexpr operator std::string_view() const;
230 constexpr const char* data() const;
231 constexpr size_t size() const;
232 constexpr size_t max_size() const;
233
234 [[nodiscard]] constexpr bool ok() const;
235 constexpr Status status() const;
236 constexpr Status last_status() const;
237 constexpr void clear();
238 constexpr void clear_status();
239
254 template <typename T>
255 constexpr JsonObject& Add(std::string_view key, const T& value);
256
257 template <typename T>
258 constexpr JsonObject& Add(std::nullptr_t, const T& value) = delete;
259
260 constexpr NestedJsonArray AddNestedArray(std::string_view key);
261
262 constexpr NestedJsonObject AddNestedObject(std::string_view key);
263
264 private:
265 friend class JsonBuilder;
266
267 constexpr JsonObject() = default;
268};
269
273class JsonBuilder : private JsonValue, private JsonArray, private JsonObject {
274 public:
276 static constexpr size_t MinBufferSize() { return 5; }
277
279 constexpr JsonBuilder(span<char> buffer)
280 : JsonBuilder(buffer.data(), buffer.size()) {}
281
283 constexpr JsonBuilder(char* buffer, size_t buffer_size)
284 : JsonBuilder(buffer, buffer_size, Uninitialized{}) {
285 PW_ASSERT(buffer_size >= MinBufferSize()); // Must be at least 5 characters
286 MakeNull();
287 }
288
290 [[nodiscard]] constexpr bool IsValue() const {
291 return !IsObject() && !IsArray();
292 }
293
295 [[nodiscard]] constexpr bool IsArray() const { return buffer_[0] == '['; }
296
298 [[nodiscard]] constexpr bool IsObject() const { return buffer_[0] == '{'; }
299
301 constexpr operator std::string_view() const { return {data(), size()}; }
302
304 constexpr const char* data() const { return buffer_; }
305
307 constexpr size_t size() const { return json_size_; }
308
310 constexpr size_t max_size() const { return max_size_; }
311
313 [[nodiscard]] constexpr bool ok() const { return status().ok(); }
314
329 constexpr Status status() const { return static_cast<Status::Code>(status_); }
330
333 constexpr Status last_status() const {
334 return static_cast<Status::Code>(last_status_);
335 }
336
338 constexpr void clear() { JsonValueClear(); }
339
341 constexpr void clear_status() { set_statuses(OkStatus()); }
342
344 template <typename T>
345 constexpr Status SetValue(const T& value);
346
349 [[nodiscard]] constexpr JsonValue& StartValue() {
350 JsonValueClear();
351 return *this;
352 }
353
363 [[nodiscard]] constexpr JsonArray& StartArray() {
364 JsonArrayClear();
365 return *this;
366 }
367
378 [[nodiscard]] constexpr JsonObject& StartObject() {
379 JsonObjectClear();
380 return *this;
381 }
382
383 protected:
384 enum class Uninitialized {};
385
386 // Constructor that doesn't initialize the buffer.
387 constexpr JsonBuilder(char* buffer, size_t buffer_size, Uninitialized)
388 : buffer_(buffer),
389 max_size_(buffer_size - 1),
390 json_size_(0),
391 status_(OkStatus().code()),
392 last_status_(OkStatus().code()) {}
393
394 // Sets the buffer to the null value.
395 constexpr void MakeNull() {
396 buffer_[0] = 'n';
397 buffer_[1] = 'u';
398 buffer_[2] = 'l';
399 buffer_[3] = 'l';
400 buffer_[4] = '\0';
401 json_size_ = 4;
402 }
403
404 constexpr void set_json_size(size_t json_size) { json_size_ = json_size; }
405
406 constexpr void set_statuses(Status status, Status last_status) {
407 status_ = status.code();
408 last_status_ = last_status.code();
409 }
410
411 private:
412 friend class JsonValue;
413 friend class JsonArray;
414 friend class JsonObject;
415
416 friend class NestedJsonArray;
417 friend class NestedJsonObject;
418
419 constexpr size_t remaining() const { return max_size() - size(); }
420
421 // Sets last_status_ and updates status_ if an error occurred.
422 constexpr void update_status(Status new_status);
423
424 constexpr void set_statuses(Status status) { set_statuses(status, status); }
425
426 constexpr void JsonValueClear() {
427 MakeNull();
428 set_statuses(OkStatus());
429 }
430
431 constexpr void JsonArrayClear() {
432 MakeEmpty('[', ']');
433 set_statuses(OkStatus());
434 }
435
436 constexpr void JsonObjectClear() {
437 MakeEmpty('{', '}');
438 set_statuses(OkStatus());
439 }
440
441 template <typename T>
442 constexpr Status JsonValueSet(const T& value);
443
444 template <typename T>
445 constexpr JsonArray& JsonArrayAppend(const T& value);
446
447 template <typename Iterator>
448 constexpr JsonArray& JsonArrayExtend(Iterator begin, Iterator end);
449
450 template <typename T>
451 constexpr JsonObject& JsonObjectAdd(std::string_view key, const T& value);
452
453 [[nodiscard]] constexpr bool JsonArrayAddElement();
454
455 // Adds the key if there's room for the key and at least one more character.
456 [[nodiscard]] constexpr bool JsonObjectAddKey(std::string_view key);
457
458 constexpr size_t NestedJsonOffset(const json_impl::Nesting& nesting) const {
459 // Point to the start of the nested JSON array or object. This will be three
460 // characters, plus one for each prior layer of nesting {..., "": []}.
461 return json_size_ - 3 - nesting.depth();
462 }
463
464 constexpr json_impl::Nesting::Type type() const {
465 return IsArray() ? json_impl::Nesting::kArray : json_impl::Nesting::kObject;
466 }
467
468 constexpr json_impl::NestedJson JsonArrayAppendNested(
469 const char (&open_close)[2], const json_impl::Nesting& nesting);
470
471 constexpr json_impl::NestedJson JsonObjectAddNested(
472 std::string_view key,
473 const char (&open_close)[2],
474 const json_impl::Nesting& nesting);
475
476 // Nesting works by shrinking the JsonBuilder to be just the nested structure,
477 // then expanding back out when done adding items.
478 constexpr void AddNestedStart(const json_impl::Nesting& nesting);
479 constexpr void AddNestedFinish(const json_impl::Nesting& nesting);
480
481 template <typename T>
482 constexpr void NestedJsonArrayAppend(const T& value,
483 const json_impl::Nesting& nesting);
484
485 template <typename T>
486 constexpr void NestedJsonObjectAdd(std::string_view key,
487 const T& value,
488 const json_impl::Nesting& nesting);
489
490 // For a single JSON value, checks if writing succeeded and clears on failure.
491 constexpr Status HandleSet(StatusWithSize written);
492
493 // For a value added to an array or object, checks if writing the characters
494 // succeeds, sets the status, and terminates the buffer as appropriate.
495 constexpr void HandleAdd(StatusWithSize written,
496 size_t starting_size,
497 char terminator);
498
499 constexpr void MakeEmpty(char open, char close) {
500 buffer_[0] = open;
501 buffer_[1] = close;
502 buffer_[2] = '\0';
503 json_size_ = 2;
504 }
505
506 // TODO: b/326097937 - Use StringBuilder here.
507 char* buffer_;
508 size_t max_size_; // max size of the JSON string, excluding the '\0'
509 size_t json_size_;
510
511 // If any errors have occurred, status_ stores the most recent error Status.
512 // last_status_ stores the status from the most recent operation.
513 uint8_t status_;
514 uint8_t last_status_;
515};
516
519template <size_t kMaxSize>
520class JsonBuffer final : public JsonBuilder {
521 public:
522 // Constructs a JsonBuffer with the value null.
523 constexpr JsonBuffer()
524 : JsonBuilder(static_buffer_, sizeof(static_buffer_), Uninitialized{}),
525 static_buffer_{} {
526 MakeNull();
527 }
528
529 template <typename T>
530 static constexpr JsonBuffer Value(const T& initial_value) {
532 PW_ASSERT(json.SetValue(initial_value).ok()); // Failed serialization.
533 return json;
534 }
535
536 // JsonBuffers may be copied or assigned, as long as the source buffer is not
537 // larger than this buffer.
538 constexpr JsonBuffer(const JsonBuffer& other) : JsonBuffer() {
539 CopyFrom(other);
540 }
541
542 template <size_t kOtherSize>
543 constexpr JsonBuffer(const JsonBuffer<kOtherSize>& other) : JsonBuffer() {
544 CopyFrom(other);
545 }
546
547 constexpr JsonBuffer& operator=(const JsonBuffer& rhs) {
548 CopyFrom(rhs);
549 return *this;
550 }
551
552 template <size_t kOtherSize>
553 constexpr JsonBuffer& operator=(const JsonBuffer<kOtherSize>& rhs) {
554 CopyFrom(rhs);
555 return *this;
556 }
557
558 static constexpr size_t max_size() { return kMaxSize; }
559
560 private:
561 static_assert(kMaxSize + 1 /* null */ >= JsonBuilder::MinBufferSize(),
562 "JsonBuffers require at least 4 bytes");
563
564 template <size_t kOtherSize>
565 constexpr void CopyFrom(const JsonBuffer<kOtherSize>& other) {
566 static_assert(kOtherSize <= kMaxSize,
567 "A JsonBuffer cannot be copied into a smaller buffer");
568 CopyFrom(static_cast<const JsonBuilder&>(other));
569 }
570
571 constexpr void CopyFrom(const JsonBuilder& other) {
572 for (size_t i = 0; i < other.size() + 1 /* include null */; ++i) {
573 static_buffer_[i] = other.data()[i];
574 }
575 JsonBuilder::set_json_size(other.size());
576 JsonBuilder::set_statuses(other.status(), other.last_status());
577 }
578
579 char static_buffer_[kMaxSize + 1];
580};
581
583
584} // namespace pw
585
586// Functions are defined inline in a separate header.
587#include "pw_json/internal/builder_impl.h"
Definition: block.h:364
Definition: builder.h:166
constexpr JsonArray & Extend(const T(&iterable)[kSize])
constexpr JsonArray & Append(const T &value)
constexpr JsonArray & Extend(const Iterable &iterable)
constexpr NestedJsonArray AppendNestedArray()
Appends a nested array to this array.
constexpr NestedJsonObject AppendNestedObject()
Appends a nested object to this array.
Definition: builder.h:520
Definition: builder.h:273
constexpr Status status() const
Definition: builder.h:329
constexpr bool IsArray() const
True if the top-level JSON entity is an array.
Definition: builder.h:295
constexpr JsonBuilder(char *buffer, size_t buffer_size)
Initializes to the value null. buffer_size must be at least 5.
Definition: builder.h:283
constexpr JsonObject & StartObject()
Definition: builder.h:378
constexpr size_t max_size() const
The maximum size of the JSON string, excluding the null terminator.
Definition: builder.h:310
constexpr bool IsValue() const
True if the top-level JSON entity is a simple value (not array or object).
Definition: builder.h:290
constexpr Status last_status() const
Definition: builder.h:333
constexpr JsonBuilder(span< char > buffer)
Initializes to the value null. buffer.size() must be at least 5.
Definition: builder.h:279
constexpr const char * data() const
Pointer to the serialized JSON, which is always a null-terminated string.
Definition: builder.h:304
constexpr Status SetValue(const T &value)
Clears the JSON and sets it to a single JSON value (see JsonValue::Set).
constexpr size_t size() const
The current size of the JSON string, excluding the null terminator.
Definition: builder.h:307
constexpr bool ok() const
True if.
Definition: builder.h:313
constexpr void clear()
Sets the JSON null and clears the status.
Definition: builder.h:338
constexpr JsonArray & StartArray()
Definition: builder.h:363
constexpr void clear_status()
Resets status() and last_status().
Definition: builder.h:341
static constexpr size_t MinBufferSize()
JsonBuilder requires at least 5 characters in its buffer.
Definition: builder.h:276
constexpr JsonValue & StartValue()
Definition: builder.h:349
constexpr bool IsObject() const
True if the top-level JSON entity is an object.
Definition: builder.h:298
Definition: builder.h:219
constexpr JsonObject & Add(std::string_view key, const T &value)
Definition: builder.h:116
constexpr Status Set(const T &value)
Definition: builder.h:50
constexpr NestedJsonObject AppendNestedObject()
Appends a new nested object to this nested array.
constexpr NestedJsonArray AppendNestedArray()
Appends a new nested array to this nested array.
constexpr NestedJsonArray & Append(const T &value)
Appends to the nested array.
Definition: builder.h:84
constexpr NestedJsonArray AddNestedArray(std::string_view key)
Adds a nested array to the nested object.
constexpr NestedJsonObject & Add(std::string_view key, const T &value)
Adds a key-value pair to the nested object.
constexpr NestedJsonObject AddNestedObject(std::string_view key)
Adds a nested object to the nested object.
Definition: status.h:84
constexpr Code code() const
Returns the Status::Code (pw_Status) for this Status.
Definition: status.h:151
constexpr bool ok() const
Definition: status.h:156
Provides basic helpers for reading and writing UTF-8 encoded strings.
Definition: alignment.h:27
constexpr Status OkStatus()
Definition: status.h:233