Line data Source code
1 : #ifndef HEADER_fd_src_util_encoders_fd_bincode_h
2 : #define HEADER_fd_src_util_encoders_fd_bincode_h
3 :
4 : #include "../../util/fd_util.h"
5 : #include "../../util/valloc/fd_valloc.h"
6 :
7 : typedef void
8 : (* fd_types_walk_fn_t)( void * self,
9 : void const * arg,
10 : char const * name,
11 : int type,
12 : char const * type_name,
13 : uint level );
14 :
15 : typedef void
16 : (* fd_types_walk_fn_t)( void * self,
17 : void const * arg,
18 : char const * name,
19 : int type,
20 : char const * type_name,
21 : uint level );
22 :
23 : /* Context argument used for encoding */
24 : struct fd_bincode_encode_ctx {
25 : /* Current position in data buffer */
26 : void * data;
27 : /* End of buffer */
28 : void * dataend;
29 : };
30 : typedef struct fd_bincode_encode_ctx fd_bincode_encode_ctx_t;
31 :
32 : /* Context argument used for decoding */
33 : struct fd_bincode_decode_ctx {
34 : /* Current position in data buffer */
35 : void const * data;
36 : /* End of buffer */
37 : void const * dataend;
38 : /* Wksp for laddr to gaddr conversions */
39 : /* TODO: This could be split out into a fd_bincode_global_decode_ctx */
40 : fd_wksp_t * wksp;
41 : };
42 : typedef struct fd_bincode_decode_ctx fd_bincode_decode_ctx_t;
43 :
44 793392 : #define FD_BINCODE_SUCCESS ( 0)
45 0 : #define FD_BINCODE_ERR_UNDERFLOW (-1001) /* Attempted to read past end of buffer */
46 3 : #define FD_BINCODE_ERR_OVERFLOW (-1002) /* Attempted to write past end of buffer */
47 6 : #define FD_BINCODE_ERR_ENCODING (-1003) /* Invalid encoding */
48 :
49 : #define FD_BINCODE_PRIMITIVE_STUBS( name, type ) \
50 : static inline int \
51 : fd_bincode_##name##_decode( type * self, \
52 32328 : fd_bincode_decode_ctx_t * ctx ) { \
53 32328 : uchar const * ptr = (uchar const *) ctx->data; \
54 32328 : if ( FD_UNLIKELY((void const *)(ptr + sizeof(type)) > ctx->dataend ) ) \
55 32328 : return FD_BINCODE_ERR_UNDERFLOW; \
56 32328 : memcpy( self, ptr, sizeof(type) ); /* unaligned */ \
57 32328 : ctx->data = ptr + sizeof(type); \
58 32328 : return FD_BINCODE_SUCCESS; \
59 32328 : } \
60 : static inline int \
61 156219 : fd_bincode_##name##_decode_footprint( fd_bincode_decode_ctx_t * ctx ) { \
62 156219 : uchar const * ptr = (uchar const *) ctx->data; \
63 156219 : if ( FD_UNLIKELY((void const *)(ptr + sizeof(type)) > ctx->dataend ) ) \
64 156219 : return FD_BINCODE_ERR_UNDERFLOW; \
65 156219 : ctx->data = ptr + sizeof(type); \
66 156219 : return FD_BINCODE_SUCCESS; \
67 156219 : } \
68 : static inline void \
69 : fd_bincode_##name##_decode_unsafe( type * self, \
70 332925 : fd_bincode_decode_ctx_t * ctx ) { \
71 332925 : uchar const * ptr = (uchar const *) ctx->data; \
72 332925 : memcpy( self, ptr, sizeof(type) ); /* unaligned */ \
73 332925 : ctx->data = ptr + sizeof(type); \
74 332925 : } \
75 : static inline int \
76 : fd_bincode_##name##_encode( type self, \
77 191265 : fd_bincode_encode_ctx_t * ctx ) { \
78 191265 : uchar * ptr = (uchar *)ctx->data; \
79 191265 : if ( FD_UNLIKELY((void *)(ptr + sizeof(type)) > ctx->dataend ) ) \
80 191265 : return FD_BINCODE_ERR_OVERFLOW; \
81 191265 : memcpy( ptr, &self, sizeof(type) ); /* unaligned */ \
82 191262 : ctx->data = ptr + sizeof(type); \
83 191262 : return FD_BINCODE_SUCCESS; \
84 191265 : }
85 :
86 : FD_BINCODE_PRIMITIVE_STUBS( uint8, uchar )
87 : FD_BINCODE_PRIMITIVE_STUBS( uint16, ushort )
88 : FD_BINCODE_PRIMITIVE_STUBS( uint32, uint )
89 : FD_BINCODE_PRIMITIVE_STUBS( uint64, ulong )
90 : FD_BINCODE_PRIMITIVE_STUBS( int64, long )
91 : #if FD_HAS_INT128
92 : FD_BINCODE_PRIMITIVE_STUBS( uint128, uint128 )
93 : #endif
94 : FD_BINCODE_PRIMITIVE_STUBS( double, double )
95 :
96 : static inline int
97 : fd_bincode_bool_decode( uchar * self,
98 63 : fd_bincode_decode_ctx_t * ctx ) {
99 :
100 63 : uchar const * ptr = (uchar const *)ctx->data;
101 63 : if( FD_UNLIKELY( ptr+1 > (uchar const *)ctx->dataend ) )
102 0 : return FD_BINCODE_ERR_UNDERFLOW;
103 :
104 63 : if( FD_UNLIKELY( *ptr & (~1U) ) )
105 3 : return FD_BINCODE_ERR_ENCODING;
106 :
107 60 : *self = *ptr;
108 60 : ctx->data = ptr + 1;
109 :
110 60 : return FD_BINCODE_SUCCESS;
111 63 : }
112 :
113 : static inline int
114 32010 : fd_bincode_bool_decode_footprint( fd_bincode_decode_ctx_t * ctx ) {
115 :
116 32010 : uchar const * ptr = (uchar const *)ctx->data;
117 32010 : if( FD_UNLIKELY( ptr+1 > (uchar const *)ctx->dataend ) )
118 0 : return FD_BINCODE_ERR_UNDERFLOW;
119 :
120 32010 : if( FD_UNLIKELY( *ptr & (~1U) ) )
121 3 : return FD_BINCODE_ERR_ENCODING;
122 :
123 32007 : ctx->data = ptr + 1;
124 :
125 32007 : return FD_BINCODE_SUCCESS;
126 32010 : }
127 :
128 : static inline void
129 : fd_bincode_bool_decode_unsafe( uchar * self,
130 48072 : fd_bincode_decode_ctx_t * ctx ) {
131 48072 : fd_bincode_uint8_decode_unsafe( self, ctx );
132 48072 : }
133 :
134 : static inline int
135 : fd_bincode_bool_encode( uchar self,
136 32256 : fd_bincode_encode_ctx_t * ctx ) {
137 :
138 32256 : uchar * ptr = (uchar *)ctx->data;
139 32256 : if ( FD_UNLIKELY( (void *)(ptr + 1) > ctx->dataend ) )
140 0 : return FD_BINCODE_ERR_OVERFLOW;
141 :
142 32256 : *ptr = !!self;
143 32256 : ctx->data = ptr + 1;
144 :
145 32256 : return FD_BINCODE_SUCCESS;
146 32256 : }
147 :
148 : static inline int
149 : fd_bincode_bytes_decode( uchar * self,
150 : ulong len,
151 0 : fd_bincode_decode_ctx_t * ctx ) {
152 0 : uchar * ptr = (uchar *) ctx->data;
153 0 : if ( FD_UNLIKELY((ulong)( (uchar *) ctx->dataend - ptr) < len ) ) // Get wrap-around case right
154 0 : return FD_BINCODE_ERR_UNDERFLOW;
155 0 :
156 0 : fd_memcpy(self, ptr, len);
157 0 : ctx->data = ptr + len;
158 0 :
159 0 : return FD_BINCODE_SUCCESS;
160 0 : }
161 :
162 : static inline int
163 : fd_bincode_bytes_decode_footprint( ulong len,
164 124878 : fd_bincode_decode_ctx_t * ctx ) {
165 124878 : uchar * ptr = (uchar *) ctx->data;
166 124878 : if ( FD_UNLIKELY((ulong)( (uchar *) ctx->dataend - ptr) < len ) ) { // Get wrap-around case right
167 0 : return FD_BINCODE_ERR_UNDERFLOW;
168 0 : }
169 :
170 124878 : ctx->data = ptr + len;
171 :
172 124878 : return FD_BINCODE_SUCCESS;
173 124878 : }
174 :
175 : static inline void
176 : fd_bincode_bytes_decode_unsafe( uchar * self,
177 : ulong len,
178 187176 : fd_bincode_decode_ctx_t * ctx ) {
179 187176 : uchar * ptr = (uchar *) ctx->data;
180 187176 : fd_memcpy(self, ptr, len);
181 187176 : ctx->data = ptr + len;
182 187176 : }
183 :
184 : static inline int
185 : fd_bincode_bytes_encode( uchar const * self,
186 : ulong len,
187 125466 : fd_bincode_encode_ctx_t * ctx ) {
188 125466 : uchar *ptr = (uchar *) ctx->data;
189 125466 : if ( FD_UNLIKELY((void *) (ptr + len) > ctx->dataend ) )
190 0 : return FD_BINCODE_ERR_OVERFLOW;
191 :
192 125466 : fd_memcpy(ptr, self, len);
193 125466 : ctx->data = ptr + len;
194 :
195 125466 : return FD_BINCODE_SUCCESS;
196 125466 : }
197 :
198 : /* Alternate versions of fd_cu16_dec to make the function signature more consistent with the
199 : other fd_bincode_decode functions. */
200 : static inline int
201 : fd_bincode_compact_u16_decode( ushort * self,
202 57 : fd_bincode_decode_ctx_t * ctx ) {
203 57 : const uchar * ptr = (const uchar*) ctx->data;
204 57 : if( FD_UNLIKELY( ptr==NULL ) ) {
205 0 : return FD_BINCODE_ERR_UNDERFLOW;
206 0 : }
207 :
208 57 : if( FD_LIKELY( (void *) (ptr + 1) <= ctx->dataend && !(0x80U & ptr[0]) ) ) {
209 51 : *self = (ushort)ptr[0];
210 51 : ctx->data = ptr + 1;
211 51 : return FD_BINCODE_SUCCESS;
212 51 : }
213 :
214 6 : if( FD_LIKELY( (void *) (ptr + 2) <= ctx->dataend && !(0x80U & ptr[1]) ) ) {
215 6 : if( FD_UNLIKELY( !ptr[1] ) ) /* Detect non-minimal encoding */
216 0 : return FD_BINCODE_ERR_ENCODING;
217 6 : *self = (ushort)((ulong)(ptr[0]&0x7FUL) + (((ulong)ptr[1])<<7));
218 6 : ctx->data = ptr + 2;
219 6 : return FD_BINCODE_SUCCESS;
220 6 : }
221 :
222 0 : if( FD_LIKELY( (void *) (ptr + 3) <= ctx->dataend && !(0xFCU & ptr[2]) ) ) {
223 0 : if( FD_UNLIKELY( !ptr[2] ) ) /* Detect non-minimal encoding */
224 0 : return FD_BINCODE_ERR_ENCODING;
225 0 : *self = (ushort)((ulong)(ptr[0]&0x7FUL) + (((ulong)(ptr[1]&0x7FUL))<<7) + (((ulong)ptr[2])<<14));
226 0 : ctx->data = ptr + 3;
227 0 : return FD_BINCODE_SUCCESS;
228 0 : }
229 :
230 0 : return FD_BINCODE_ERR_UNDERFLOW;
231 0 : }
232 :
233 : static inline void
234 : fd_bincode_compact_u16_decode_unsafe( ushort * self,
235 57 : fd_bincode_decode_ctx_t * ctx ) {
236 57 : const uchar * ptr = (const uchar*) ctx->data;
237 :
238 57 : if( !(0x80U & ptr[0]) ) {
239 51 : *self = (ushort)ptr[0];
240 51 : ctx->data = ptr + 1;
241 51 : return;
242 51 : }
243 :
244 6 : if( !(0x80U & ptr[1]) ) {
245 6 : *self = (ushort)((ulong)(ptr[0]&0x7FUL) + (((ulong)ptr[1])<<7));
246 6 : ctx->data = ptr + 2;
247 6 : return;
248 6 : }
249 :
250 0 : *self = (ushort)((ulong)(ptr[0]&0x7FUL) + (((ulong)(ptr[1]&0x7FUL))<<7) + (((ulong)ptr[2])<<14));
251 0 : ctx->data = ptr + 3;
252 0 : }
253 :
254 : static inline int
255 : fd_bincode_compact_u16_encode( ushort const * self,
256 12 : fd_bincode_encode_ctx_t * ctx ) {
257 12 : uchar * ptr = (uchar*) ctx->data;
258 12 : ulong val = *self;
259 :
260 12 : if ( val < 0x80UL ) {
261 12 : if ( FD_UNLIKELY((void *) (ptr + 1) > ctx->dataend ) )
262 0 : return FD_BINCODE_ERR_OVERFLOW;
263 12 : *ptr = (uchar)val;
264 12 : ctx->data = ptr + 1;
265 12 : return FD_BINCODE_SUCCESS;
266 12 : }
267 :
268 0 : else if ( val < 0x4000UL ) {
269 0 : if ( FD_UNLIKELY((void *) (ptr + 2) > ctx->dataend ) )
270 0 : return FD_BINCODE_ERR_OVERFLOW;
271 0 : ptr[0] = (uchar)((val&0x7FUL)|0x80UL);
272 0 : ptr[1] = (uchar)(val>>7);
273 0 : ctx->data = ptr + 2;
274 0 : return FD_BINCODE_SUCCESS;
275 0 : }
276 :
277 0 : else {
278 0 : if ( FD_UNLIKELY((void *) (ptr + 3) > ctx->dataend ) )
279 0 : return FD_BINCODE_ERR_OVERFLOW;
280 0 : ptr[0] = (uchar)((val&0x7FUL)|0x80UL);
281 0 : ptr[1] = (uchar)(((val>>7)&0x7FUL)|0x80UL);
282 0 : ptr[2] = (uchar)(val>>14);
283 0 : ctx->data = ptr + 3;
284 0 : return FD_BINCODE_SUCCESS;
285 0 : }
286 12 : }
287 :
288 : static inline ulong
289 0 : fd_bincode_compact_u16_size( ushort const * self ) {
290 0 : ulong val = *self;
291 :
292 0 : if ( val < 0x80UL ) {
293 0 : return 1;
294 0 : }
295 0 : else if ( val < 0x4000UL ) {
296 0 : return 2;
297 0 : }
298 0 : else {
299 0 : return 3;
300 0 : }
301 0 : }
302 :
303 : /* Decodes an integer encoded using the serde_varint algorithm:
304 : https://github.com/solana-labs/solana/blob/master/sdk/program/src/serde_varint.rs
305 :
306 : A variable number of bytes could have been used to encode the integer.
307 : The most significant bit of each byte indicates if more bytes have been used, so we keep consuming until
308 : we reach a byte where the most significant bit is 0.
309 : */
310 : static inline int
311 : fd_bincode_varint_decode( ulong * self,
312 0 : fd_bincode_decode_ctx_t * ctx ) {
313 0 : ulong out = 0UL;
314 0 : uint shift = 0U;
315 0 :
316 0 : while( FD_LIKELY( shift < 64U ) ) {
317 0 :
318 0 : if( FD_UNLIKELY( ctx->data >= ctx->dataend ) )
319 0 : return FD_BINCODE_ERR_UNDERFLOW;
320 0 :
321 0 : uint byte = *(uchar const *)ctx->data;
322 0 : ctx->data = (uchar const *)ctx->data + 1;
323 0 : out |= (byte & 0x7FUL) << shift;
324 0 :
325 0 : if( (byte & 0x80U) == 0U ) {
326 0 : if( (out>>shift) != byte )
327 0 : return FD_BINCODE_ERR_ENCODING;
328 0 : if( byte==0U && (shift!=0U || out!=0UL) )
329 0 : return FD_BINCODE_ERR_ENCODING;
330 0 : *self = out;
331 0 : return FD_BINCODE_SUCCESS;
332 0 : }
333 0 :
334 0 : shift += 7U;
335 0 :
336 0 : }
337 0 :
338 0 : return FD_BINCODE_ERR_ENCODING;
339 0 : }
340 :
341 : static inline int
342 3 : fd_bincode_varint_decode_footprint( fd_bincode_decode_ctx_t * ctx ) {
343 3 : ulong out = 0UL;
344 3 : uint shift = 0U;
345 :
346 18 : while( FD_LIKELY( shift < 64U ) ) {
347 :
348 18 : if( FD_UNLIKELY( ctx->data >= ctx->dataend ) )
349 0 : return FD_BINCODE_ERR_UNDERFLOW;
350 :
351 18 : uint byte = *(uchar const *)ctx->data;
352 18 : ctx->data = (uchar const *)ctx->data + 1;
353 18 : out |= (byte & 0x7FUL) << shift;
354 :
355 18 : if( (byte & 0x80U) == 0U ) {
356 3 : if( (out>>shift) != byte )
357 0 : return FD_BINCODE_ERR_ENCODING;
358 3 : if( byte==0U && (shift!=0U || out!=0UL) )
359 0 : return FD_BINCODE_ERR_ENCODING;
360 3 : return FD_BINCODE_SUCCESS;
361 3 : }
362 :
363 15 : shift += 7U;
364 :
365 15 : }
366 :
367 0 : return FD_BINCODE_ERR_ENCODING;
368 3 : }
369 :
370 : static inline void
371 : fd_bincode_varint_decode_unsafe( ulong * self,
372 3 : fd_bincode_decode_ctx_t * ctx ) {
373 3 : ulong out = 0UL;
374 3 : uint shift = 0U;
375 :
376 18 : for(;;) {
377 18 : uint byte = *(uchar const *)ctx->data;
378 18 : ctx->data = (uchar const *)ctx->data + 1;
379 18 : out |= (byte & 0x7FUL) << shift;
380 :
381 18 : if( (byte & 0x80U) == 0U ) {
382 3 : *self = out;
383 3 : return;
384 3 : }
385 :
386 15 : shift += 7U;
387 15 : }
388 3 : }
389 :
390 : static inline int
391 : fd_bincode_varint_encode( ulong val,
392 0 : fd_bincode_encode_ctx_t * ctx ) {
393 0 : uchar * ptr = (uchar *) ctx->data;
394 0 : while (1) {
395 0 : if ( FD_UNLIKELY((void *) (ptr + 1) > ctx->dataend ) )
396 0 : return FD_BINCODE_ERR_OVERFLOW;
397 0 : if ( val < 0x80UL ) {
398 0 : *(ptr++) = (uchar)val;
399 0 : ctx->data = ptr;
400 0 : return FD_BINCODE_SUCCESS;
401 0 : }
402 0 : *(ptr++) = (uchar)((val&0x7FUL)|0x80UL);
403 0 : val >>= 7;
404 0 : }
405 0 : }
406 :
407 : static inline ulong
408 0 : fd_bincode_varint_size( ulong val ) {
409 0 : ulong sz = 0;
410 0 : while (1) {
411 0 : if ( val < 0x80UL ) {
412 0 : return sz+1;
413 0 : }
414 0 : sz++;
415 0 : val >>= 7;
416 0 : }
417 0 : }
418 :
419 : enum {
420 : /* All meta tags must fit in 6 bits */
421 :
422 : /* Primitive types with an implicit encoding length */
423 : FD_ARCHIVE_META_CHAR = 0x1,
424 : FD_ARCHIVE_META_STRING = 0x2,
425 : FD_ARCHIVE_META_CHAR32 = 0x3,
426 : FD_ARCHIVE_META_DOUBLE = 0x4,
427 : FD_ARCHIVE_META_LONG = 0x5,
428 : FD_ARCHIVE_META_UINT = 0x6,
429 : FD_ARCHIVE_META_UINT128 = 0x7,
430 : FD_ARCHIVE_META_BOOL = 0x8,
431 : FD_ARCHIVE_META_UCHAR = 0x9,
432 : FD_ARCHIVE_META_UCHAR32 = 0xa,
433 : FD_ARCHIVE_META_UCHAR128 = 0xb,
434 : FD_ARCHIVE_META_UCHAR2048 = 0xc,
435 : FD_ARCHIVE_META_ULONG = 0xd,
436 : FD_ARCHIVE_META_USHORT = 0xe,
437 :
438 : /* Meta types which have an encoding length after the short tag */
439 : FD_ARCHIVE_META_STRUCT = 0x21,
440 : FD_ARCHIVE_META_VECTOR = 0x22,
441 : FD_ARCHIVE_META_DEQUE = 0x23,
442 : FD_ARCHIVE_META_MAP = 0x24,
443 : FD_ARCHIVE_META_TREAP = 0x25,
444 : FD_ARCHIVE_META_OPTION = 0x26,
445 : FD_ARCHIVE_META_ARRAY = 0x27,
446 : FD_ARCHIVE_META_STATIC_VECTOR = 0x28,
447 : };
448 :
449 : #define FD_ARCHIVE_META_SENTINAL (ushort)0 /* End of structure */
450 :
451 0 : static inline int fd_archive_encode_setup_length( fd_bincode_encode_ctx_t * ctx, void ** offset_out ) {
452 0 : uchar * ptr = (uchar *)ctx->data;
453 0 : if ( FD_UNLIKELY((void *)(ptr + sizeof(uint)) > ctx->dataend ) )
454 0 : return FD_BINCODE_ERR_OVERFLOW;
455 0 : /* Skip over length for now but make space for it */
456 0 : *offset_out = ptr;
457 0 : ctx->data = ptr + sizeof(uint);
458 0 : return FD_BINCODE_SUCCESS;
459 0 : }
460 :
461 0 : static inline int fd_archive_encode_set_length( fd_bincode_encode_ctx_t * ctx, void * offset ) {
462 0 : *(uint *)offset = (uint)((uchar *)ctx->data - ((uchar *)offset + sizeof(uint)));
463 0 : return FD_BINCODE_SUCCESS;
464 0 : }
465 :
466 0 : static inline int fd_archive_decode_setup_length( fd_bincode_decode_ctx_t * ctx, void ** offset_out ) {
467 0 : uchar * ptr = (uchar *)ctx->data;
468 0 : if ( FD_UNLIKELY((void *)(ptr + sizeof(uint)) > ctx->dataend ) )
469 0 : return FD_BINCODE_ERR_UNDERFLOW;
470 0 : /* Skip over length for now and verify it later */
471 0 : *offset_out = ptr;
472 0 : ctx->data = ptr + sizeof(uint);
473 0 : return FD_BINCODE_SUCCESS;
474 0 : }
475 :
476 0 : static inline int fd_archive_decode_check_length( fd_bincode_decode_ctx_t * ctx, void * offset ) {
477 0 : if( *(uint *)offset != (uint)((uchar *)ctx->data - ((uchar *)offset + sizeof(uint))) )
478 0 : return FD_BINCODE_ERR_ENCODING;
479 0 : return FD_BINCODE_SUCCESS;
480 0 : }
481 :
482 : #endif /* HEADER_fd_src_util_encoders_fd_bincode_h */
|