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
47namespace google {
48namespace protobuf {
49namespace internal {
50
51namespace {
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__
60constexpr size_t kNewAlign = __STDCPP_DEFAULT_NEW_ALIGNMENT__;
61#elif (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40900
62constexpr size_t kNewAlign = alignof(::max_align_t);
63#else
64constexpr size_t kNewAlign = alignof(std::max_align_t);
65#endif
66constexpr size_t kStringAlign = alignof(std::string);
67
68static_assert((kStringAlign > kNewAlign ? kStringAlign : kNewAlign) >= 4, "");
69static_assert(alignof(ExplicitlyConstructedArenaString) >= 4, "");
70
71} // namespace
72
73const 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
87namespace {
88
89
90#if defined(NDEBUG) || !GOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL
91
92class 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.
100inline 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.
109TaggedStringPtr 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
119void 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
131void 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
145std::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
154std::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
164std::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
175template <typename... Lazy>
176std::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
186std::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
199void 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
219void ArenaStringPtr::Destroy() {
220 delete tagged_ptr_.GetIfAllocated();
221}
222
223void 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
237void 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
248const 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