xref: /aosp_15_r20/external/cronet/third_party/protobuf/src/google/protobuf/arenastring.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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