/proc/self/cwd/pw_string/string_builder.cc
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright 2019 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 | | |
15 | | #include "pw_string/string_builder.h" |
16 | | |
17 | | #include <cstdio> |
18 | | |
19 | | #include "pw_string/format.h" |
20 | | #include "pw_string/util.h" |
21 | | |
22 | | namespace pw { |
23 | | |
24 | 0 | void StringBuilder::clear() { |
25 | 0 | *size_ = 0; |
26 | 0 | NullTerminate(); |
27 | 0 | status_ = StatusCode(OkStatus()); |
28 | 0 | last_status_ = StatusCode(OkStatus()); |
29 | 0 | } |
30 | | |
31 | 0 | StringBuilder& StringBuilder::append(size_t count, char ch) { |
32 | 0 | char* const append_destination = buffer_.data() + size(); |
33 | 0 | std::fill_n(append_destination, ResizeAndTerminate(count), ch); |
34 | 0 | return *this; |
35 | 0 | } |
36 | | |
37 | 0 | StringBuilder& StringBuilder::append(const char* str, size_t count) { |
38 | 0 | char* const append_destination = buffer_.data() + size(); |
39 | 0 | std::copy_n(str, ResizeAndTerminate(count), append_destination); |
40 | 0 | return *this; |
41 | 0 | } |
42 | | |
43 | 0 | StringBuilder& StringBuilder::append(const char* str) { |
44 | | // Use buffer_.size() - size() as the maximum length so that strings too long |
45 | | // to fit in the buffer will request one character too many, which sets the |
46 | | // status to RESOURCE_EXHAUSTED. |
47 | 0 | return append(string::ClampedCString(str, buffer_.size() - size())); |
48 | 0 | } |
49 | | |
50 | 0 | StringBuilder& StringBuilder::append(std::string_view str) { |
51 | 0 | return append(str.data(), str.size()); |
52 | 0 | } |
53 | | |
54 | | StringBuilder& StringBuilder::append(std::string_view str, |
55 | | size_t pos, |
56 | 0 | size_t count) { |
57 | 0 | if (pos > str.size()) { |
58 | 0 | SetErrorStatus(Status::OutOfRange()); |
59 | 0 | return *this; |
60 | 0 | } |
61 | | |
62 | 0 | return append(str.data() + pos, std::min(str.size() - pos, count)); |
63 | 0 | } |
64 | | |
65 | 0 | size_t StringBuilder::ResizeAndTerminate(size_t chars_to_append) { |
66 | 0 | const size_t copied = std::min(chars_to_append, max_size() - size()); |
67 | | // NOTE: `+=` is not used in order to avoid implicit integer conversion which |
68 | | // results in an error on some compilers. |
69 | 0 | *size_ = static_cast<InlineString<>::size_type>(copied + *size_); |
70 | 0 | NullTerminate(); |
71 | |
|
72 | 0 | if (buffer_.empty() || chars_to_append != copied) { |
73 | 0 | SetErrorStatus(Status::ResourceExhausted()); |
74 | 0 | } else { |
75 | 0 | last_status_ = StatusCode(OkStatus()); |
76 | 0 | } |
77 | 0 | return copied; |
78 | 0 | } |
79 | | |
80 | 0 | void StringBuilder::resize(size_t new_size) { |
81 | 0 | if (new_size <= size()) { |
82 | 0 | *size_ = static_cast<InlineString<>::size_type>(new_size); |
83 | 0 | NullTerminate(); |
84 | 0 | last_status_ = StatusCode(OkStatus()); |
85 | 0 | } else { |
86 | 0 | SetErrorStatus(Status::OutOfRange()); |
87 | 0 | } |
88 | 0 | } |
89 | | |
90 | 0 | StringBuilder& StringBuilder::Format(const char* format, ...) { |
91 | 0 | va_list args; |
92 | 0 | va_start(args, format); |
93 | 0 | FormatVaList(format, args); |
94 | 0 | va_end(args); |
95 | |
|
96 | 0 | return *this; |
97 | 0 | } |
98 | | |
99 | 0 | StringBuilder& StringBuilder::FormatVaList(const char* format, va_list args) { |
100 | 0 | HandleStatusWithSize( |
101 | 0 | string::FormatVaList(buffer_.subspan(size()), format, args)); |
102 | 0 | return *this; |
103 | 0 | } |
104 | | |
105 | 0 | void StringBuilder::WriteBytes(span<const std::byte> data) { |
106 | 0 | if (size() + data.size() * 2 > max_size()) { |
107 | 0 | SetErrorStatus(Status::ResourceExhausted()); |
108 | 0 | } else { |
109 | 0 | for (std::byte val : data) { |
110 | 0 | *this << val; |
111 | 0 | } |
112 | 0 | } |
113 | 0 | } |
114 | | |
115 | 0 | void StringBuilder::CopySizeAndStatus(const StringBuilder& other) { |
116 | 0 | *size_ = static_cast<InlineString<>::size_type>(other.size()); |
117 | 0 | status_ = other.status_; |
118 | 0 | last_status_ = other.last_status_; |
119 | 0 | } |
120 | | |
121 | 0 | void StringBuilder::HandleStatusWithSize(StatusWithSize written) { |
122 | 0 | const Status status = written.status(); |
123 | 0 | last_status_ = StatusCode(status); |
124 | 0 | if (!status.ok()) { |
125 | 0 | status_ = StatusCode(status); |
126 | 0 | } |
127 | | |
128 | | // NOTE: `+=` is not used in order to avoid implicit integer conversion which |
129 | | // results in an error on some compilers. |
130 | 0 | *size_ = static_cast<InlineString<>::size_type>(written.size() + *size_); |
131 | 0 | } |
132 | | |
133 | 0 | void StringBuilder::SetErrorStatus(Status status) { |
134 | 0 | last_status_ = StatusCode(status); |
135 | 0 | status_ = StatusCode(status); |
136 | 0 | } |
137 | | |
138 | | } // namespace pw |