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
Init() const73 const std::string& LazyString::Init() const {
74 static WrappedMutex mu{GOOGLE_PROTOBUF_LINKER_INITIALIZED};
75 mu.Lock();
76 const std::string* res = inited_.load(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(res, 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:
ScopedCheckPtrInvariants(const TaggedStringPtr *)94 explicit ScopedCheckPtrInvariants(const TaggedStringPtr*) {}
95 };
96
97 #endif // NDEBUG || !GOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL
98
99 // Creates a heap allocated std::string value.
CreateString(ConstStringParam 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.
CreateArenaString(Arena & arena,ConstStringParam s)109 TaggedStringPtr CreateArenaString(Arena& arena, ConstStringParam s) {
110 TaggedStringPtr res;
111 res.SetMutableArena(Arena::Create<std::string>(&arena, s.data(), s.length()));
112 return res;
113 }
114
115 #endif // !GOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL
116
117 } // namespace
118
Set(ConstStringParam value,Arena * arena)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, value)
125 : CreateString(value);
126 } else {
127 UnsafeMutablePointer()->assign(value.data(), value.length());
128 }
129 }
130
Set(std::string && value,Arena * arena)131 void ArenaStringPtr::Set(std::string&& value, Arena* arena) {
132 ScopedCheckPtrInvariants check(&tagged_ptr_);
133 if (IsDefault()) {
134 NewString(arena, 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(s);
139 tagged_ptr_.SetMutableArena(s);
140 } else /* !IsFixedSizeArena() */ {
141 *UnsafeMutablePointer() = std::move(value);
142 }
143 }
144
Mutable(Arena * arena)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
Mutable(const LazyString & default_value,Arena * arena)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, default_value);
161 }
162 }
163
MutableNoCopy(Arena * arena)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>
MutableSlow(::google::protobuf::Arena * arena,const Lazy &...lazy_default)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
Release()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
SetAllocated(std::string * value,Arena * arena)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(value, arena);
216 }
217 }
218
Destroy()219 void ArenaStringPtr::Destroy() {
220 delete tagged_ptr_.GetIfAllocated();
221 }
222
ClearToEmpty()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
ClearToDefault(const LazyString & default_value,::google::protobuf::Arena * arena)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(default_value.get());
245 }
246 }
247
ReadArenaString(const char * ptr,ArenaStringPtr * s,Arena * arena)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(&ptr);
255 if (!ptr) return nullptr;
256
257 auto* str = s->NewString(arena);
258 ptr = ReadString(ptr, size, 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