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