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 <cstdio>
22 : #include <lib/support/Assertions.h>
23 :
24 : #include "BufferWriter.h"
25 :
26 : namespace chip {
27 :
28 : /// Build a c-style string out of distinct parts
29 : class StringBuilderBase
30 : {
31 : public:
32 577 : StringBuilderBase(char * buffer, size_t size) : mWriter(reinterpret_cast<uint8_t *>(buffer), size - 1)
33 : {
34 577 : VerifyOrDieWithoutLogging(size > 0);
35 577 : buffer[0] = 0; // make c-str work by default
36 577 : }
37 :
38 : /// Append a null terminated string
39 843 : StringBuilderBase & Add(const char * s)
40 : {
41 843 : mWriter.Put(s);
42 843 : NullTerminate();
43 843 : return *this;
44 : }
45 :
46 : /// Append an integer value
47 3 : StringBuilderBase & Add(int value)
48 : {
49 : char buff[32];
50 3 : snprintf(buff, sizeof(buff), "%d", value);
51 3 : buff[sizeof(buff) - 1] = 0;
52 3 : return Add(buff);
53 : }
54 :
55 : /// Append an uint8_t value
56 : StringBuilderBase & Add(uint8_t value)
57 : {
58 : uint8_t actual = std::isprint(value) ? value : '.';
59 : mWriter.Put(actual);
60 : NullTerminate();
61 : return *this;
62 : }
63 :
64 : /// Append a memory block
65 24 : StringBuilderBase & Add(const void * data, size_t size)
66 : {
67 24 : mWriter.Put(data, size);
68 24 : NullTerminate();
69 24 : return *this;
70 : }
71 :
72 : /// Did all the values fit?
73 168 : bool Fit() const { return mWriter.Fit(); }
74 :
75 : /// Was nothing written yet?
76 : bool Empty() const { return mWriter.Needed() == 0; }
77 :
78 : /// Write a formatted string to the stringbuilder
79 : StringBuilderBase & AddFormat(const char * format, ...) ENFORCE_FORMAT(2, 3);
80 :
81 : /// For strings we often want to know when they were truncated. If the underlying writer did
82 : /// not fit, this replaces the last 3 characters with "."
83 : StringBuilderBase & AddMarkerIfOverflow();
84 :
85 384 : StringBuilderBase & Reset()
86 : {
87 384 : mWriter.Reset();
88 384 : NullTerminate();
89 384 : return *this;
90 : }
91 :
92 : /// access the underlying value
93 566 : const char * c_str() const { return reinterpret_cast<const char *>(mWriter.Buffer()); }
94 :
95 : protected:
96 : /// Number of bytes actually needed
97 8 : size_t Needed() const { return mWriter.Needed(); }
98 :
99 : /// Size of the output buffer
100 16 : size_t Size() const { return mWriter.Size(); }
101 :
102 : private:
103 : Encoding::BufferWriter mWriter;
104 :
105 4319 : void NullTerminate()
106 : {
107 4319 : if (mWriter.Fit())
108 : {
109 4280 : mWriter.Buffer()[mWriter.Needed()] = 0;
110 : }
111 : else
112 : {
113 39 : mWriter.Buffer()[mWriter.Size()] = 0;
114 : }
115 4319 : }
116 : };
117 :
118 : /// A preallocated sized string builder
119 : template <size_t kSize>
120 : class StringBuilder : public StringBuilderBase
121 : {
122 : public:
123 566 : StringBuilder() : StringBuilderBase(mBuffer, kSize) {}
124 :
125 12 : StringBuilder(const char * data, size_t size, bool add_marker_if_overflow = true) : StringBuilder()
126 : {
127 12 : Add(data, size);
128 :
129 12 : if (add_marker_if_overflow)
130 : {
131 8 : AddMarkerIfOverflow();
132 : }
133 :
134 12 : size_t length = Fit() ? Needed() : Size();
135 150 : for (size_t i = 0; i < length; ++i)
136 : {
137 138 : if (mBuffer[i] == '\0')
138 : {
139 18 : mBuffer[i] = '.';
140 : }
141 : }
142 12 : }
143 :
144 6 : StringBuilder(const CharSpan & span, bool add_marker_if_overflow = true) :
145 6 : StringBuilder(span.data(), span.size(), add_marker_if_overflow)
146 6 : {}
147 :
148 12 : StringBuilder(const uint8_t * data, size_t size, bool add_marker_if_overflow = true) : StringBuilder()
149 : {
150 12 : Add(data, size);
151 :
152 12 : if (add_marker_if_overflow)
153 : {
154 8 : AddMarkerIfOverflow();
155 : }
156 :
157 12 : size_t length = Fit() ? Needed() : Size();
158 168 : for (size_t i = 0; i < length; ++i)
159 : {
160 156 : if (!std::isprint(mBuffer[i]))
161 : {
162 36 : mBuffer[i] = '.';
163 : }
164 : }
165 12 : }
166 :
167 6 : StringBuilder(const ByteSpan & span, bool add_marker_if_overflow = true) :
168 6 : StringBuilder(span.data(), span.size(), add_marker_if_overflow)
169 6 : {}
170 :
171 : private:
172 : char mBuffer[kSize];
173 : };
174 :
175 : /// Default buffer size is 257 to accommodate values with size up to 256
176 : /// If the buffer size is not enough the value will be truncated and an overflow marker will be added
177 : /// TODO Note that this buffer size will be used always by default even for much smaller values
178 : /// TODO Preferably the buffer size should be the one appropriate for each value
179 : using NullTerminated = StringBuilder<257>;
180 :
181 : } // namespace chip
|