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