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 <google/protobuf/stubs/logging.h>
34 #include <google/protobuf/stubs/common.h>
35 #include <google/protobuf/parse_context.h>
36 #include <google/protobuf/io/coded_stream.h>
37 #include <google/protobuf/message_lite.h>
38 #include <google/protobuf/stubs/mutex.h>
39 #include <google/protobuf/stubs/strutil.h>
40 #include <google/protobuf/stubs/stl_util.h>
41 
42 // clang-format off
43 #include <google/protobuf/port_def.inc>
44 // clang-format on
45 
46 namespace google {
47 namespace protobuf {
48 namespace internal {
49 
Init() const50 const std::string& LazyString::Init() const {
51   static WrappedMutex mu{GOOGLE_PROTOBUF_LINKER_INITIALIZED};
52   mu.Lock();
53   const std::string* res = inited_.load(std::memory_order_acquire);
54   if (res == nullptr) {
55     auto init_value = init_value_;
56     res = ::new (static_cast<void*>(string_buf_))
57         std::string(init_value.ptr, init_value.size);
58     inited_.store(res, std::memory_order_release);
59   }
60   mu.Unlock();
61   return *res;
62 }
63 
64 
SetAndReturnNewString()65 std::string* ArenaStringPtr::SetAndReturnNewString() {
66   std::string* new_string = new std::string();
67   tagged_ptr_.Set(new_string);
68   return new_string;
69 }
70 
DestroyNoArenaSlowPath()71 void ArenaStringPtr::DestroyNoArenaSlowPath() { delete UnsafeMutablePointer(); }
72 
Set(const std::string * default_value,ConstStringParam value,::google::protobuf::Arena * arena)73 void ArenaStringPtr::Set(const std::string* default_value,
74                          ConstStringParam value, ::google::protobuf::Arena* arena) {
75   if (IsDefault(default_value)) {
76     tagged_ptr_.Set(Arena::Create<std::string>(arena, value));
77   } else {
78     UnsafeMutablePointer()->assign(value.data(), value.length());
79   }
80 }
81 
Set(const std::string * default_value,std::string && value,::google::protobuf::Arena * arena)82 void ArenaStringPtr::Set(const std::string* default_value, std::string&& value,
83                          ::google::protobuf::Arena* arena) {
84   if (IsDefault(default_value)) {
85     if (arena == nullptr) {
86       tagged_ptr_.Set(new std::string(std::move(value)));
87     } else {
88       tagged_ptr_.Set(Arena::Create<std::string>(arena, std::move(value)));
89     }
90   } else if (IsDonatedString()) {
91     std::string* current = tagged_ptr_.Get();
92     auto* s = new (current) std::string(std::move(value));
93     arena->OwnDestructor(s);
94     tagged_ptr_.Set(s);
95   } else /* !IsDonatedString() */ {
96     *UnsafeMutablePointer() = std::move(value);
97   }
98 }
99 
Set(EmptyDefault,ConstStringParam value,::google::protobuf::Arena * arena)100 void ArenaStringPtr::Set(EmptyDefault, ConstStringParam value,
101                          ::google::protobuf::Arena* arena) {
102   Set(&GetEmptyStringAlreadyInited(), value, arena);
103 }
104 
Set(EmptyDefault,std::string && value,::google::protobuf::Arena * arena)105 void ArenaStringPtr::Set(EmptyDefault, std::string&& value,
106                          ::google::protobuf::Arena* arena) {
107   Set(&GetEmptyStringAlreadyInited(), std::move(value), arena);
108 }
109 
Set(NonEmptyDefault,ConstStringParam value,::google::protobuf::Arena * arena)110 void ArenaStringPtr::Set(NonEmptyDefault, ConstStringParam value,
111                          ::google::protobuf::Arena* arena) {
112   Set(nullptr, value, arena);
113 }
114 
Set(NonEmptyDefault,std::string && value,::google::protobuf::Arena * arena)115 void ArenaStringPtr::Set(NonEmptyDefault, std::string&& value,
116                          ::google::protobuf::Arena* arena) {
117   Set(nullptr, std::move(value), arena);
118 }
119 
Mutable(EmptyDefault,::google::protobuf::Arena * arena)120 std::string* ArenaStringPtr::Mutable(EmptyDefault, ::google::protobuf::Arena* arena) {
121   if (!IsDonatedString() && !IsDefault(&GetEmptyStringAlreadyInited())) {
122     return UnsafeMutablePointer();
123   } else {
124     return MutableSlow(arena);
125   }
126 }
127 
Mutable(const LazyString & default_value,::google::protobuf::Arena * arena)128 std::string* ArenaStringPtr::Mutable(const LazyString& default_value,
129                                      ::google::protobuf::Arena* arena) {
130   if (!IsDonatedString() && !IsDefault(nullptr)) {
131     return UnsafeMutablePointer();
132   } else {
133     return MutableSlow(arena, default_value);
134   }
135 }
136 
MutableNoCopy(const std::string * default_value,::google::protobuf::Arena * arena)137 std::string* ArenaStringPtr::MutableNoCopy(const std::string* default_value,
138                                            ::google::protobuf::Arena* arena) {
139   if (!IsDonatedString() && !IsDefault(default_value)) {
140     return UnsafeMutablePointer();
141   } else {
142     GOOGLE_DCHECK(IsDefault(default_value));
143     // Allocate empty. The contents are not relevant.
144     std::string* new_string = Arena::Create<std::string>(arena);
145     tagged_ptr_.Set(new_string);
146     return new_string;
147   }
148 }
149 
150 template <typename... Lazy>
MutableSlow(::google::protobuf::Arena * arena,const Lazy &...lazy_default)151 std::string* ArenaStringPtr::MutableSlow(::google::protobuf::Arena* arena,
152                                          const Lazy&... lazy_default) {
153   const std::string* const default_value =
154       sizeof...(Lazy) == 0 ? &GetEmptyStringAlreadyInited() : nullptr;
155   GOOGLE_DCHECK(IsDefault(default_value));
156   std::string* new_string =
157       Arena::Create<std::string>(arena, lazy_default.get()...);
158   tagged_ptr_.Set(new_string);
159   return new_string;
160 }
161 
Release(const std::string * default_value,::google::protobuf::Arena * arena)162 std::string* ArenaStringPtr::Release(const std::string* default_value,
163                                      ::google::protobuf::Arena* arena) {
164   if (IsDefault(default_value)) {
165     return nullptr;
166   } else {
167     return ReleaseNonDefault(default_value, arena);
168   }
169 }
170 
ReleaseNonDefault(const std::string * default_value,::google::protobuf::Arena * arena)171 std::string* ArenaStringPtr::ReleaseNonDefault(const std::string* default_value,
172                                                ::google::protobuf::Arena* arena) {
173   GOOGLE_DCHECK(!IsDefault(default_value));
174 
175   if (!IsDonatedString()) {
176     std::string* released;
177     if (arena != nullptr) {
178       released = new std::string;
179       released->swap(*UnsafeMutablePointer());
180     } else {
181       released = UnsafeMutablePointer();
182     }
183     tagged_ptr_.Set(const_cast<std::string*>(default_value));
184     return released;
185   } else /* IsDonatedString() */ {
186     GOOGLE_DCHECK(arena != nullptr);
187     std::string* released = new std::string(Get());
188     tagged_ptr_.Set(const_cast<std::string*>(default_value));
189     return released;
190   }
191 }
192 
SetAllocated(const std::string * default_value,std::string * value,::google::protobuf::Arena * arena)193 void ArenaStringPtr::SetAllocated(const std::string* default_value,
194                                   std::string* value, ::google::protobuf::Arena* arena) {
195   // Release what we have first.
196   if (arena == nullptr && !IsDefault(default_value)) {
197     delete UnsafeMutablePointer();
198   }
199   if (value == nullptr) {
200     tagged_ptr_.Set(const_cast<std::string*>(default_value));
201   } else {
202 #ifdef NDEBUG
203     tagged_ptr_.Set(value);
204     if (arena != nullptr) {
205       arena->Own(value);
206     }
207 #else
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 = Arena::Create<std::string>(arena, *value);
212     delete value;
213     tagged_ptr_.Set(new_value);
214 #endif
215   }
216 }
217 
Destroy(const std::string * default_value,::google::protobuf::Arena * arena)218 void ArenaStringPtr::Destroy(const std::string* default_value,
219                              ::google::protobuf::Arena* arena) {
220   if (arena == nullptr) {
221     GOOGLE_DCHECK(!IsDonatedString());
222     if (!IsDefault(default_value)) {
223       delete UnsafeMutablePointer();
224     }
225   }
226 }
227 
Destroy(EmptyDefault,::google::protobuf::Arena * arena)228 void ArenaStringPtr::Destroy(EmptyDefault, ::google::protobuf::Arena* arena) {
229   Destroy(&GetEmptyStringAlreadyInited(), arena);
230 }
231 
Destroy(NonEmptyDefault,::google::protobuf::Arena * arena)232 void ArenaStringPtr::Destroy(NonEmptyDefault, ::google::protobuf::Arena* arena) {
233   Destroy(nullptr, arena);
234 }
235 
ClearToEmpty()236 void ArenaStringPtr::ClearToEmpty() {
237   if (IsDefault(&GetEmptyStringAlreadyInited())) {
238     // Already set to default -- do nothing.
239   } else {
240     // Unconditionally mask away the tag.
241     //
242     // UpdateDonatedString uses assign when capacity is larger than the new
243     // value, which is trivially true in the donated string case.
244     // const_cast<std::string*>(PtrValue<std::string>())->clear();
245     tagged_ptr_.Get()->clear();
246   }
247 }
248 
ClearToDefault(const LazyString & default_value,::google::protobuf::Arena * arena)249 void ArenaStringPtr::ClearToDefault(const LazyString& default_value,
250                                     ::google::protobuf::Arena* arena) {
251   (void)arena;
252   if (IsDefault(nullptr)) {
253     // Already set to default -- do nothing.
254   } else if (!IsDonatedString()) {
255     UnsafeMutablePointer()->assign(default_value.get());
256   }
257 }
258 
ReadArenaString(const char * ptr,ArenaStringPtr * s,Arena * arena)259 const char* EpsCopyInputStream::ReadArenaString(const char* ptr,
260                                                 ArenaStringPtr* s,
261                                                 Arena* arena) {
262   GOOGLE_DCHECK(arena != nullptr);
263 
264   int size = ReadSize(&ptr);
265   if (!ptr) return nullptr;
266 
267   auto str = Arena::Create<std::string>(arena);
268   ptr = ReadString(ptr, size, str);
269   GOOGLE_PROTOBUF_PARSER_ASSERT(ptr);
270 
271   TaggedPtr<std::string> res;
272   res.Set(str);
273   s->UnsafeSetTaggedPointer(res);
274 
275   return ptr;
276 }
277 
278 }  // namespace internal
279 }  // namespace protobuf
280 }  // namespace google
281 
282 #include <google/protobuf/port_undef.inc>
283