1 | // Protocol Buffers - Google's data interchange format |
2 | // Copyright 2008 Google Inc. All rights reserved. |
3 | // https://developers.google.com/protocol-buffers/ |
4 | // |
5 | // Redistribution and use in source and binary forms, with or without |
6 | // modification, are permitted provided that the following conditions are |
7 | // met: |
8 | // |
9 | // * Redistributions of source code must retain the above copyright |
10 | // notice, this list of conditions and the following disclaimer. |
11 | // * Redistributions in binary form must reproduce the above |
12 | // copyright notice, this list of conditions and the following disclaimer |
13 | // in the documentation and/or other materials provided with the |
14 | // distribution. |
15 | // * Neither the name of Google Inc. nor the names of its |
16 | // contributors may be used to endorse or promote products derived from |
17 | // this software without specific prior written permission. |
18 | // |
19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
30 | |
31 | #include <google/protobuf/arenastring.h> |
32 | |
33 | #include <cstddef> |
34 | #include <google/protobuf/stubs/logging.h> |
35 | #include <google/protobuf/stubs/common.h> |
36 | #include <google/protobuf/io/coded_stream.h> |
37 | #include <google/protobuf/stubs/mutex.h> |
38 | #include <google/protobuf/stubs/strutil.h> |
39 | #include <google/protobuf/message_lite.h> |
40 | #include <google/protobuf/parse_context.h> |
41 | #include <google/protobuf/stubs/stl_util.h> |
42 | |
43 | // clang-format off |
44 | #include <google/protobuf/port_def.inc> |
45 | // clang-format on |
46 | |
47 | namespace google { |
48 | namespace protobuf { |
49 | namespace internal { |
50 | |
51 | namespace { |
52 | |
53 | // TaggedStringPtr::Flags uses the lower 2 bits as tags. |
54 | // Enforce that allocated data aligns to at least 4 bytes, and that |
55 | // the alignment of the global const string value does as well. |
56 | // The alignment guaranteed by `new std::string` depends on both: |
57 | // - new align = __STDCPP_DEFAULT_NEW_ALIGNMENT__ / max_align_t |
58 | // - alignof(std::string) |
59 | #ifdef __STDCPP_DEFAULT_NEW_ALIGNMENT__ |
60 | constexpr size_t kNewAlign = __STDCPP_DEFAULT_NEW_ALIGNMENT__; |
61 | #elif (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40900 |
62 | constexpr size_t kNewAlign = alignof(::max_align_t); |
63 | #else |
64 | constexpr size_t kNewAlign = alignof(std::max_align_t); |
65 | #endif |
66 | constexpr size_t kStringAlign = alignof(std::string); |
67 | |
68 | static_assert((kStringAlign > kNewAlign ? kStringAlign : kNewAlign) >= 4, "" ); |
69 | static_assert(alignof(ExplicitlyConstructedArenaString) >= 4, "" ); |
70 | |
71 | } // namespace |
72 | |
73 | const std::string& LazyString::Init() const { |
74 | static WrappedMutex mu{GOOGLE_PROTOBUF_LINKER_INITIALIZED}; |
75 | mu.Lock(); |
76 | const std::string* res = inited_.load(m: std::memory_order_acquire); |
77 | if (res == nullptr) { |
78 | auto init_value = init_value_; |
79 | res = ::new (static_cast<void*>(string_buf_)) |
80 | std::string(init_value.ptr, init_value.size); |
81 | inited_.store(p: res, m: std::memory_order_release); |
82 | } |
83 | mu.Unlock(); |
84 | return *res; |
85 | } |
86 | |
87 | namespace { |
88 | |
89 | |
90 | #if defined(NDEBUG) || !GOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL |
91 | |
92 | class ScopedCheckPtrInvariants { |
93 | public: |
94 | explicit ScopedCheckPtrInvariants(const TaggedStringPtr*) {} |
95 | }; |
96 | |
97 | #endif // NDEBUG || !GOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL |
98 | |
99 | // Creates a heap allocated std::string value. |
100 | inline TaggedStringPtr CreateString(ConstStringParam value) { |
101 | TaggedStringPtr res; |
102 | res.SetAllocated(new std::string(value.data(), value.length())); |
103 | return res; |
104 | } |
105 | |
106 | #if !GOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL |
107 | |
108 | // Creates an arena allocated std::string value. |
109 | TaggedStringPtr CreateArenaString(Arena& arena, ConstStringParam s) { |
110 | TaggedStringPtr res; |
111 | res.SetMutableArena(Arena::Create<std::string>(arena: &arena, args: s.data(), args: s.length())); |
112 | return res; |
113 | } |
114 | |
115 | #endif // !GOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL |
116 | |
117 | } // namespace |
118 | |
119 | void ArenaStringPtr::Set(ConstStringParam value, Arena* arena) { |
120 | ScopedCheckPtrInvariants check(&tagged_ptr_); |
121 | if (IsDefault()) { |
122 | // If we're not on an arena, skip straight to a true string to avoid |
123 | // possible copy cost later. |
124 | tagged_ptr_ = arena != nullptr ? CreateArenaString(arena&: *arena, s: value) |
125 | : CreateString(value); |
126 | } else { |
127 | UnsafeMutablePointer()->assign(s: value.data(), n: value.length()); |
128 | } |
129 | } |
130 | |
131 | void ArenaStringPtr::Set(std::string&& value, Arena* arena) { |
132 | ScopedCheckPtrInvariants check(&tagged_ptr_); |
133 | if (IsDefault()) { |
134 | NewString(arena, args: std::move(value)); |
135 | } else if (IsFixedSizeArena()) { |
136 | std::string* current = tagged_ptr_.Get(); |
137 | auto* s = new (current) std::string(std::move(value)); |
138 | arena->OwnDestructor(object: s); |
139 | tagged_ptr_.SetMutableArena(s); |
140 | } else /* !IsFixedSizeArena() */ { |
141 | *UnsafeMutablePointer() = std::move(value); |
142 | } |
143 | } |
144 | |
145 | std::string* ArenaStringPtr::Mutable(Arena* arena) { |
146 | ScopedCheckPtrInvariants check(&tagged_ptr_); |
147 | if (tagged_ptr_.IsMutable()) { |
148 | return tagged_ptr_.Get(); |
149 | } else { |
150 | return MutableSlow(arena); |
151 | } |
152 | } |
153 | |
154 | std::string* ArenaStringPtr::Mutable(const LazyString& default_value, |
155 | Arena* arena) { |
156 | ScopedCheckPtrInvariants check(&tagged_ptr_); |
157 | if (tagged_ptr_.IsMutable()) { |
158 | return tagged_ptr_.Get(); |
159 | } else { |
160 | return MutableSlow(arena, lazy_default: default_value); |
161 | } |
162 | } |
163 | |
164 | std::string* ArenaStringPtr::MutableNoCopy(Arena* arena) { |
165 | ScopedCheckPtrInvariants check(&tagged_ptr_); |
166 | if (tagged_ptr_.IsMutable()) { |
167 | return tagged_ptr_.Get(); |
168 | } else { |
169 | GOOGLE_DCHECK(IsDefault()); |
170 | // Allocate empty. The contents are not relevant. |
171 | return NewString(arena); |
172 | } |
173 | } |
174 | |
175 | template <typename... Lazy> |
176 | std::string* ArenaStringPtr::MutableSlow(::google::protobuf::Arena* arena, |
177 | const Lazy&... lazy_default) { |
178 | GOOGLE_DCHECK(IsDefault()); |
179 | |
180 | // For empty defaults, this ends up calling the default constructor which is |
181 | // more efficient than a copy construction from |
182 | // GetEmptyStringAlreadyInited(). |
183 | return NewString(arena, lazy_default.get()...); |
184 | } |
185 | |
186 | std::string* ArenaStringPtr::Release() { |
187 | ScopedCheckPtrInvariants check(&tagged_ptr_); |
188 | if (IsDefault()) return nullptr; |
189 | |
190 | std::string* released = tagged_ptr_.Get(); |
191 | if (tagged_ptr_.IsArena()) { |
192 | released = tagged_ptr_.IsMutable() ? new std::string(std::move(*released)) |
193 | : new std::string(*released); |
194 | } |
195 | InitDefault(); |
196 | return released; |
197 | } |
198 | |
199 | void ArenaStringPtr::SetAllocated(std::string* value, Arena* arena) { |
200 | ScopedCheckPtrInvariants check(&tagged_ptr_); |
201 | // Release what we have first. |
202 | Destroy(); |
203 | |
204 | if (value == nullptr) { |
205 | InitDefault(); |
206 | } else { |
207 | #ifndef NDEBUG |
208 | // On debug builds, copy the string so the address differs. delete will |
209 | // fail if value was a stack-allocated temporary/etc., which would have |
210 | // failed when arena ran its cleanup list. |
211 | std::string* new_value = new std::string(std::move(*value)); |
212 | delete value; |
213 | value = new_value; |
214 | #endif // !NDEBUG |
215 | InitAllocated(str: value, arena); |
216 | } |
217 | } |
218 | |
219 | void ArenaStringPtr::Destroy() { |
220 | delete tagged_ptr_.GetIfAllocated(); |
221 | } |
222 | |
223 | void ArenaStringPtr::ClearToEmpty() { |
224 | ScopedCheckPtrInvariants check(&tagged_ptr_); |
225 | if (IsDefault()) { |
226 | // Already set to default -- do nothing. |
227 | } else { |
228 | // Unconditionally mask away the tag. |
229 | // |
230 | // UpdateArenaString uses assign when capacity is larger than the new |
231 | // value, which is trivially true in the donated string case. |
232 | // const_cast<std::string*>(PtrValue<std::string>())->clear(); |
233 | tagged_ptr_.Get()->clear(); |
234 | } |
235 | } |
236 | |
237 | void ArenaStringPtr::ClearToDefault(const LazyString& default_value, |
238 | ::google::protobuf::Arena* arena) { |
239 | ScopedCheckPtrInvariants check(&tagged_ptr_); |
240 | (void)arena; |
241 | if (IsDefault()) { |
242 | // Already set to default -- do nothing. |
243 | } else { |
244 | UnsafeMutablePointer()->assign(str: default_value.get()); |
245 | } |
246 | } |
247 | |
248 | const char* EpsCopyInputStream::ReadArenaString(const char* ptr, |
249 | ArenaStringPtr* s, |
250 | Arena* arena) { |
251 | ScopedCheckPtrInvariants check(&s->tagged_ptr_); |
252 | GOOGLE_DCHECK(arena != nullptr); |
253 | |
254 | int size = ReadSize(pp: &ptr); |
255 | if (!ptr) return nullptr; |
256 | |
257 | auto* str = s->NewString(arena); |
258 | ptr = ReadString(ptr, size, s: str); |
259 | GOOGLE_PROTOBUF_PARSER_ASSERT(ptr); |
260 | return ptr; |
261 | } |
262 | |
263 | } // namespace internal |
264 | } // namespace protobuf |
265 | } // namespace google |
266 | |
267 | #include <google/protobuf/port_undef.inc> |
268 | |