xref: /aosp_15_r20/external/pigweed/pw_json/public/pw_json/internal/nesting.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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 
16 #include <cstdint>
17 
18 #include "pw_assert/assert.h"
19 #include "pw_status/status.h"
20 
21 namespace pw {
22 
23 class JsonBuilder;
24 class NestedJsonObject;
25 
26 namespace json_impl {
27 
28 // Tracks how deeply nested an array or object is and the types of the
29 // structures it is nested in.
30 class Nesting {
31  public:
32   enum Type : uint16_t { kArray = 0, kObject = 1 };
33 
Nesting()34   constexpr Nesting() : json_offset_(0), depth_(0), types_(0) {}
35 
36   constexpr Nesting(const Nesting&) = default;
37   constexpr Nesting& operator=(const Nesting&) = default;
38 
Nest(size_t start,Type type)39   constexpr Nesting Nest(size_t start, Type type) const {
40     PW_ASSERT(depth_ < 16);  // Arrays or objects may be nested at most 17 times
41     return Nesting(start, depth_ + 1, (types_ << 1) | type);
42   }
43 
44   // The start of this structure in the original buffer.
offset()45   constexpr size_t offset() const { return json_offset_; }
46 
47   // Number of layers this array or object is nested within.
depth()48   constexpr size_t depth() const { return depth_; }
49 
CheckNesting(char * json)50   constexpr void CheckNesting(char* json) const {
51     PW_ASSERT(json[depth_] == '\0');  // Enclosing JSON has changed.
52     for (uint16_t i = 0; i < depth_; ++i) {
53       PW_ASSERT(json[i] == close(i));  // Enclosing JSON has changed.
54     }
55   }
56 
57   // Writes closing ] or } and the final \0.
Terminate(char * buffer)58   constexpr void Terminate(char* buffer) const {
59     for (uint16_t i = 0; i < depth_; ++i) {
60       buffer[i] = close(i);
61     }
62     buffer[depth_] = '\0';
63   }
64 
65  private:
Nesting(size_t offset,uint16_t depth,uint16_t types)66   constexpr Nesting(size_t offset, uint16_t depth, uint16_t types)
67       : json_offset_(offset), depth_(depth), types_(types) {}
68 
close(uint16_t i)69   constexpr char close(uint16_t i) const {
70     return (types_ & (1 << i)) == 0 ? ']' : '}';
71   }
72 
73   size_t json_offset_;
74   uint16_t depth_;  // Depth only counts nested structures; [] is 0, [{}] is 1
75   uint16_t types_;
76 };
77 
78 // Represents a nested array or object.
79 class NestedJson {
80  public:
81   constexpr NestedJson(const NestedJson&) = delete;
82   constexpr NestedJson& operator=(const NestedJson&) = delete;
83 
NestedJson(NestedJson && other)84   constexpr NestedJson(NestedJson&& other) : builder_(), nesting_() {
85     *this = std::move(other);
86   }
87 
88   constexpr NestedJson& operator=(NestedJson&& other) {
89     builder_ = other.builder_;
90     nesting_ = other.nesting_;
91     other.builder_ = nullptr;
92     return *this;
93   }
94 
builder()95   constexpr JsonBuilder& builder() const { return *builder_; }
nesting()96   constexpr const Nesting& nesting() const { return nesting_; }
97 
98  private:
99   friend class pw::JsonBuilder;
100 
NestedJson(JsonBuilder & builder,const Nesting & nesting)101   constexpr NestedJson(JsonBuilder& builder, const Nesting& nesting)
102       : builder_(&builder), nesting_(nesting) {}
103 
104   JsonBuilder* builder_;
105   Nesting nesting_;
106 };
107 
108 }  // namespace json_impl
109 }  // namespace pw
110