C/C++ API Reference
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#include "pw_string/type_to_string.h"
40
41namespace pw {
42
44
47
53class [[nodiscard]] NestedJsonArray {
54 public:
55 NestedJsonArray(const NestedJsonArray&) = delete;
56 NestedJsonArray& operator=(const NestedJsonArray&) = delete;
57
58 constexpr NestedJsonArray(NestedJsonArray&&) = default;
59 constexpr NestedJsonArray& operator=(NestedJsonArray&&) = default;
60
62 template <typename T>
63 constexpr NestedJsonArray& Append(const T& value);
64
66 constexpr NestedJsonArray AppendNestedArray();
67
69 constexpr NestedJsonObject AppendNestedObject();
70
71 private:
72 friend class JsonArray;
73 friend class JsonObject;
74 friend class NestedJsonObject;
75
76 constexpr NestedJsonArray(json_impl::NestedJson&& nested)
77 : json_(std::move(nested)) {}
78
79 json_impl::NestedJson json_;
80};
81
87class [[nodiscard]] NestedJsonObject {
88 public:
89 NestedJsonObject(const NestedJsonObject&) = delete;
90 NestedJsonObject& operator=(const NestedJsonObject&) = delete;
91
92 constexpr NestedJsonObject(NestedJsonObject&&) = default;
93 constexpr NestedJsonObject& operator=(NestedJsonObject&&) = default;
94
96 template <typename T>
97 constexpr NestedJsonObject& Add(std::string_view key, const T& value);
98
100 constexpr NestedJsonArray AddNestedArray(std::string_view key);
101
103 constexpr NestedJsonObject AddNestedObject(std::string_view key);
104
105 private:
106 friend class JsonArray;
107 friend class JsonObject;
108 friend class NestedJsonArray;
109
110 constexpr NestedJsonObject(json_impl::NestedJson&& nested)
111 : json_(std::move(nested)) {}
112
113 json_impl::NestedJson json_;
114};
115
120 public:
121 constexpr JsonValue(const JsonValue&) = delete;
122 constexpr JsonValue& operator=(const JsonValue&) = delete;
123
124 // Functions common to all JSON types.
125 [[nodiscard]] constexpr bool IsValue() const;
126 [[nodiscard]] constexpr bool IsArray() const;
127 [[nodiscard]] constexpr bool IsObject() const;
128
129 constexpr operator std::string_view() const;
130 constexpr const char* data() const;
131 constexpr size_t size() const;
132 constexpr size_t max_size() const;
133
134 [[nodiscard]] constexpr bool ok() const;
135 constexpr Status status() const;
136 constexpr Status last_status() const;
137 constexpr void clear();
138 constexpr void clear_status();
139
150 template <typename T>
151 constexpr Status Set(const T& value);
152
153 private:
154 friend class JsonBuilder;
155
156 constexpr JsonValue() = default;
157};
158
163 public:
164 constexpr JsonArray(const JsonArray&) = delete;
165 constexpr JsonArray& operator=(const JsonArray&) = delete;
166
167 // Functions common to all JSON types. See documentation for `JsonBuilder`.
168 [[nodiscard]] constexpr bool IsValue() const;
169 [[nodiscard]] constexpr bool IsArray() const;
170 [[nodiscard]] constexpr bool IsObject() const;
171
172 constexpr operator std::string_view() const;
173 constexpr const char* data() const;
174 constexpr size_t size() const;
175 constexpr size_t max_size() const;
176
177 [[nodiscard]] constexpr bool ok() const;
178 constexpr Status status() const;
179 constexpr Status last_status() const;
180 constexpr void clear();
181 constexpr void clear_status();
182
187 template <typename T>
188 constexpr JsonArray& Append(const T& value);
189
192
195
198 template <typename Iterable>
199 constexpr JsonArray& Extend(const Iterable& iterable);
200
203 template <typename T, size_t kSize>
204 constexpr JsonArray& Extend(const T (&iterable)[kSize]);
205
206 private:
207 friend class JsonBuilder;
208
209 constexpr JsonArray() = default;
210};
211
216 public:
217 constexpr JsonObject(const JsonObject&) = delete;
218 constexpr JsonObject& operator=(const JsonObject&) = delete;
219
220 // Functions common to all JSON types. See documentation for `JsonBuilder`.
221 [[nodiscard]] constexpr bool IsValue() const;
222 [[nodiscard]] constexpr bool IsArray() const;
223 [[nodiscard]] constexpr bool IsObject() const;
224
225 constexpr operator std::string_view() const;
226 constexpr const char* data() const;
227 constexpr size_t size() const;
228 constexpr size_t max_size() const;
229
230 [[nodiscard]] constexpr bool ok() const;
231 constexpr Status status() const;
232 constexpr Status last_status() const;
233 constexpr void clear();
234 constexpr void clear_status();
235
244 template <typename T>
245 constexpr JsonObject& Add(std::string_view key, const T& value);
246
247 template <typename T>
248 constexpr JsonObject& Add(std::nullptr_t, const T& value) = delete;
249
250 constexpr NestedJsonArray AddNestedArray(std::string_view key);
251
252 constexpr NestedJsonObject AddNestedObject(std::string_view key);
253
254 private:
255 friend class JsonBuilder;
256
257 constexpr JsonObject() = default;
258};
259
263class JsonBuilder : private JsonValue, private JsonArray, private JsonObject {
264 public:
266 static constexpr size_t MinBufferSize() { return 5; }
267
269 constexpr JsonBuilder(span<char> buffer)
270 : JsonBuilder(buffer.data(), buffer.size()) {}
271
273 constexpr JsonBuilder(char* buffer, size_t buffer_size)
274 : JsonBuilder(buffer, buffer_size, Uninitialized{}) {
275 PW_ASSERT(buffer_size >= MinBufferSize()); // Must be at least 5 characters
276 MakeNull();
277 }
278
280 [[nodiscard]] constexpr bool IsValue() const {
281 return !IsObject() && !IsArray();
282 }
283
285 [[nodiscard]] constexpr bool IsArray() const { return buffer_[0] == '['; }
286
288 [[nodiscard]] constexpr bool IsObject() const { return buffer_[0] == '{'; }
289
291 constexpr operator std::string_view() const { return {data(), size()}; }
292
294 constexpr const char* data() const { return buffer_; }
295
297 constexpr size_t size() const { return json_size_; }
298
300 constexpr size_t max_size() const { return max_size_; }
301
303 [[nodiscard]] constexpr bool ok() const { return status().ok(); }
304
313 constexpr Status status() const { return static_cast<Status::Code>(status_); }
314
317 constexpr Status last_status() const {
318 return static_cast<Status::Code>(last_status_);
319 }
320
322 constexpr void clear() { JsonValueClear(); }
323
325 constexpr void clear_status() { set_statuses(OkStatus()); }
326
328 template <typename T>
329 constexpr Status SetValue(const T& value);
330
333 [[nodiscard]] constexpr JsonValue& StartValue() {
334 JsonValueClear();
335 return *this;
336 }
337
347 [[nodiscard]] constexpr JsonArray& StartArray() {
348 JsonArrayClear();
349 return *this;
350 }
351
362 [[nodiscard]] constexpr JsonObject& StartObject() {
363 JsonObjectClear();
364 return *this;
365 }
366
367 protected:
368 enum class Uninitialized {};
369
370 // Constructor that doesn't initialize the buffer.
371 constexpr JsonBuilder(char* buffer, size_t buffer_size, Uninitialized)
372 : buffer_(buffer),
373 max_size_(buffer_size - 1),
374 json_size_(0),
375 status_(OkStatus().code()),
376 last_status_(OkStatus().code()) {}
377
378 // Sets the buffer to the null value.
379 constexpr void MakeNull() {
380 buffer_[0] = 'n';
381 buffer_[1] = 'u';
382 buffer_[2] = 'l';
383 buffer_[3] = 'l';
384 buffer_[4] = '\0';
385 json_size_ = 4;
386 }
387
388 constexpr void set_json_size(size_t json_size) { json_size_ = json_size; }
389
390 constexpr void set_statuses(Status status, Status last_status) {
391 status_ = status.code();
392 last_status_ = last_status.code();
393 }
394
395 private:
396 friend class JsonValue;
397 friend class JsonArray;
398 friend class JsonObject;
399
400 friend class NestedJsonArray;
401 friend class NestedJsonObject;
402
403 constexpr size_t remaining() const { return max_size() - size(); }
404
405 // Sets last_status_ and updates status_ if an error occurred.
406 constexpr void update_status(Status new_status);
407
408 constexpr void set_statuses(Status status) { set_statuses(status, status); }
409
410 constexpr void JsonValueClear() {
411 MakeNull();
412 set_statuses(OkStatus());
413 }
414
415 constexpr void JsonArrayClear() {
416 MakeEmpty('[', ']');
417 set_statuses(OkStatus());
418 }
419
420 constexpr void JsonObjectClear() {
421 MakeEmpty('{', '}');
422 set_statuses(OkStatus());
423 }
424
425 template <typename T>
426 constexpr Status JsonValueSet(const T& value);
427
428 template <typename T>
429 constexpr JsonArray& JsonArrayAppend(const T& value);
430
431 template <typename Iterator>
432 constexpr JsonArray& JsonArrayExtend(Iterator begin, Iterator end);
433
434 template <typename T>
435 constexpr JsonObject& JsonObjectAdd(std::string_view key, const T& value);
436
437 [[nodiscard]] constexpr bool JsonArrayAddElement();
438
439 // Adds the key if there's room for the key and at least one more character.
440 [[nodiscard]] constexpr bool JsonObjectAddKey(std::string_view key);
441
442 constexpr size_t NestedJsonOffset(const json_impl::Nesting& nesting) const {
443 // Point to the start of the nested JSON array or object. This will be three
444 // characters, plus one for each prior layer of nesting {..., "": []}.
445 return json_size_ - 3 - nesting.depth();
446 }
447
448 constexpr json_impl::Nesting::Type type() const {
449 return IsArray() ? json_impl::Nesting::kArray : json_impl::Nesting::kObject;
450 }
451
452 constexpr json_impl::NestedJson JsonArrayAppendNested(
453 const char (&open_close)[2], const json_impl::Nesting& nesting);
454
455 constexpr json_impl::NestedJson JsonObjectAddNested(
456 std::string_view key,
457 const char (&open_close)[2],
458 const json_impl::Nesting& nesting);
459
460 // Nesting works by shrinking the JsonBuilder to be just the nested structure,
461 // then expanding back out when done adding items.
462 constexpr void AddNestedStart(const json_impl::Nesting& nesting);
463 constexpr void AddNestedFinish(const json_impl::Nesting& nesting);
464
465 template <typename T>
466 constexpr void NestedJsonArrayAppend(const T& value,
467 const json_impl::Nesting& nesting);
468
469 template <typename T>
470 constexpr void NestedJsonObjectAdd(std::string_view key,
471 const T& value,
472 const json_impl::Nesting& nesting);
473
474 // For a single JSON value, checks if writing succeeded and clears on failure.
475 constexpr Status HandleSet(StatusWithSize written);
476
477 // For a value added to an array or object, checks if writing the characters
478 // succeeds, sets the status, and terminates the buffer as appropriate.
479 constexpr void HandleAdd(StatusWithSize written,
480 size_t starting_size,
481 char terminator);
482
483 constexpr void MakeEmpty(char open, char close) {
484 buffer_[0] = open;
485 buffer_[1] = close;
486 buffer_[2] = '\0';
487 json_size_ = 2;
488 }
489
490 // TODO: b/326097937 - Use StringBuilder here.
491 char* buffer_;
492 size_t max_size_; // max size of the JSON string, excluding the '\0'
493 size_t json_size_;
494
495 // If any errors have occurred, status_ stores the most recent error Status.
496 // last_status_ stores the status from the most recent operation.
497 uint8_t status_;
498 uint8_t last_status_;
499};
500
503template <size_t kMaxSize>
504class JsonBuffer final : public JsonBuilder {
505 public:
506 // Constructs a JsonBuffer with the value null.
507 constexpr JsonBuffer()
508 : JsonBuilder(static_buffer_, sizeof(static_buffer_), Uninitialized{}),
509 static_buffer_{} {
510 MakeNull();
511 }
512
513 template <typename T>
514 static constexpr JsonBuffer Value(const T& initial_value) {
516 PW_ASSERT(json.SetValue(initial_value).ok()); // Failed serialization.
517 return json;
518 }
519
520 // JsonBuffers may be copied or assigned, as long as the source buffer is not
521 // larger than this buffer.
522 constexpr JsonBuffer(const JsonBuffer& other) : JsonBuffer() {
523 CopyFrom(other);
524 }
525
526 template <size_t kOtherSize>
527 constexpr JsonBuffer(const JsonBuffer<kOtherSize>& other) : JsonBuffer() {
528 CopyFrom(other);
529 }
530
531 constexpr JsonBuffer& operator=(const JsonBuffer& rhs) {
532 CopyFrom(rhs);
533 return *this;
534 }
535
536 template <size_t kOtherSize>
537 constexpr JsonBuffer& operator=(const JsonBuffer<kOtherSize>& rhs) {
538 CopyFrom(rhs);
539 return *this;
540 }
541
542 static constexpr size_t max_size() { return kMaxSize; }
543
544 private:
545 static_assert(kMaxSize + 1 /* null */ >= JsonBuilder::MinBufferSize(),
546 "JsonBuffers require at least 4 bytes");
547
548 template <size_t kOtherSize>
549 constexpr void CopyFrom(const JsonBuffer<kOtherSize>& other) {
550 static_assert(kOtherSize <= kMaxSize,
551 "A JsonBuffer cannot be copied into a smaller buffer");
552 CopyFrom(static_cast<const JsonBuilder&>(other));
553 }
554
555 constexpr void CopyFrom(const JsonBuilder& other) {
556 for (size_t i = 0; i < other.size() + 1 /* include null */; ++i) {
557 static_buffer_[i] = other.data()[i];
558 }
559 JsonBuilder::set_json_size(other.size());
560 JsonBuilder::set_statuses(other.status(), other.last_status());
561 }
562
563 char static_buffer_[kMaxSize + 1];
564};
566
567// Implementation details
568namespace json_impl {
569
570inline constexpr char kArray[2] = {'[', ']'};
571inline constexpr char kObject[2] = {'{', '}'};
572
573constexpr StatusWithSize WriteString(std::string_view value,
574 char* buffer,
575 size_t remaining) {
576 if (value.size() + 1 /* null */ > remaining) {
577 return StatusWithSize::ResourceExhausted();
578 }
579 for (char c : value) {
580 *buffer++ = c;
581 }
582 *buffer = '\0';
583 return StatusWithSize(value.size());
584}
585
586constexpr char NibbleToHex(uint8_t nibble) {
587 return nibble + (nibble < 10 ? '0' : ('a' - 10));
588}
589
590// In accordance with RFC 8259, JSON strings must escape control characters,
591// quotation marks, and reverse solidus (\‍). This function copies a string and
592// escapes these characters as shown in http://www.json.org/string.gif.
593//
594// The return value is the number of bytes written to the destination
595// buffer or -1 if the string does not fit in the destination buffer. Since
596// escaped characters result in two or six bytes in the output, the return value
597// won't necessarily equal the number of bytes read from the source buffer.
598//
599// The destination buffer is NEVER null-terminated!
600//
601// Currently this function ONLY supports ASCII! Bytes ≥128 will be escaped
602// individually, rather than treated as multibyte Unicode characters.
603constexpr int EscapedStringCopy(char* destination,
604 int copy_limit,
605 std::string_view source) {
606 int destination_index = 0;
607
608 for (char source_char : source) {
609 if (destination_index >= copy_limit) {
610 return -1;
611 }
612
613 char escaped_character = '\0';
614
615 if (source_char >= '\b' && source_char <= '\r' && source_char != '\v') {
616 constexpr char kControlChars[] = {'b', 't', 'n', '?', 'f', 'r'};
617 escaped_character = kControlChars[source_char - '\b'];
618 } else if (source_char == '"' || source_char == '\\') {
619 escaped_character = source_char;
620 } else if (source_char >= ' ' && source_char <= '~') {
621 // This is a printable character; no escaping is needed.
622 destination[destination_index++] = source_char;
623 continue; // Skip the escaping step below.
624 } else {
625 // Escape control characters that haven't already been handled. These take
626 // six bytes to encode (e.g. \u0056).
627 if (copy_limit - destination_index < 6) {
628 return -1;
629 }
630
631 destination[destination_index++] = '\\';
632 destination[destination_index++] = 'u';
633 destination[destination_index++] = '0'; // Only handle ASCII for now
634 destination[destination_index++] = '0';
635 destination[destination_index++] = NibbleToHex((source_char >> 4) & 0x0f);
636 destination[destination_index++] = NibbleToHex(source_char & 0x0f);
637 continue; // Already escaped; skip the single-character escaping step.
638 }
639
640 // Escape the \b, \t, \n, \f, \r, \", or \\ character, if it fits.
641 if (copy_limit - destination_index < 2) {
642 return -1;
643 }
644
645 destination[destination_index++] = '\\';
646 destination[destination_index++] = escaped_character;
647 }
648 return destination_index;
649}
650
651// Writes "<value>", escaping special characters; returns true if successful.
652// Null terminates ONLY if successful.
653constexpr StatusWithSize WriteQuotedString(std::string_view value,
654 char* buffer,
655 size_t buffer_size) {
656 constexpr size_t kOverhead = 2 /* quotes */ + 1 /* null */;
657 if (value.size() + kOverhead > buffer_size) {
658 return StatusWithSize::ResourceExhausted();
659 }
660 // If the string might fit, try to copy it. May still run out of room due to
661 // escaping.
662 const int written =
663 EscapedStringCopy(buffer + 1 /* quote */, buffer_size - kOverhead, value);
664 if (written < 0) {
665 return StatusWithSize::ResourceExhausted();
666 }
667
668 buffer[0] = '"'; // open quote
669 buffer[written + 1] = '"'; // close quote
670 buffer[written + 2] = '\0';
671 return StatusWithSize(written + 2); // characters written excludes \0
672}
673
674constexpr StatusWithSize WriteCharPointer(const char* ptr,
675 char* buffer,
676 size_t buffer_size) {
677 if (ptr == nullptr) {
678 return WriteString("null", buffer, buffer_size);
679 }
680 return WriteQuotedString(ptr, buffer, buffer_size);
681}
682
683template <typename T>
684inline constexpr bool kIsJson =
685 std::is_base_of_v<JsonValue, T> || std::is_base_of_v<JsonArray, T> ||
686 std::is_base_of_v<JsonObject, T>;
687
688template <typename T>
689struct InvalidJsonType : std::false_type {};
690
692 const char (&open_close)[2];
693};
694
695template <typename T>
696constexpr StatusWithSize SerializeJson(const T& value,
697 char* buffer,
698 size_t remaining) {
699 if constexpr (kIsJson<T>) { // nested JsonBuilder, JsonArray, JsonObject
700 return WriteString(value, buffer, remaining);
701 } else if constexpr (std::is_same_v<T, LiteralChars>) { // Nested append/add
702 return WriteString(
703 std::string_view(value.open_close, 2), buffer, remaining);
704 } else if constexpr (std::is_null_pointer_v<T> || // nullptr & C strings
705 std::is_same_v<T, char*> ||
706 std::is_same_v<T, const char*>) {
707 return WriteCharPointer(value, buffer, remaining);
708 } else if constexpr (std::is_convertible_v<T, std::string_view>) { // strings
709 return WriteQuotedString(value, buffer, remaining);
710 } else if constexpr (std::is_floating_point_v<T>) {
711 return string::FloatAsIntToString(value, {buffer, remaining});
712 } else if constexpr (std::is_same_v<T, bool>) { // boolean
713 return WriteString(value ? "true" : "false", buffer, remaining);
714 } else if constexpr (std::is_integral_v<T>) { // integers
715 return string::IntToString(value, {buffer, remaining});
716 } else {
717 static_assert(InvalidJsonType<T>(),
718 "JSON values may only be numbers, strings, JSON arrays, JSON "
719 "objects, or null");
720 return StatusWithSize::Internal();
721 }
722}
723
724} // namespace json_impl
725
726// Forward NestedJsonArray, NestedJsonObject, JsonArray, and JsonObject
727// function calls to JsonBuilder.
728
729#define PW_JSON_COMMON_INTERFACE_IMPL(name) \
730 constexpr bool name::IsValue() const { \
731 return static_cast<const JsonBuilder*>(this)->IsValue(); \
732 } \
733 constexpr bool name::IsArray() const { \
734 return static_cast<const JsonBuilder*>(this)->IsArray(); \
735 } \
736 constexpr bool name::IsObject() const { \
737 return static_cast<const JsonBuilder*>(this)->IsObject(); \
738 } \
739 constexpr name::operator std::string_view() const { \
740 return static_cast<const JsonBuilder*>(this)->operator std::string_view(); \
741 } \
742 constexpr const char* name::data() const { \
743 return static_cast<const JsonBuilder*>(this)->data(); \
744 } \
745 constexpr size_t name::size() const { \
746 return static_cast<const JsonBuilder*>(this)->size(); \
747 } \
748 constexpr size_t name::max_size() const { \
749 return static_cast<const JsonBuilder*>(this)->max_size(); \
750 } \
751 constexpr bool name::ok() const { \
752 return static_cast<const JsonBuilder*>(this)->ok(); \
753 } \
754 constexpr Status name::status() const { \
755 return static_cast<const JsonBuilder*>(this)->status(); \
756 } \
757 constexpr Status name::last_status() const { \
758 return static_cast<const JsonBuilder*>(this)->last_status(); \
759 } \
760 constexpr void name::clear() { \
761 static_cast<JsonBuilder*>(this)->name##Clear(); \
762 } \
763 constexpr void name::clear_status() { \
764 static_cast<JsonBuilder*>(this)->clear_status(); \
765 } \
766 static_assert(true)
767
768PW_JSON_COMMON_INTERFACE_IMPL(JsonValue);
769PW_JSON_COMMON_INTERFACE_IMPL(JsonArray);
770PW_JSON_COMMON_INTERFACE_IMPL(JsonObject);
771
772#undef PW_JSON_COMMON_INTERFACE_IMPL
773
774template <typename T>
775constexpr NestedJsonArray& NestedJsonArray::Append(const T& value) {
776 json_.builder().NestedJsonArrayAppend(value, json_.nesting());
777 return *this;
778}
780 return json_.builder().JsonArrayAppendNested(json_impl::kArray,
781 json_.nesting());
782}
784 return json_.builder().JsonArrayAppendNested(json_impl::kObject,
785 json_.nesting());
786}
788 std::string_view key) {
789 return json_.builder().JsonObjectAddNested(
790 key, json_impl::kArray, json_.nesting());
791}
793 std::string_view key) {
794 return json_.builder().JsonObjectAddNested(
795 key, json_impl::kObject, json_.nesting());
796}
797template <typename T>
798constexpr NestedJsonObject& NestedJsonObject::Add(std::string_view key,
799 const T& value) {
800 json_.builder().NestedJsonObjectAdd(key, value, json_.nesting());
801 return *this;
802}
803template <typename T>
804constexpr Status JsonValue::Set(const T& value) {
805 return static_cast<JsonBuilder*>(this)->JsonValueSet(value);
806}
807template <typename T>
808constexpr JsonArray& JsonArray::Append(const T& value) {
809 return static_cast<JsonBuilder*>(this)->JsonArrayAppend(value);
810}
812 return static_cast<JsonBuilder*>(this)->JsonArrayAppendNested(
813 json_impl::kArray, {});
814}
816 return static_cast<JsonBuilder*>(this)->JsonArrayAppendNested(
817 json_impl::kObject, {});
818}
819template <typename Iterable>
820constexpr JsonArray& JsonArray::Extend(const Iterable& iterable) {
821 return static_cast<JsonBuilder*>(this)->JsonArrayExtend(std::cbegin(iterable),
822 std::cend(iterable));
823}
824template <typename T, size_t kSize>
825constexpr JsonArray& JsonArray::Extend(const T (&iterable)[kSize]) {
826 return static_cast<JsonBuilder*>(this)->JsonArrayExtend(std::cbegin(iterable),
827 std::cend(iterable));
828}
829template <typename T>
830constexpr JsonObject& JsonObject::Add(std::string_view key, const T& value) {
831 return static_cast<JsonBuilder*>(this)->JsonObjectAdd(key, value);
832}
833constexpr NestedJsonArray JsonObject::AddNestedArray(std::string_view key) {
834 return static_cast<JsonBuilder*>(this)->JsonObjectAddNested(
835 key, json_impl::kArray, {});
836}
837constexpr NestedJsonObject JsonObject::AddNestedObject(std::string_view key) {
838 return static_cast<JsonBuilder*>(this)->JsonObjectAddNested(
839 key, json_impl::kObject, {});
840}
841
842// JsonBuilder function implementations.
843
844template <typename T>
845constexpr Status JsonBuilder::SetValue(const T& value) {
846 return HandleSet(json_impl::SerializeJson(value, buffer_, max_size_ + 1));
847}
848
849constexpr void JsonBuilder::update_status(Status new_status) {
850 last_status_ = new_status.code();
851 if (!new_status.ok() && status().ok()) {
852 status_ = new_status.code();
853 }
854}
855
856template <typename T>
857constexpr Status JsonBuilder::JsonValueSet(const T& value) {
858 if constexpr (json_impl::kIsJson<T>) {
859 PW_ASSERT(this != &value); // Self-nesting is disallowed.
860 PW_ASSERT(value.IsValue()); // Cannot set JsonValue to an array or object.
861 }
862 return SetValue(value);
863}
864
865template <typename T>
866constexpr JsonArray& JsonBuilder::JsonArrayAppend(const T& value) {
867 if constexpr (json_impl::kIsJson<T>) {
868 PW_ASSERT(this != &value); // Self-nesting is disallowed.
869 }
870
871 const size_t starting_size = size();
872 if (JsonArrayAddElement()) {
873 // The buffer size is remaining() + 1 bytes, but drop the + 1 to leave room
874 // for the closing ].
875 HandleAdd(json_impl::SerializeJson(value, &buffer_[size()], remaining()),
876 starting_size,
877 ']');
878 }
879 return *this;
880}
881
882template <typename Iterator>
883constexpr JsonArray& JsonBuilder::JsonArrayExtend(Iterator begin,
884 Iterator end) {
885 const size_t starting_size = size();
886
887 for (Iterator cur = begin; cur != end; ++cur) {
888 const Status status = Append(*cur).last_status();
889 if (!status.ok()) { // Undo changes if there is an issue.
890 json_size_ = starting_size;
891 buffer_[size() - 1] = ']';
892 buffer_[size()] = '\0';
893 break;
894 }
895 }
896 return *this;
897}
898
899template <typename T>
900constexpr JsonObject& JsonBuilder::JsonObjectAdd(std::string_view key,
901 const T& value) {
902 if constexpr (json_impl::kIsJson<T>) {
903 PW_ASSERT(this != &value); // Self-nesting is disallowed.
904 }
905
906 const size_t starting_size = size();
907 if (JsonObjectAddKey(key)) {
908 // The buffer size is remaining() + 1 bytes, but drop the + 1 to leave room
909 // for the closing }.
910 HandleAdd(json_impl::SerializeJson(value, &buffer_[size()], remaining()),
911 starting_size,
912 '}');
913 }
914 return *this;
915}
916
917constexpr bool JsonBuilder::JsonArrayAddElement() {
918 PW_ASSERT(IsArray()); // Attempted to append to an object or value
919
920 // Needs space for at least 3 new characters (, 1)
921 if (size() + 3 > max_size()) {
922 update_status(Status::ResourceExhausted());
923 return false;
924 }
925
926 // If this is the first element, just drop the ]. Otherwise, add a comma.
927 if (size() == 2) {
928 json_size_ = 1;
929 } else {
930 buffer_[json_size_ - 1] = ',';
931 buffer_[json_size_++] = ' ';
932 }
933
934 return true;
935}
936
937constexpr bool JsonBuilder::JsonObjectAddKey(std::string_view key) {
938 PW_ASSERT(IsObject()); // Attempted add a key-value pair to an array or value
939
940 // Each key needs 7 more characters: (, "": ) plus at least 1 for the value.
941 // The ',' replaces the terminal '}', but a new '}' is placed at the end, so
942 // the total remains 7. The first key could get away with 5, but oh well.
943 if (size() + key.size() + 7 > max_size()) {
944 update_status(Status::ResourceExhausted());
945 return false;
946 }
947
948 // If this is the first key, just drop the }. Otherwise, add a comma.
949 if (size() == 2) {
950 json_size_ = 1;
951 } else {
952 buffer_[json_size_ - 1] = ','; // change the last } to ,
953 buffer_[json_size_++] = ' ';
954 }
955
956 // Buffer size is remaining() + 1, but - 4 for at least ": 0}" after the key.
957 auto written =
958 json_impl::WriteQuotedString(key, &buffer_[json_size_], remaining() - 3);
959 if (!written.ok()) {
960 return false;
961 }
962
963 json_size_ += written.size(); // Now have {"key" or {..., "key"
964 buffer_[json_size_++] = ':';
965 buffer_[json_size_++] = ' ';
966 return true;
967}
968
969constexpr json_impl::NestedJson JsonBuilder::JsonArrayAppendNested(
970 const char (&open_close)[2], const json_impl::Nesting& nesting) {
971 AddNestedStart(nesting);
972 json_impl::Nesting::Type nesting_within = type();
973 JsonArrayAppend(json_impl::LiteralChars{open_close}); // [..., {}]
974 AddNestedFinish(nesting);
975 return json_impl::NestedJson(
976 *this,
977 last_status().ok()
978 ? nesting.Nest(NestedJsonOffset(nesting), nesting_within)
979 : json_impl::Nesting());
980}
981
982constexpr json_impl::NestedJson JsonBuilder::JsonObjectAddNested(
983 std::string_view key,
984 const char (&open_close)[2],
985 const json_impl::Nesting& nesting) {
986 AddNestedStart(nesting);
987 json_impl::Nesting::Type nesting_within = type();
988 JsonObjectAdd(key, json_impl::LiteralChars{open_close}); // {..., "key": {}}
989 AddNestedFinish(nesting);
990 return json_impl::NestedJson(
991 *this,
992 last_status().ok()
993 ? nesting.Nest(NestedJsonOffset(nesting), nesting_within)
994 : json_impl::Nesting());
995}
996
997constexpr void JsonBuilder::AddNestedStart(const json_impl::Nesting& nesting) {
998 // A nested structure must be the last thing in the JSON. Back up to where the
999 // first of the closing ] or } should be, and check from there.
1000 PW_ASSERT( // JSON must not have been cleared since nesting.
1001 json_size_ >= nesting.offset() + nesting.depth() + 2 /* [] or {} */);
1002 PW_ASSERT( // Nested structure must match the expected type
1003 buffer_[json_size_ - nesting.depth() - 1] ==
1004 buffer_[nesting.offset()] + 2 /* convert [ to ] or { to } */);
1005 nesting.CheckNesting(&buffer_[json_size_ - nesting.depth()]);
1006
1007 buffer_ += nesting.offset();
1008 json_size_ -= nesting.offset() + nesting.depth();
1009 max_size_ -= nesting.offset() + nesting.depth();
1010}
1011
1012constexpr void JsonBuilder::AddNestedFinish(const json_impl::Nesting& nesting) {
1013 buffer_ -= nesting.offset();
1014 max_size_ += nesting.offset() + nesting.depth();
1015
1016 json_size_ += nesting.offset();
1017 nesting.Terminate(&buffer_[json_size_]);
1018 json_size_ += nesting.depth();
1019}
1020
1021template <typename T>
1022constexpr void JsonBuilder::NestedJsonArrayAppend(
1023 const T& value, const json_impl::Nesting& nesting) {
1024 AddNestedStart(nesting);
1025 JsonArrayAppend(value);
1026 AddNestedFinish(nesting);
1027}
1028
1029template <typename T>
1030constexpr void JsonBuilder::NestedJsonObjectAdd(
1031 std::string_view key, const T& value, const json_impl::Nesting& nesting) {
1032 AddNestedStart(nesting);
1033 JsonObjectAdd(key, value);
1034 AddNestedFinish(nesting);
1035}
1036
1037constexpr Status JsonBuilder::HandleSet(StatusWithSize written) {
1038 if (written.ok()) {
1039 json_size_ = written.size();
1040 } else {
1041 MakeNull();
1042 }
1043 set_statuses(written.status()); // status is always reset when setting value
1044 return last_status();
1045}
1046
1047constexpr void JsonBuilder::HandleAdd(StatusWithSize written,
1048 size_t starting_size,
1049 char terminator) {
1050 update_status(written.status()); // save room for } or ]
1051 if (last_status().ok()) {
1052 json_size_ += written.size();
1053 } else {
1054 json_size_ = starting_size - 1; // make room for closing character
1055 }
1056
1057 buffer_[json_size_++] = terminator;
1058 buffer_[json_size_] = '\0';
1059}
1060
1061} // namespace pw
Definition: builder.h:162
Definition: builder.h:504
Definition: builder.h:263
Definition: builder.h:215
Definition: builder.h:119
Definition: builder.h:53
Definition: builder.h:87
Definition: status.h:120
constexpr Code code() const
Definition: status.h:341
constexpr bool ok() const
Definition: status.h:346
static constexpr Status ResourceExhausted()
Definition: status.h:230
Definition: status_with_size.h:51
Definition: span_impl.h:235
constexpr Status status() const
Definition: builder.h:313
constexpr bool IsArray() const
True if the top-level JSON entity is an array.
Definition: builder.h:285
constexpr JsonBuilder(char *buffer, size_t buffer_size)
Initializes to the value null. buffer_size must be at least 5.
Definition: builder.h:273
constexpr JsonObject & StartObject()
Definition: builder.h:362
constexpr size_t max_size() const
The maximum size of the JSON string, excluding the null terminator.
Definition: builder.h:300
constexpr bool IsValue() const
True if the top-level JSON entity is a simple value (not array or object).
Definition: builder.h:280
constexpr Status last_status() const
Definition: builder.h:317
constexpr NestedJsonArray AddNestedArray(std::string_view key)
Adds a nested array to the nested object.
Definition: builder.h:787
constexpr JsonBuilder(span< char > buffer)
Initializes to the value null. buffer.size() must be at least 5.
Definition: builder.h:269
constexpr NestedJsonObject AppendNestedObject()
Appends a new nested object to this nested array.
Definition: builder.h:783
constexpr const char * data() const
Pointer to the serialized JSON, which is always a null-terminated string.
Definition: builder.h:294
constexpr JsonArray & Append(const T &value)
Definition: builder.h:808
constexpr Status Set(const T &value)
Definition: builder.h:804
constexpr NestedJsonArray AppendNestedArray()
Appends a new nested array to this nested array.
Definition: builder.h:779
constexpr Status SetValue(const T &value)
Clears the JSON and sets it to a single JSON value (see JsonValue::Set).
Definition: builder.h:845
constexpr NestedJsonArray & Append(const T &value)
Appends to the nested array.
Definition: builder.h:775
constexpr size_t size() const
The current size of the JSON string, excluding the null terminator.
Definition: builder.h:297
constexpr bool ok() const
True if.
Definition: builder.h:303
constexpr void clear()
Sets the JSON null and clears the status.
Definition: builder.h:322
constexpr JsonArray & Extend(const Iterable &iterable)
Definition: builder.h:820
constexpr NestedJsonArray AppendNestedArray()
Appends a nested array to this array.
Definition: builder.h:811
constexpr JsonArray & StartArray()
Definition: builder.h:347
constexpr NestedJsonObject AppendNestedObject()
Appends a nested object to this array.
Definition: builder.h:815
constexpr NestedJsonObject & Add(std::string_view key, const T &value)
Adds a key-value pair to the nested object.
Definition: builder.h:798
constexpr NestedJsonObject AddNestedObject(std::string_view key)
Adds a nested object to the nested object.
Definition: builder.h:792
constexpr void clear_status()
Resets status() and last_status().
Definition: builder.h:325
constexpr JsonObject & Add(std::string_view key, const T &value)
Definition: builder.h:830
static constexpr size_t MinBufferSize()
JsonBuilder requires at least 5 characters in its buffer.
Definition: builder.h:266
constexpr JsonValue & StartValue()
Definition: builder.h:333
constexpr bool IsObject() const
True if the top-level JSON entity is an object.
Definition: builder.h:288
pw_Status
C API for status codes. In C++, use the pw::Status class instead.
Definition: status.h:40
constexpr Status OkStatus()
Definition: status.h:450
The Pigweed namespace.
Definition: alignment.h:27
Definition: builder.h:689
Definition: builder.h:691