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