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