protobuf/src/google/protobuf/inlined_string_field.h
Go to the documentation of this file.
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 #ifndef GOOGLE_PROTOBUF_INLINED_STRING_FIELD_H__
32 #define GOOGLE_PROTOBUF_INLINED_STRING_FIELD_H__
33 
34 #include <string>
35 #include <utility>
36 
37 #include <google/protobuf/stubs/logging.h>
38 #include <google/protobuf/stubs/common.h>
39 #include <google/protobuf/arenastring.h>
40 #include <google/protobuf/message_lite.h>
41 #include <google/protobuf/port.h>
42 #include <google/protobuf/stubs/strutil.h>
43 
44 // Must be included last.
45 #include <google/protobuf/port_def.inc>
46 
47 #ifdef SWIG
48 #error "You cannot SWIG proto headers"
49 #endif
50 
51 namespace google {
52 namespace protobuf {
53 
54 class Arena;
55 
56 namespace internal {
57 
58 // InlinedStringField wraps a std::string instance and exposes an API similar to
59 // ArenaStringPtr's wrapping of a std::string* instance.
60 //
61 // default_value parameters are taken for consistency with ArenaStringPtr, but
62 // are not used for most methods. With inlining, these should be removed from
63 // the generated binary.
64 //
65 // InlinedStringField has a donating mechanism that allows string buffer
66 // allocated on arena. A string is donated means both the string container and
67 // the data buffer are on arena. The donating mechanism here is similar to the
68 // one in ArenaStringPtr with some differences:
69 //
70 // When an InlinedStringField is constructed, the donating state is true. This
71 // is because the string container is directly stored in the message on the
72 // arena:
73 //
74 // Construction: donated=true
75 // Arena:
76 // +-----------------------+
77 // |Message foo: |
78 // | +-------------------+ |
79 // | |InlinedStringField:| |
80 // | | +-----+ | |
81 // | | | | | | | |
82 // | | +-----+ | |
83 // | +-------------------+ |
84 // +-----------------------+
85 //
86 // When lvalue Set is called, the donating state is still true. String data will
87 // be allocated on the arena:
88 //
89 // Lvalue Set: donated=true
90 // Arena:
91 // +-----------------------+
92 // |Message foo: |
93 // | +-------------------+ |
94 // | |InlinedStringField:| |
95 // | | +-----+ | |
96 // | | | | | | | |
97 // | | +|----+ | |
98 // | +--|----------------+ |
99 // | V |
100 // | +----------------+ |
101 // | |'f','o','o',... | |
102 // | +----------------+ |
103 // +-----------------------+
104 //
105 // Some operations will undonate a donated string, including: Mutable,
106 // SetAllocated, Rvalue Set, and Swap with a non-donated string.
107 //
108 // For more details of the donating states transitions, go/pd-inlined-string.
109 class PROTOBUF_EXPORT InlinedStringField {
110  public:
112  inline void Init() { new (get_mutable()) std::string(); }
113  // Add the dummy parameter just to make InlinedStringField(nullptr)
114  // unambiguous.
116  const ExplicitlyConstructed<std::string>* /*default_value*/,
117  bool /*dummy*/)
118  : value_{} {}
119  explicit InlinedStringField(const std::string& default_value);
120  explicit InlinedStringField(Arena* arena);
122 
123  // Lvalue Set. To save space, we pack the donating states of multiple
124  // InlinedStringFields into an uint32_t `donating_states`. The `mask`
125  // indicates the position of the bit for this InlinedStringField. `donated` is
126  // whether this field is donated.
127  //
128  // The caller should guarantee that:
129  //
130  // `donated == ((donating_states & ~mask) != 0)`
131  //
132  // This method never changes the `donating_states`.
133  void Set(const std::string* default_value, ConstStringParam value,
134  Arena* arena, bool donated, uint32_t* /*donating_states*/,
135  uint32_t /*mask*/) {
136  (void)arena;
137  (void)donated;
138  SetNoArena(default_value, value);
139  }
140 
141  // Rvalue Set. If this field is donated, this method will undonate this field
142  // by mutating the `donating_states` according to `mask`.
143  void Set(const std::string* default_value, std::string&& value, Arena* arena,
144  bool donated, uint32_t* donating_states, uint32_t mask);
145 
146  template <typename FirstParam>
147  void Set(FirstParam p1, const char* str, ::google::protobuf::Arena* arena, bool donated,
148  uint32_t* donating_states, uint32_t mask) {
149  Set(p1, ConstStringParam(str), arena, donated, donating_states, mask);
150  }
151 
152  template <typename FirstParam>
153  void Set(FirstParam p1, const char* str, size_t size, ::google::protobuf::Arena* arena,
154  bool donated, uint32_t* donating_states, uint32_t mask) {
155  ConstStringParam sp{str, size}; // for string_view and `const string &`
156  Set(p1, sp, arena, donated, donating_states, mask);
157  }
158 
159  template <typename FirstParam, typename RefWrappedType>
160  void Set(FirstParam p1,
161  std::reference_wrapper<RefWrappedType> const_string_ref,
162  ::google::protobuf::Arena* arena, bool donated, uint32_t* donating_states,
163  uint32_t mask) {
164  Set(p1, const_string_ref.get(), arena, donated, donating_states, mask);
165  }
166 
167  template <typename FirstParam, typename SecondParam>
168  void SetBytes(FirstParam p1, SecondParam&& p2, ::google::protobuf::Arena* arena,
169  bool donated, uint32_t* donating_states, uint32_t mask) {
170  Set(p1, static_cast<SecondParam&&>(p2), arena, donated, donating_states,
171  mask);
172  }
173 
174  template <typename FirstParam>
175  void SetBytes(FirstParam p1, const void* str, size_t size,
176  ::google::protobuf::Arena* arena, bool donated, uint32_t* donating_states,
177  uint32_t mask) {
178  // Must work whether ConstStringParam is string_view or `const string &`
179  ConstStringParam sp{static_cast<const char*>(str), size};
180  Set(p1, sp, arena, donated, donating_states, mask);
181  }
182 
183  PROTOBUF_NDEBUG_INLINE void SetNoArena(const std::string* default_value,
185  PROTOBUF_NDEBUG_INLINE void SetNoArena(const std::string* default_value,
186  std::string&& value);
187 
188  // Basic accessors.
189  PROTOBUF_NDEBUG_INLINE const std::string& Get() const { return GetNoArena(); }
190  PROTOBUF_NDEBUG_INLINE const std::string& GetNoArena() const;
191 
192  // Mutable returns a std::string* instance that is heap-allocated. If this
193  // field is donated, this method undonates this field by mutating the
194  // `donating_states` according to `mask`, and copies the content of the
195  // original string to the returning string.
196  std::string* Mutable(const LazyString& default_value, Arena* arena,
197  bool donated, uint32_t* donating_states, uint32_t mask);
198  std::string* Mutable(ArenaStringPtr::EmptyDefault, Arena* arena, bool donated,
199  uint32_t* donating_states, uint32_t mask);
200 
201  // Release returns a std::string* instance that is heap-allocated and is not
202  // Own()'d by any arena. If the field is not set, this returns nullptr. The
203  // caller retains ownership. Clears this field back to nullptr state. Used to
204  // implement release_<field>() methods on generated classes.
205  PROTOBUF_NODISCARD std::string* Release(const std::string* default_value,
206  Arena* arena, bool donated);
207  PROTOBUF_NODISCARD std::string* ReleaseNonDefault(
208  const std::string* default_value, Arena* arena);
209  std::string* ReleaseNonDefaultNoArena(const std::string* default_value);
210 
211  // Takes a std::string that is heap-allocated, and takes ownership. The
212  // std::string's destructor is registered with the arena. Used to implement
213  // set_allocated_<field> in generated classes.
214  //
215  // If this field is donated, this method undonates this field by mutating the
216  // `donating_states` according to `mask`.
217  void SetAllocated(const std::string* default_value, std::string* value,
218  Arena* arena, bool donated, uint32_t* donating_states,
219  uint32_t mask);
220 
221  void SetAllocatedNoArena(const std::string* default_value,
222  std::string* value);
223 
224  // When one of `this` and `from` is donated and the other is not donated, this
225  // method will undonate the donated one and swap the two heap-allocated
226  // strings.
227  PROTOBUF_NDEBUG_INLINE void Swap(InlinedStringField* from,
228  const std::string* default_value,
229  Arena* arena, bool donated,
230  bool from_donated, uint32_t* donating_states,
231  uint32_t* from_donating_states,
232  uint32_t mask);
233 
234  // Frees storage (if not on an arena).
235  PROTOBUF_NDEBUG_INLINE void Destroy(const std::string* default_value,
236  Arena* arena) {
237  if (arena == nullptr) {
238  DestroyNoArena(default_value);
239  }
240  }
241  PROTOBUF_NDEBUG_INLINE void DestroyNoArena(const std::string* default_value);
242 
243  // Clears content, but keeps allocated std::string, to avoid the overhead of
244  // heap operations. After this returns, the content (as seen by the user) will
245  // always be the empty std::string.
246  PROTOBUF_NDEBUG_INLINE void ClearToEmpty() { ClearNonDefaultToEmpty(); }
247  PROTOBUF_NDEBUG_INLINE void ClearNonDefaultToEmpty() {
248  get_mutable()->clear();
249  }
250 
251  // Clears content, but keeps allocated std::string if arena != nullptr, to
252  // avoid the overhead of heap operations. After this returns, the content (as
253  // seen by the user) will always be equal to |default_value|.
254  void ClearToDefault(const LazyString& default_value, Arena* arena,
255  bool donated);
256 
257  // Returns a mutable pointer, but doesn't initialize the string to the
258  // default value.
259  PROTOBUF_NDEBUG_INLINE std::string* MutableNoArenaNoDefault(
260  const std::string* /*default_value*/);
261 
262  // Generated code / reflection only! Returns a mutable pointer to the string.
263  PROTOBUF_NDEBUG_INLINE std::string* UnsafeMutablePointer();
264 
265  // InlinedStringField doesn't have things like the `default_value` pointer in
266  // ArenaStringPtr.
267  bool IsDefault(const std::string* /*default_value*/) const { return false; }
268 
269  private:
270  void Destruct() { get_mutable()->~basic_string(); }
271 
272  PROTOBUF_NDEBUG_INLINE std::string* get_mutable();
273  PROTOBUF_NDEBUG_INLINE const std::string* get_const() const;
274 
275  alignas(std::string) char value_[sizeof(std::string)];
276 
277  std::string* MutableSlow(::google::protobuf::Arena* arena, bool donated,
278  uint32_t* donating_states, uint32_t mask);
279 
280 
281  // When constructed in an Arena, we want our destructor to be skipped.
284  typedef void DestructorSkippable_;
285 };
286 
288  return reinterpret_cast<std::string*>(&value_);
289 }
290 
292  return reinterpret_cast<const std::string*>(&value_);
293 }
294 
296  const std::string& default_value) {
297  new (get_mutable()) std::string(default_value);
298 }
299 
301  Init();
302  if (arena != nullptr) {
303  arena->OwnDestructor(get_mutable());
304  }
305 }
306 
307 inline const std::string& InlinedStringField::GetNoArena() const {
308  return *get_const();
309 }
310 
312  const std::string* /*default_value*/, std::string* value) {
313  if (value == nullptr) {
314  // Currently, inlined string field can't have non empty default.
315  get_mutable()->clear();
316  } else {
317  get_mutable()->assign(std::move(*value));
318  delete value;
319  }
320 }
321 
323  // This is invoked from the generated message's ArenaDtor, which is used to
324  // clean up objects not allocated on the Arena.
325  this->~InlinedStringField();
326 }
327 
329  const std::string* /*default_value*/) {
330  // Currently, inlined string field can't have non empty default.
331  auto* released = new std::string();
332  get_mutable()->swap(*released);
333  return released;
334 }
335 
336 inline void InlinedStringField::SetNoArena(const std::string* /*default_value*/,
337  StringPiece value) {
338  get_mutable()->assign(value.data(), value.length());
339 }
340 
341 inline void InlinedStringField::SetNoArena(const std::string* /*default_value*/,
342  std::string&& value) {
343  get_mutable()->assign(std::move(value));
344 }
345 
347  InlinedStringField* from, const std::string* /*default_value*/,
348  Arena* arena, bool donated, bool from_donated, uint32_t* donating_states,
349  uint32_t* from_donating_states, uint32_t mask) {
350 #if GOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL_INLINE
351  // If one is donated and the other is not, undonate the donated one.
352  if (donated && !from_donated) {
353  MutableSlow(arena, donated, donating_states, mask);
354  } else if (!donated && from_donated) {
355  from->MutableSlow(arena, from_donated, from_donating_states, mask);
356  }
357  // Then, swap the two undonated strings.
358 #else
359  (void)arena;
360  (void)donated;
361  (void)from_donated;
362  (void)donating_states;
363  (void)from_donating_states;
364  (void)mask;
365 #endif
366  get_mutable()->swap(*from->get_mutable());
367 }
368 
370  const std::string*) {
371  return get_mutable();
372 }
373 
375  return get_mutable();
376 }
377 
378 } // namespace internal
379 } // namespace protobuf
380 } // namespace google
381 
382 #include <google/protobuf/port_undef.inc>
383 
384 #endif // GOOGLE_PROTOBUF_INLINED_STRING_FIELD_H__
google::protobuf.internal::InlinedStringField::UnsafeMutablePointer
std::string * UnsafeMutablePointer()
Definition: bloaty/third_party/protobuf/src/google/protobuf/inlined_string_field.h:231
xds_interop_client.str
str
Definition: xds_interop_client.py:487
Arena
struct Arena Arena
Definition: third_party/bloaty/third_party/protobuf/src/google/protobuf/arena.h:189
absl::swap_internal::Swap
void Swap(T &lhs, T &rhs) noexcept(IsNothrowSwappable< T >::value)
Definition: abseil-cpp/absl/meta/type_traits.h:772
google::protobuf::value
const Descriptor::ReservedRange value
Definition: bloaty/third_party/protobuf/src/google/protobuf/descriptor.h:1954
google::protobuf.internal::InlinedStringField::~InlinedStringField
~InlinedStringField()
Definition: protobuf/src/google/protobuf/inlined_string_field.h:121
Arena
Definition: arena.c:39
testing::internal::string
::std::string string
Definition: bloaty/third_party/protobuf/third_party/googletest/googletest/include/gtest/internal/gtest-port.h:881
google::protobuf
Definition: bloaty/third_party/protobuf/benchmarks/util/data_proto2_to_proto3_util.h:12
google::protobuf.internal::InlinedStringField::DestructorSkippable_
void DestructorSkippable_
Definition: protobuf/src/google/protobuf/inlined_string_field.h:284
google::protobuf::python::cmessage::Init
static int Init(CMessage *self, PyObject *args, PyObject *kwargs)
Definition: bloaty/third_party/protobuf/python/google/protobuf/pyext/message.cc:1287
google::protobuf.internal::InlinedStringField::GetNoArena
const std::string & GetNoArena() const PROTOBUF_ALWAYS_INLINE
Definition: bloaty/third_party/protobuf/src/google/protobuf/inlined_string_field.h:172
arena
grpc_core::ScopedArenaPtr arena
Definition: binder_transport_test.cc:237
google::protobuf.internal::InlinedStringField::Set
void Set(FirstParam p1, const char *str, size_t size, ::google::protobuf::Arena *arena, bool donated, uint32_t *donating_states, uint32_t mask)
Definition: protobuf/src/google/protobuf/inlined_string_field.h:153
google::protobuf.internal::InlinedStringField::DestroyNoArena
void DestroyNoArena(const std::string *default_value) PROTOBUF_ALWAYS_INLINE
Definition: bloaty/third_party/protobuf/src/google/protobuf/inlined_string_field.h:190
uint32_t
unsigned int uint32_t
Definition: stdint-msvc2008.h:80
from
size_t from
Definition: abseil-cpp/absl/container/internal/layout_test.cc:1384
google::protobuf.internal::InlinedStringField::Get
const PROTOBUF_NDEBUG_INLINE std::string & Get() const
Definition: protobuf/src/google/protobuf/inlined_string_field.h:189
google::protobuf.internal::InlinedStringField::Destruct
void Destruct()
Definition: protobuf/src/google/protobuf/inlined_string_field.h:270
google::protobuf::ConstStringParam
const std::string & ConstStringParam
Definition: third_party/protobuf/src/google/protobuf/stubs/port.h:129
google::protobuf.internal::InlinedStringField::InlinedStringField
constexpr InlinedStringField(const ExplicitlyConstructed< std::string > *, bool)
Definition: protobuf/src/google/protobuf/inlined_string_field.h:115
absl::move
constexpr absl::remove_reference_t< T > && move(T &&t) noexcept
Definition: abseil-cpp/absl/utility/utility.h:221
google::protobuf.internal::InlinedStringField::value_
std::string value_
Definition: bloaty/third_party/protobuf/src/google/protobuf/inlined_string_field.h:159
google::protobuf.internal::LazyString
Definition: protobuf/src/google/protobuf/arenastring.h:61
google::protobuf.internal::InlinedStringField::IsDefault
bool IsDefault(const std::string *) const
Definition: protobuf/src/google/protobuf/inlined_string_field.h:267
google::protobuf.internal::InlinedStringField::ClearToEmpty
PROTOBUF_NDEBUG_INLINE void ClearToEmpty()
Definition: protobuf/src/google/protobuf/inlined_string_field.h:246
google::protobuf::StringPiece
Definition: bloaty/third_party/protobuf/src/google/protobuf/stubs/stringpiece.h:180
google::protobuf.internal::InlinedStringField::Set
void Set(FirstParam p1, std::reference_wrapper< RefWrappedType > const_string_ref, ::google::protobuf::Arena *arena, bool donated, uint32_t *donating_states, uint32_t mask)
Definition: protobuf/src/google/protobuf/inlined_string_field.h:160
google::protobuf.internal::InlinedStringField::SetBytes
void SetBytes(FirstParam p1, const void *str, size_t size, ::google::protobuf::Arena *arena, bool donated, uint32_t *donating_states, uint32_t mask)
Definition: protobuf/src/google/protobuf/inlined_string_field.h:175
google::protobuf.internal::InlinedStringField::SetNoArena
void SetNoArena(const std::string *default_value, StringPiece value) PROTOBUF_ALWAYS_INLINE
Definition: bloaty/third_party/protobuf/src/google/protobuf/inlined_string_field.h:212
google::protobuf.internal::InlinedStringField::InternalArenaConstructable_
void InternalArenaConstructable_
Definition: protobuf/src/google/protobuf/inlined_string_field.h:283
google::protobuf.internal::InlinedStringField::Swap
void Swap(InlinedStringField *from) PROTOBUF_ALWAYS_INLINE
Definition: bloaty/third_party/protobuf/src/google/protobuf/inlined_string_field.h:227
google::protobuf.internal::InlinedStringField
Definition: bloaty/third_party/protobuf/src/google/protobuf/inlined_string_field.h:62
value_
int value_
Definition: orphanable_test.cc:38
google::protobuf.internal::InlinedStringField::MutableSlow
std::string * MutableSlow(::google::protobuf::Arena *arena, bool donated, uint32_t *donating_states, uint32_t mask)
Definition: inlined_string_field.cc:68
google::protobuf.internal::InlinedStringField::Set
void Set(const std::string *default_value, ConstStringParam value, Arena *arena, bool donated, uint32_t *, uint32_t)
Definition: protobuf/src/google/protobuf/inlined_string_field.h:133
google::protobuf.internal::InlinedStringField::Destroy
PROTOBUF_NDEBUG_INLINE void Destroy(const std::string *default_value, Arena *arena)
Definition: protobuf/src/google/protobuf/inlined_string_field.h:235
grpc_core::Destruct
void Destruct(T *p)
Definition: construct_destruct.h:27
google::protobuf.internal::InlinedStringField::get_mutable
PROTOBUF_NDEBUG_INLINE std::string * get_mutable()
Definition: protobuf/src/google/protobuf/inlined_string_field.h:287
google::protobuf.internal::InlinedStringField::MutableNoArenaNoDefault
PROTOBUF_NDEBUG_INLINE std::string * MutableNoArenaNoDefault(const std::string *)
Definition: protobuf/src/google/protobuf/inlined_string_field.h:369
google::protobuf.internal::InlinedStringField::get_const
const PROTOBUF_NDEBUG_INLINE std::string * get_const() const
Definition: protobuf/src/google/protobuf/inlined_string_field.h:291
google::protobuf.internal::InlinedStringField::SetAllocatedNoArena
void SetAllocatedNoArena(const std::string *default_value, std::string *value)
Definition: bloaty/third_party/protobuf/src/google/protobuf/inlined_string_field.h:180
google::protobuf.internal::InlinedStringField::Init
void Init()
Definition: protobuf/src/google/protobuf/inlined_string_field.h:112
google::protobuf.internal::InlinedStringField::InlinedStringField
InlinedStringField() PROTOBUF_ALWAYS_INLINE
Definition: bloaty/third_party/protobuf/src/google/protobuf/inlined_string_field.h:162
google::protobuf.internal::ArenaStringPtr::EmptyDefault
Definition: protobuf/src/google/protobuf/arenastring.h:187
internal
Definition: benchmark/test/output_test_helper.cc:20
google::protobuf.internal::ExplicitlyConstructed< std::string >
size
voidpf void uLong size
Definition: bloaty/third_party/zlib/contrib/minizip/ioapi.h:136
google::protobuf.internal::InlinedStringField::SetBytes
void SetBytes(FirstParam p1, SecondParam &&p2, ::google::protobuf::Arena *arena, bool donated, uint32_t *donating_states, uint32_t mask)
Definition: protobuf/src/google/protobuf/inlined_string_field.h:168
google::protobuf.internal::InlinedStringField::ReleaseNonDefaultNoArena
std::string * ReleaseNonDefaultNoArena(const std::string *default_value)
Definition: bloaty/third_party/protobuf/src/google/protobuf/inlined_string_field.h:205
google::protobuf.internal::InlinedStringField::InlinedStringField
InlinedStringField()
Definition: protobuf/src/google/protobuf/inlined_string_field.h:111
google
Definition: bloaty/third_party/protobuf/benchmarks/util/data_proto2_to_proto3_util.h:11
google::protobuf.internal::InlinedStringField::ClearNonDefaultToEmpty
PROTOBUF_NDEBUG_INLINE void ClearNonDefaultToEmpty()
Definition: protobuf/src/google/protobuf/inlined_string_field.h:247
google::protobuf.internal::InlinedStringField::Set
void Set(FirstParam p1, const char *str, ::google::protobuf::Arena *arena, bool donated, uint32_t *donating_states, uint32_t mask)
Definition: protobuf/src/google/protobuf/inlined_string_field.h:147


grpc
Author(s):
autogenerated on Fri May 16 2025 02:59:03