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