Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2020-2021 Project CHIP Authors
4 : *
5 : * Licensed under the Apache License, Version 2.0 (the "License");
6 : * you may not use this file except in compliance with the License.
7 : * You may obtain a copy of the License at
8 : *
9 : * http://www.apache.org/licenses/LICENSE-2.0
10 : *
11 : * Unless required by applicable law or agreed to in writing, software
12 : * distributed under the License is distributed on an "AS IS" BASIS,
13 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 : * See the License for the specific language governing permissions and
15 : * limitations under the License.
16 : */
17 :
18 : #pragma once
19 :
20 : #include <cctype>
21 : #include <nlassert.h>
22 :
23 : #include "BufferWriter.h"
24 :
25 : namespace chip {
26 :
27 : /// Build a c-style string out of distinct parts
28 : class StringBuilderBase
29 : {
30 : public:
31 921 : StringBuilderBase(char * buffer, size_t size) : mWriter(reinterpret_cast<uint8_t *>(buffer), size - 1)
32 : {
33 : nlASSERT(size > 0);
34 921 : buffer[0] = 0; // make c-str work by default
35 921 : }
36 :
37 : /// Append a null terminated string
38 2215 : StringBuilderBase & Add(const char * s)
39 : {
40 2215 : mWriter.Put(s);
41 2215 : NullTerminate();
42 2215 : return *this;
43 : }
44 :
45 : /// Append an integer value
46 3 : StringBuilderBase & Add(int value)
47 : {
48 : char buff[32];
49 3 : snprintf(buff, sizeof(buff), "%d", value);
50 3 : buff[sizeof(buff) - 1] = 0;
51 3 : return Add(buff);
52 : }
53 :
54 : /// Append an uint8_t value
55 : StringBuilderBase & Add(uint8_t value)
56 : {
57 : uint8_t actual = std::isprint(value) ? value : '.';
58 : mWriter.Put(actual);
59 : NullTerminate();
60 : return *this;
61 : }
62 :
63 : /// Append a memory block
64 91 : StringBuilderBase & Add(const void * data, size_t size)
65 : {
66 91 : mWriter.Put(data, size);
67 91 : NullTerminate();
68 91 : return *this;
69 : }
70 :
71 : /// Did all the values fit?
72 214 : bool Fit() const { return mWriter.Fit(); }
73 :
74 : /// Was nothing written yet?
75 : bool Empty() const { return mWriter.Needed() == 0; }
76 :
77 : /// Write a formatted string to the stringbuilder
78 : StringBuilderBase & AddFormat(const char * format, ...) ENFORCE_FORMAT(2, 3);
79 :
80 : /// For strings we often want to know when they were truncated. If the underlying writer did
81 : /// not fit, this replaces the last 3 characters with "."
82 : StringBuilderBase & AddMarkerIfOverflow();
83 :
84 383 : StringBuilderBase & Reset()
85 : {
86 383 : mWriter.Reset();
87 383 : NullTerminate();
88 383 : return *this;
89 : }
90 :
91 : /// access the underlying value
92 1270 : const char * c_str() const { return reinterpret_cast<const char *>(mWriter.Buffer()); }
93 :
94 : protected:
95 : /// Number of bytes actually needed
96 75 : size_t Needed() const { return mWriter.Needed(); }
97 :
98 : /// Size of the output buffer
99 16 : size_t Size() const { return mWriter.Size(); }
100 :
101 : private:
102 : Encoding::BufferWriter mWriter;
103 :
104 5718 : void NullTerminate()
105 : {
106 5718 : if (mWriter.Fit())
107 : {
108 5683 : mWriter.Buffer()[mWriter.Needed()] = 0;
109 : }
110 : else
111 : {
112 35 : mWriter.Buffer()[mWriter.Size()] = 0;
113 : }
114 5718 : }
115 : };
116 :
117 : /// A preallocated sized string builder
118 : template <size_t kSize>
119 : class StringBuilder : public StringBuilderBase
120 : {
121 : public:
122 921 : StringBuilder() : StringBuilderBase(mBuffer, kSize) {}
123 :
124 79 : StringBuilder(const char * data, size_t size, bool add_marker_if_overflow = true) : StringBuilder()
125 : {
126 79 : Add(data, size);
127 :
128 79 : if (add_marker_if_overflow)
129 : {
130 75 : AddMarkerIfOverflow();
131 : }
132 :
133 79 : size_t length = Fit() ? Needed() : Size();
134 3238 : for (size_t i = 0; i < length; ++i)
135 : {
136 3159 : if (mBuffer[i] == '\0')
137 : {
138 18 : mBuffer[i] = '.';
139 : }
140 : }
141 79 : }
142 :
143 7 : StringBuilder(const CharSpan & span, bool add_marker_if_overflow = true) :
144 7 : StringBuilder(span.data(), span.size(), add_marker_if_overflow)
145 7 : {}
146 :
147 12 : StringBuilder(const uint8_t * data, size_t size, bool add_marker_if_overflow = true) : StringBuilder()
148 : {
149 12 : Add(data, size);
150 :
151 12 : if (add_marker_if_overflow)
152 : {
153 8 : AddMarkerIfOverflow();
154 : }
155 :
156 12 : size_t length = Fit() ? Needed() : Size();
157 168 : for (size_t i = 0; i < length; ++i)
158 : {
159 156 : if (!std::isprint(mBuffer[i]))
160 : {
161 36 : mBuffer[i] = '.';
162 : }
163 : }
164 12 : }
165 :
166 6 : StringBuilder(const ByteSpan & span, bool add_marker_if_overflow = true) :
167 6 : StringBuilder(span.data(), span.size(), add_marker_if_overflow)
168 6 : {}
169 :
170 : private:
171 : char mBuffer[kSize];
172 : };
173 :
174 : /// Default buffer size is 257 to accommodate values with size up to 256
175 : /// If the buffer size is not enough the value will be truncated and an overflow marker will be added
176 : /// TODO Note that this buffer size will be used always by default even for much smaller values
177 : /// TODO Preferably the buffer size should be the one appropriate for each value
178 : using NullTerminated = StringBuilder<257>;
179 :
180 : } // namespace chip
|