xref: /aosp_15_r20/external/pigweed/pw_string/string_builder.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1*61c4878aSAndroid Build Coastguard Worker // Copyright 2019 The Pigweed Authors
2*61c4878aSAndroid Build Coastguard Worker //
3*61c4878aSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4*61c4878aSAndroid Build Coastguard Worker // use this file except in compliance with the License. You may obtain a copy of
5*61c4878aSAndroid Build Coastguard Worker // the License at
6*61c4878aSAndroid Build Coastguard Worker //
7*61c4878aSAndroid Build Coastguard Worker //     https://www.apache.org/licenses/LICENSE-2.0
8*61c4878aSAndroid Build Coastguard Worker //
9*61c4878aSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*61c4878aSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11*61c4878aSAndroid Build Coastguard Worker // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12*61c4878aSAndroid Build Coastguard Worker // License for the specific language governing permissions and limitations under
13*61c4878aSAndroid Build Coastguard Worker // the License.
14*61c4878aSAndroid Build Coastguard Worker 
15*61c4878aSAndroid Build Coastguard Worker #include "pw_string/string_builder.h"
16*61c4878aSAndroid Build Coastguard Worker 
17*61c4878aSAndroid Build Coastguard Worker #include <cstdio>
18*61c4878aSAndroid Build Coastguard Worker 
19*61c4878aSAndroid Build Coastguard Worker #include "pw_string/format.h"
20*61c4878aSAndroid Build Coastguard Worker #include "pw_string/util.h"
21*61c4878aSAndroid Build Coastguard Worker 
22*61c4878aSAndroid Build Coastguard Worker namespace pw {
23*61c4878aSAndroid Build Coastguard Worker 
clear()24*61c4878aSAndroid Build Coastguard Worker void StringBuilder::clear() {
25*61c4878aSAndroid Build Coastguard Worker   *size_ = 0;
26*61c4878aSAndroid Build Coastguard Worker   NullTerminate();
27*61c4878aSAndroid Build Coastguard Worker   status_ = StatusCode(OkStatus());
28*61c4878aSAndroid Build Coastguard Worker   last_status_ = StatusCode(OkStatus());
29*61c4878aSAndroid Build Coastguard Worker }
30*61c4878aSAndroid Build Coastguard Worker 
append(size_t count,char ch)31*61c4878aSAndroid Build Coastguard Worker StringBuilder& StringBuilder::append(size_t count, char ch) {
32*61c4878aSAndroid Build Coastguard Worker   char* const append_destination = buffer_.data() + size();
33*61c4878aSAndroid Build Coastguard Worker   std::fill_n(append_destination, ResizeAndTerminate(count), ch);
34*61c4878aSAndroid Build Coastguard Worker   return *this;
35*61c4878aSAndroid Build Coastguard Worker }
36*61c4878aSAndroid Build Coastguard Worker 
append(const char * str,size_t count)37*61c4878aSAndroid Build Coastguard Worker StringBuilder& StringBuilder::append(const char* str, size_t count) {
38*61c4878aSAndroid Build Coastguard Worker   char* const append_destination = buffer_.data() + size();
39*61c4878aSAndroid Build Coastguard Worker   std::copy_n(str, ResizeAndTerminate(count), append_destination);
40*61c4878aSAndroid Build Coastguard Worker   return *this;
41*61c4878aSAndroid Build Coastguard Worker }
42*61c4878aSAndroid Build Coastguard Worker 
append(const char * str)43*61c4878aSAndroid Build Coastguard Worker StringBuilder& StringBuilder::append(const char* str) {
44*61c4878aSAndroid Build Coastguard Worker   // Use buffer_.size() - size() as the maximum length so that strings too long
45*61c4878aSAndroid Build Coastguard Worker   // to fit in the buffer will request one character too many, which sets the
46*61c4878aSAndroid Build Coastguard Worker   // status to RESOURCE_EXHAUSTED.
47*61c4878aSAndroid Build Coastguard Worker   return append(string::ClampedCString(str, buffer_.size() - size()));
48*61c4878aSAndroid Build Coastguard Worker }
49*61c4878aSAndroid Build Coastguard Worker 
append(std::string_view str)50*61c4878aSAndroid Build Coastguard Worker StringBuilder& StringBuilder::append(std::string_view str) {
51*61c4878aSAndroid Build Coastguard Worker   return append(str.data(), str.size());
52*61c4878aSAndroid Build Coastguard Worker }
53*61c4878aSAndroid Build Coastguard Worker 
append(std::string_view str,size_t pos,size_t count)54*61c4878aSAndroid Build Coastguard Worker StringBuilder& StringBuilder::append(std::string_view str,
55*61c4878aSAndroid Build Coastguard Worker                                      size_t pos,
56*61c4878aSAndroid Build Coastguard Worker                                      size_t count) {
57*61c4878aSAndroid Build Coastguard Worker   if (pos > str.size()) {
58*61c4878aSAndroid Build Coastguard Worker     SetErrorStatus(Status::OutOfRange());
59*61c4878aSAndroid Build Coastguard Worker     return *this;
60*61c4878aSAndroid Build Coastguard Worker   }
61*61c4878aSAndroid Build Coastguard Worker 
62*61c4878aSAndroid Build Coastguard Worker   return append(str.data() + pos, std::min(str.size() - pos, count));
63*61c4878aSAndroid Build Coastguard Worker }
64*61c4878aSAndroid Build Coastguard Worker 
ResizeAndTerminate(size_t chars_to_append)65*61c4878aSAndroid Build Coastguard Worker size_t StringBuilder::ResizeAndTerminate(size_t chars_to_append) {
66*61c4878aSAndroid Build Coastguard Worker   const size_t copied = std::min(chars_to_append, max_size() - size());
67*61c4878aSAndroid Build Coastguard Worker   // NOTE: `+=` is not used in order to avoid implicit integer conversion which
68*61c4878aSAndroid Build Coastguard Worker   // results in an error on some compilers.
69*61c4878aSAndroid Build Coastguard Worker   *size_ = static_cast<InlineString<>::size_type>(copied + *size_);
70*61c4878aSAndroid Build Coastguard Worker   NullTerminate();
71*61c4878aSAndroid Build Coastguard Worker 
72*61c4878aSAndroid Build Coastguard Worker   if (buffer_.empty() || chars_to_append != copied) {
73*61c4878aSAndroid Build Coastguard Worker     SetErrorStatus(Status::ResourceExhausted());
74*61c4878aSAndroid Build Coastguard Worker   } else {
75*61c4878aSAndroid Build Coastguard Worker     last_status_ = StatusCode(OkStatus());
76*61c4878aSAndroid Build Coastguard Worker   }
77*61c4878aSAndroid Build Coastguard Worker   return copied;
78*61c4878aSAndroid Build Coastguard Worker }
79*61c4878aSAndroid Build Coastguard Worker 
resize(size_t new_size)80*61c4878aSAndroid Build Coastguard Worker void StringBuilder::resize(size_t new_size) {
81*61c4878aSAndroid Build Coastguard Worker   if (new_size <= size()) {
82*61c4878aSAndroid Build Coastguard Worker     *size_ = static_cast<InlineString<>::size_type>(new_size);
83*61c4878aSAndroid Build Coastguard Worker     NullTerminate();
84*61c4878aSAndroid Build Coastguard Worker     last_status_ = StatusCode(OkStatus());
85*61c4878aSAndroid Build Coastguard Worker   } else {
86*61c4878aSAndroid Build Coastguard Worker     SetErrorStatus(Status::OutOfRange());
87*61c4878aSAndroid Build Coastguard Worker   }
88*61c4878aSAndroid Build Coastguard Worker }
89*61c4878aSAndroid Build Coastguard Worker 
Format(const char * format,...)90*61c4878aSAndroid Build Coastguard Worker StringBuilder& StringBuilder::Format(const char* format, ...) {
91*61c4878aSAndroid Build Coastguard Worker   va_list args;
92*61c4878aSAndroid Build Coastguard Worker   va_start(args, format);
93*61c4878aSAndroid Build Coastguard Worker   FormatVaList(format, args);
94*61c4878aSAndroid Build Coastguard Worker   va_end(args);
95*61c4878aSAndroid Build Coastguard Worker 
96*61c4878aSAndroid Build Coastguard Worker   return *this;
97*61c4878aSAndroid Build Coastguard Worker }
98*61c4878aSAndroid Build Coastguard Worker 
FormatVaList(const char * format,va_list args)99*61c4878aSAndroid Build Coastguard Worker StringBuilder& StringBuilder::FormatVaList(const char* format, va_list args) {
100*61c4878aSAndroid Build Coastguard Worker   HandleStatusWithSize(
101*61c4878aSAndroid Build Coastguard Worker       string::FormatVaList(buffer_.subspan(size()), format, args));
102*61c4878aSAndroid Build Coastguard Worker   return *this;
103*61c4878aSAndroid Build Coastguard Worker }
104*61c4878aSAndroid Build Coastguard Worker 
WriteBytes(span<const std::byte> data)105*61c4878aSAndroid Build Coastguard Worker void StringBuilder::WriteBytes(span<const std::byte> data) {
106*61c4878aSAndroid Build Coastguard Worker   if (size() + data.size() * 2 > max_size()) {
107*61c4878aSAndroid Build Coastguard Worker     SetErrorStatus(Status::ResourceExhausted());
108*61c4878aSAndroid Build Coastguard Worker   } else {
109*61c4878aSAndroid Build Coastguard Worker     for (std::byte val : data) {
110*61c4878aSAndroid Build Coastguard Worker       *this << val;
111*61c4878aSAndroid Build Coastguard Worker     }
112*61c4878aSAndroid Build Coastguard Worker   }
113*61c4878aSAndroid Build Coastguard Worker }
114*61c4878aSAndroid Build Coastguard Worker 
CopySizeAndStatus(const StringBuilder & other)115*61c4878aSAndroid Build Coastguard Worker void StringBuilder::CopySizeAndStatus(const StringBuilder& other) {
116*61c4878aSAndroid Build Coastguard Worker   *size_ = static_cast<InlineString<>::size_type>(other.size());
117*61c4878aSAndroid Build Coastguard Worker   status_ = other.status_;
118*61c4878aSAndroid Build Coastguard Worker   last_status_ = other.last_status_;
119*61c4878aSAndroid Build Coastguard Worker }
120*61c4878aSAndroid Build Coastguard Worker 
HandleStatusWithSize(StatusWithSize written)121*61c4878aSAndroid Build Coastguard Worker void StringBuilder::HandleStatusWithSize(StatusWithSize written) {
122*61c4878aSAndroid Build Coastguard Worker   const Status status = written.status();
123*61c4878aSAndroid Build Coastguard Worker   last_status_ = StatusCode(status);
124*61c4878aSAndroid Build Coastguard Worker   if (!status.ok()) {
125*61c4878aSAndroid Build Coastguard Worker     status_ = StatusCode(status);
126*61c4878aSAndroid Build Coastguard Worker   }
127*61c4878aSAndroid Build Coastguard Worker 
128*61c4878aSAndroid Build Coastguard Worker   // NOTE: `+=` is not used in order to avoid implicit integer conversion which
129*61c4878aSAndroid Build Coastguard Worker   // results in an error on some compilers.
130*61c4878aSAndroid Build Coastguard Worker   *size_ = static_cast<InlineString<>::size_type>(written.size() + *size_);
131*61c4878aSAndroid Build Coastguard Worker }
132*61c4878aSAndroid Build Coastguard Worker 
SetErrorStatus(Status status)133*61c4878aSAndroid Build Coastguard Worker void StringBuilder::SetErrorStatus(Status status) {
134*61c4878aSAndroid Build Coastguard Worker   last_status_ = StatusCode(status);
135*61c4878aSAndroid Build Coastguard Worker   status_ = StatusCode(status);
136*61c4878aSAndroid Build Coastguard Worker }
137*61c4878aSAndroid Build Coastguard Worker 
138*61c4878aSAndroid Build Coastguard Worker }  // namespace pw
139