1 /*
2  * Copyright (c) 2009-2023, Google LLC
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *     * Redistributions of source code must retain the above copyright
8  *       notice, this list of conditions and the following disclaimer.
9  *     * Redistributions in binary form must reproduce the above copyright
10  *       notice, this list of conditions and the following disclaimer in the
11  *       documentation and/or other materials provided with the distribution.
12  *     * Neither the name of Google LLC nor the
13  *       names of its contributors may be used to endorse or promote products
14  *       derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT,
20  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #ifndef UPB_MESSAGE_ACCESSORS_INTERNAL_H_
29 #define UPB_MESSAGE_ACCESSORS_INTERNAL_H_
30 
31 #include "upb/collections/array.h"
32 #include "upb/collections/map_internal.h"
33 #include "upb/message/extension_internal.h"
34 #include "upb/message/internal.h"
35 #include "upb/mini_table/common.h"
36 #include "upb/mini_table/field_internal.h"
37 
38 // Must be last.
39 #include "upb/port/def.inc"
40 
41 #if defined(__GNUC__) && !defined(__clang__)
42 // GCC raises incorrect warnings in these functions.  It thinks that we are
43 // overrunning buffers, but we carefully write the functions in this file to
44 // guarantee that this is impossible.  GCC gets this wrong due it its failure
45 // to perform constant propagation as we expect:
46 //   - https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108217
47 //   - https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108226
48 //
49 // Unfortunately this also indicates that GCC is not optimizing away the
50 // switch() in cases where it should be, compromising the performance.
51 #pragma GCC diagnostic push
52 #pragma GCC diagnostic ignored "-Warray-bounds"
53 #pragma GCC diagnostic ignored "-Wstringop-overflow"
54 #if __GNUC__ >= 11
55 #pragma GCC diagnostic ignored "-Wstringop-overread"
56 #endif
57 #endif
58 
59 #ifdef __cplusplus
60 extern "C" {
61 #endif
62 
_upb_MiniTableField_InOneOf(const upb_MiniTableField * field)63 UPB_INLINE bool _upb_MiniTableField_InOneOf(const upb_MiniTableField* field) {
64   return field->presence < 0;
65 }
66 
_upb_MiniTableField_GetPtr(upb_Message * msg,const upb_MiniTableField * field)67 UPB_INLINE void* _upb_MiniTableField_GetPtr(upb_Message* msg,
68                                             const upb_MiniTableField* field) {
69   return (char*)msg + field->offset;
70 }
71 
_upb_MiniTableField_GetConstPtr(const upb_Message * msg,const upb_MiniTableField * field)72 UPB_INLINE const void* _upb_MiniTableField_GetConstPtr(
73     const upb_Message* msg, const upb_MiniTableField* field) {
74   return (char*)msg + field->offset;
75 }
76 
_upb_Message_SetPresence(upb_Message * msg,const upb_MiniTableField * field)77 UPB_INLINE void _upb_Message_SetPresence(upb_Message* msg,
78                                          const upb_MiniTableField* field) {
79   if (field->presence > 0) {
80     _upb_sethas_field(msg, field);
81   } else if (_upb_MiniTableField_InOneOf(field)) {
82     *_upb_oneofcase_field(msg, field) = field->number;
83   }
84 }
85 
86 // LINT.IfChange(message_raw_fields)
87 
_upb_MiniTable_ValueIsNonZero(const void * default_val,const upb_MiniTableField * field)88 UPB_INLINE bool _upb_MiniTable_ValueIsNonZero(const void* default_val,
89                                               const upb_MiniTableField* field) {
90   char zero[16] = {0};
91   switch (_upb_MiniTableField_GetRep(field)) {
92     case kUpb_FieldRep_1Byte:
93       return memcmp(&zero, default_val, 1) != 0;
94     case kUpb_FieldRep_4Byte:
95       return memcmp(&zero, default_val, 4) != 0;
96     case kUpb_FieldRep_8Byte:
97       return memcmp(&zero, default_val, 8) != 0;
98     case kUpb_FieldRep_StringView: {
99       const upb_StringView* sv = (const upb_StringView*)default_val;
100       return sv->size != 0;
101     }
102   }
103   UPB_UNREACHABLE();
104 }
105 
_upb_MiniTable_CopyFieldData(void * to,const void * from,const upb_MiniTableField * field)106 UPB_INLINE void _upb_MiniTable_CopyFieldData(void* to, const void* from,
107                                              const upb_MiniTableField* field) {
108   switch (_upb_MiniTableField_GetRep(field)) {
109     case kUpb_FieldRep_1Byte:
110       memcpy(to, from, 1);
111       return;
112     case kUpb_FieldRep_4Byte:
113       memcpy(to, from, 4);
114       return;
115     case kUpb_FieldRep_8Byte:
116       memcpy(to, from, 8);
117       return;
118     case kUpb_FieldRep_StringView: {
119       memcpy(to, from, sizeof(upb_StringView));
120       return;
121     }
122   }
123   UPB_UNREACHABLE();
124 }
125 
126 // LINT.ThenChange(//depot/google3/third_party/upb/js/impl/upb_bits/message.ts:message_raw_fields)
127 
128 UPB_INLINE size_t
_upb_MiniTable_ElementSizeLg2(const upb_MiniTableField * field)129 _upb_MiniTable_ElementSizeLg2(const upb_MiniTableField* field) {
130   const unsigned char table[] = {
131       0,
132       3,               // kUpb_FieldType_Double = 1,
133       2,               // kUpb_FieldType_Float = 2,
134       3,               // kUpb_FieldType_Int64 = 3,
135       3,               // kUpb_FieldType_UInt64 = 4,
136       2,               // kUpb_FieldType_Int32 = 5,
137       3,               // kUpb_FieldType_Fixed64 = 6,
138       2,               // kUpb_FieldType_Fixed32 = 7,
139       0,               // kUpb_FieldType_Bool = 8,
140       UPB_SIZE(3, 4),  // kUpb_FieldType_String = 9,
141       UPB_SIZE(2, 3),  // kUpb_FieldType_Group = 10,
142       UPB_SIZE(2, 3),  // kUpb_FieldType_Message = 11,
143       UPB_SIZE(3, 4),  // kUpb_FieldType_Bytes = 12,
144       2,               // kUpb_FieldType_UInt32 = 13,
145       2,               // kUpb_FieldType_Enum = 14,
146       2,               // kUpb_FieldType_SFixed32 = 15,
147       3,               // kUpb_FieldType_SFixed64 = 16,
148       2,               // kUpb_FieldType_SInt32 = 17,
149       3,               // kUpb_FieldType_SInt64 = 18,
150   };
151   return table[field->UPB_PRIVATE(descriptortype)];
152 }
153 
154 // Here we define universal getter/setter functions for message fields.
155 // These look very branchy and inefficient, but as long as the MiniTableField
156 // values are known at compile time, all the branches are optimized away and
157 // we are left with ideal code.  This can happen either through through
158 // literals or UPB_ASSUME():
159 //
160 //   // Via struct literals.
161 //   bool FooMessage_set_bool_field(const upb_Message* msg, bool val) {
162 //     const upb_MiniTableField field = {1, 0, 0, /* etc... */};
163 //     // All value in "field" are compile-time known.
164 //     _upb_Message_SetNonExtensionField(msg, &field, &value);
165 //   }
166 //
167 //   // Via UPB_ASSUME().
168 //   UPB_INLINE bool upb_Message_SetBool(upb_Message* msg,
169 //                                       const upb_MiniTableField* field,
170 //                                       bool value, upb_Arena* a) {
171 //     UPB_ASSUME(field->UPB_PRIVATE(descriptortype) == kUpb_FieldType_Bool);
172 //     UPB_ASSUME(!upb_IsRepeatedOrMap(field));
173 //     UPB_ASSUME(_upb_MiniTableField_GetRep(field) == kUpb_FieldRep_1Byte);
174 //     _upb_Message_SetField(msg, field, &value, a);
175 //   }
176 //
177 // As a result, we can use these universal getters/setters for *all* message
178 // accessors: generated code, MiniTable accessors, and reflection.  The only
179 // exception is the binary encoder/decoder, which need to be a bit more clever
180 // about how they read/write the message data, for efficiency.
181 //
182 // These functions work on both extensions and non-extensions. If the field
183 // of a setter is known to be a non-extension, the arena may be NULL and the
184 // returned bool value may be ignored since it will always succeed.
185 
_upb_Message_HasExtensionField(const upb_Message * msg,const upb_MiniTableExtension * ext)186 UPB_INLINE bool _upb_Message_HasExtensionField(
187     const upb_Message* msg, const upb_MiniTableExtension* ext) {
188   UPB_ASSERT(upb_MiniTableField_HasPresence(&ext->field));
189   return _upb_Message_Getext(msg, ext) != NULL;
190 }
191 
_upb_Message_HasNonExtensionField(const upb_Message * msg,const upb_MiniTableField * field)192 UPB_INLINE bool _upb_Message_HasNonExtensionField(
193     const upb_Message* msg, const upb_MiniTableField* field) {
194   UPB_ASSERT(upb_MiniTableField_HasPresence(field));
195   UPB_ASSUME(!upb_MiniTableField_IsExtension(field));
196   if (_upb_MiniTableField_InOneOf(field)) {
197     return _upb_getoneofcase_field(msg, field) == field->number;
198   } else {
199     return _upb_hasbit_field(msg, field);
200   }
201 }
202 
_upb_Message_GetNonExtensionField(const upb_Message * msg,const upb_MiniTableField * field,const void * default_val,void * val)203 static UPB_FORCEINLINE void _upb_Message_GetNonExtensionField(
204     const upb_Message* msg, const upb_MiniTableField* field,
205     const void* default_val, void* val) {
206   UPB_ASSUME(!upb_MiniTableField_IsExtension(field));
207   if ((_upb_MiniTableField_InOneOf(field) ||
208        _upb_MiniTable_ValueIsNonZero(default_val, field)) &&
209       !_upb_Message_HasNonExtensionField(msg, field)) {
210     _upb_MiniTable_CopyFieldData(val, default_val, field);
211     return;
212   }
213   _upb_MiniTable_CopyFieldData(val, _upb_MiniTableField_GetConstPtr(msg, field),
214                                field);
215 }
216 
_upb_Message_GetExtensionField(const upb_Message * msg,const upb_MiniTableExtension * mt_ext,const void * default_val,void * val)217 UPB_INLINE void _upb_Message_GetExtensionField(
218     const upb_Message* msg, const upb_MiniTableExtension* mt_ext,
219     const void* default_val, void* val) {
220   UPB_ASSUME(upb_MiniTableField_IsExtension(&mt_ext->field));
221   const upb_Message_Extension* ext = _upb_Message_Getext(msg, mt_ext);
222   if (ext) {
223     _upb_MiniTable_CopyFieldData(val, &ext->data, &mt_ext->field);
224   } else {
225     _upb_MiniTable_CopyFieldData(val, default_val, &mt_ext->field);
226   }
227 }
228 
_upb_Message_GetField(const upb_Message * msg,const upb_MiniTableField * field,const void * default_val,void * val)229 UPB_INLINE void _upb_Message_GetField(const upb_Message* msg,
230                                       const upb_MiniTableField* field,
231                                       const void* default_val, void* val) {
232   if (upb_MiniTableField_IsExtension(field)) {
233     _upb_Message_GetExtensionField(msg, (upb_MiniTableExtension*)field,
234                                    default_val, val);
235   } else {
236     _upb_Message_GetNonExtensionField(msg, field, default_val, val);
237   }
238 }
239 
_upb_Message_SetNonExtensionField(upb_Message * msg,const upb_MiniTableField * field,const void * val)240 UPB_INLINE void _upb_Message_SetNonExtensionField(
241     upb_Message* msg, const upb_MiniTableField* field, const void* val) {
242   UPB_ASSUME(!upb_MiniTableField_IsExtension(field));
243   _upb_Message_SetPresence(msg, field);
244   _upb_MiniTable_CopyFieldData(_upb_MiniTableField_GetPtr(msg, field), val,
245                                field);
246 }
247 
_upb_Message_SetExtensionField(upb_Message * msg,const upb_MiniTableExtension * mt_ext,const void * val,upb_Arena * a)248 UPB_INLINE bool _upb_Message_SetExtensionField(
249     upb_Message* msg, const upb_MiniTableExtension* mt_ext, const void* val,
250     upb_Arena* a) {
251   UPB_ASSERT(a);
252   upb_Message_Extension* ext =
253       _upb_Message_GetOrCreateExtension(msg, mt_ext, a);
254   if (!ext) return false;
255   _upb_MiniTable_CopyFieldData(&ext->data, val, &mt_ext->field);
256   return true;
257 }
258 
_upb_Message_SetField(upb_Message * msg,const upb_MiniTableField * field,const void * val,upb_Arena * a)259 UPB_INLINE bool _upb_Message_SetField(upb_Message* msg,
260                                       const upb_MiniTableField* field,
261                                       const void* val, upb_Arena* a) {
262   if (upb_MiniTableField_IsExtension(field)) {
263     const upb_MiniTableExtension* ext = (const upb_MiniTableExtension*)field;
264     return _upb_Message_SetExtensionField(msg, ext, val, a);
265   } else {
266     _upb_Message_SetNonExtensionField(msg, field, val);
267     return true;
268   }
269 }
270 
_upb_Message_ClearExtensionField(upb_Message * msg,const upb_MiniTableExtension * ext_l)271 UPB_INLINE void _upb_Message_ClearExtensionField(
272     upb_Message* msg, const upb_MiniTableExtension* ext_l) {
273   upb_Message_Internal* in = upb_Message_Getinternal(msg);
274   if (!in->internal) return;
275   const upb_Message_Extension* base =
276       UPB_PTR_AT(in->internal, in->internal->ext_begin, upb_Message_Extension);
277   upb_Message_Extension* ext =
278       (upb_Message_Extension*)_upb_Message_Getext(msg, ext_l);
279   if (ext) {
280     *ext = *base;
281     in->internal->ext_begin += sizeof(upb_Message_Extension);
282   }
283 }
284 
_upb_Message_ClearNonExtensionField(upb_Message * msg,const upb_MiniTableField * field)285 UPB_INLINE void _upb_Message_ClearNonExtensionField(
286     upb_Message* msg, const upb_MiniTableField* field) {
287   if (field->presence > 0) {
288     _upb_clearhas(msg, _upb_Message_Hasidx(field));
289   } else if (_upb_MiniTableField_InOneOf(field)) {
290     uint32_t* oneof_case = _upb_oneofcase_field(msg, field);
291     if (*oneof_case != field->number) return;
292     *oneof_case = 0;
293   }
294   const char zeros[16] = {0};
295   _upb_MiniTable_CopyFieldData(_upb_MiniTableField_GetPtr(msg, field), zeros,
296                                field);
297 }
298 
_upb_Message_GetOrCreateMutableMap(upb_Message * msg,const upb_MiniTableField * field,size_t key_size,size_t val_size,upb_Arena * arena)299 UPB_INLINE upb_Map* _upb_Message_GetOrCreateMutableMap(
300     upb_Message* msg, const upb_MiniTableField* field, size_t key_size,
301     size_t val_size, upb_Arena* arena) {
302   _upb_MiniTableField_CheckIsMap(field);
303   upb_Map* map = NULL;
304   upb_Map* default_map_value = NULL;
305   _upb_Message_GetNonExtensionField(msg, field, &default_map_value, &map);
306   if (!map) {
307     map = _upb_Map_New(arena, key_size, val_size);
308     // Check again due to: https://godbolt.org/z/7WfaoKG1r
309     _upb_MiniTableField_CheckIsMap(field);
310     _upb_Message_SetNonExtensionField(msg, field, &map);
311   }
312   return map;
313 }
314 
315 #ifdef __cplusplus
316 } /* extern "C" */
317 #endif
318 
319 #if defined(__GNUC__) && !defined(__clang__)
320 #pragma GCC diagnostic pop
321 #endif
322 
323 #include "upb/port/undef.inc"
324 
325 #endif  // UPB_MESSAGE_ACCESSORS_INTERNAL_H_
326