Line data Source code
1 : #ifndef HEADER_fd_src_flamenco_types_fd_bincode_h
2 : #define HEADER_fd_src_flamenco_types_fd_bincode_h
3 :
4 : #include "../../util/fd_util.h"
5 :
6 : /* Context argument used for encoding */
7 : struct fd_bincode_encode_ctx {
8 : /* Current position in data buffer */
9 : void * data;
10 : /* End of buffer */
11 : void * dataend;
12 : };
13 : typedef struct fd_bincode_encode_ctx fd_bincode_encode_ctx_t;
14 :
15 : /* Context argument used for decoding */
16 : struct fd_bincode_decode_ctx {
17 : /* Current position in data buffer */
18 : void const * data;
19 : /* End of buffer */
20 : void const * dataend;
21 : };
22 : typedef struct fd_bincode_decode_ctx fd_bincode_decode_ctx_t;
23 :
24 18723112 : #define FD_BINCODE_SUCCESS ( 0)
25 1795 : #define FD_BINCODE_ERR_UNDERFLOW (-1001) /* Attempted to read past end of buffer */
26 7178 : #define FD_BINCODE_ERR_OVERFLOW (-1002) /* Attempted to write past end of buffer */
27 8046 : #define FD_BINCODE_ERR_ENCODING (-1003) /* Invalid encoding */
28 :
29 : #define FD_BINCODE_PRIMITIVE_STUBS( name, type ) \
30 : static inline int \
31 : fd_bincode_##name##_decode( type * self, \
32 147179 : fd_bincode_decode_ctx_t * ctx ) { \
33 147179 : uchar const * ptr = (uchar const *) ctx->data; \
34 147179 : if ( FD_UNLIKELY((void const *)(ptr + sizeof(type)) > ctx->dataend ) ) \
35 147179 : return FD_BINCODE_ERR_UNDERFLOW; \
36 147179 : memcpy( self, ptr, sizeof(type) ); /* unaligned */ \
37 146562 : ctx->data = ptr + sizeof(type); \
38 146562 : return FD_BINCODE_SUCCESS; \
39 147179 : } \
40 : static inline int \
41 12933805 : fd_bincode_##name##_decode_footprint( fd_bincode_decode_ctx_t * ctx ) { \
42 12933805 : uchar const * ptr = (uchar const *) ctx->data; \
43 12933805 : if ( FD_UNLIKELY((void const *)(ptr + sizeof(type)) > ctx->dataend ) ) \
44 12933805 : return FD_BINCODE_ERR_UNDERFLOW; \
45 12933805 : ctx->data = ptr + sizeof(type); \
46 12933634 : return FD_BINCODE_SUCCESS; \
47 12933805 : } \
48 : static inline void \
49 : fd_bincode_##name##_decode_unsafe( type * self, \
50 29968885 : fd_bincode_decode_ctx_t * ctx ) { \
51 29968885 : uchar const * ptr = (uchar const *) ctx->data; \
52 29968885 : memcpy( self, ptr, sizeof(type) ); /* unaligned */ \
53 29968885 : ctx->data = ptr + sizeof(type); \
54 29968885 : } \
55 : static inline int \
56 : fd_bincode_##name##_encode( type self, \
57 5044375 : fd_bincode_encode_ctx_t * ctx ) { \
58 5044375 : uchar * ptr = (uchar *)ctx->data; \
59 5044375 : if ( FD_UNLIKELY((void *)(ptr + sizeof(type)) > ctx->dataend ) ) \
60 5044375 : return FD_BINCODE_ERR_OVERFLOW; \
61 5044375 : memcpy( ptr, &self, sizeof(type) ); /* unaligned */ \
62 5044375 : ctx->data = ptr + sizeof(type); \
63 5044375 : return FD_BINCODE_SUCCESS; \
64 5044375 : }
65 :
66 : /* fd_w_u128 is a wrapped "uint128" type providing basic 128-bit
67 : unsigned int functionality to fd_types, even if the compile target
68 : does not natively support uint128. */
69 :
70 : union __attribute__((aligned(16))) fd_w_u128 {
71 : uchar uc[16];
72 : ulong ul[2];
73 : # if FD_HAS_INT128
74 : uint128 ud;
75 : # endif
76 : };
77 :
78 : typedef union fd_w_u128 fd_w_u128_t;
79 :
80 : FD_BINCODE_PRIMITIVE_STUBS( uint8, uchar )
81 : FD_BINCODE_PRIMITIVE_STUBS( uint16, ushort )
82 : FD_BINCODE_PRIMITIVE_STUBS( uint32, uint )
83 : FD_BINCODE_PRIMITIVE_STUBS( uint64, ulong )
84 : FD_BINCODE_PRIMITIVE_STUBS( int64, long )
85 : FD_BINCODE_PRIMITIVE_STUBS( uint128, fd_w_u128_t )
86 : FD_BINCODE_PRIMITIVE_STUBS( double, double )
87 :
88 : static inline int
89 : fd_bincode_bool_decode( uchar * self,
90 28473 : fd_bincode_decode_ctx_t * ctx ) {
91 :
92 28473 : uchar const * ptr = (uchar const *)ctx->data;
93 28473 : if( FD_UNLIKELY( ptr+1 > (uchar const *)ctx->dataend ) )
94 201 : return FD_BINCODE_ERR_UNDERFLOW;
95 :
96 28272 : if( FD_UNLIKELY( *ptr & (~1U) ) )
97 3444 : return FD_BINCODE_ERR_ENCODING;
98 :
99 24828 : *self = *ptr;
100 24828 : ctx->data = ptr + 1;
101 :
102 24828 : return FD_BINCODE_SUCCESS;
103 28272 : }
104 :
105 : static inline int
106 35165 : fd_bincode_bool_decode_footprint( fd_bincode_decode_ctx_t * ctx ) {
107 :
108 35165 : uchar const * ptr = (uchar const *)ctx->data;
109 35165 : if( FD_UNLIKELY( ptr+1 > (uchar const *)ctx->dataend ) )
110 9 : return FD_BINCODE_ERR_UNDERFLOW;
111 :
112 35156 : if( FD_UNLIKELY( *ptr & (~1U) ) )
113 17 : return FD_BINCODE_ERR_ENCODING;
114 :
115 35139 : ctx->data = ptr + 1;
116 :
117 35139 : return FD_BINCODE_SUCCESS;
118 35156 : }
119 :
120 : static inline void
121 : fd_bincode_bool_decode_unsafe( uchar * self,
122 58356 : fd_bincode_decode_ctx_t * ctx ) {
123 58356 : fd_bincode_uint8_decode_unsafe( self, ctx );
124 58356 : }
125 :
126 : static inline int
127 : fd_bincode_bool_encode( uchar self,
128 1028 : fd_bincode_encode_ctx_t * ctx ) {
129 :
130 1028 : uchar * ptr = (uchar *)ctx->data;
131 1028 : if ( FD_UNLIKELY( (void *)(ptr + 1) > ctx->dataend ) )
132 0 : return FD_BINCODE_ERR_OVERFLOW;
133 :
134 1028 : *ptr = !!self;
135 1028 : ctx->data = ptr + 1;
136 :
137 1028 : return FD_BINCODE_SUCCESS;
138 1028 : }
139 :
140 : static inline int
141 : fd_bincode_bytes_decode( uchar * self,
142 : ulong len,
143 0 : fd_bincode_decode_ctx_t * ctx ) {
144 0 : uchar * ptr = (uchar *) ctx->data;
145 0 : if ( FD_UNLIKELY((ulong)( (uchar *) ctx->dataend - ptr) < len ) ) // Get wrap-around case right
146 0 : return FD_BINCODE_ERR_UNDERFLOW;
147 0 :
148 0 : fd_memcpy(self, ptr, len);
149 0 : ctx->data = ptr + len;
150 0 :
151 0 : return FD_BINCODE_SUCCESS;
152 0 : }
153 :
154 : static inline int
155 : fd_bincode_bytes_decode_footprint( ulong len,
156 79595 : fd_bincode_decode_ctx_t * ctx ) {
157 79595 : uchar * ptr = (uchar *) ctx->data;
158 79595 : if ( FD_UNLIKELY((ulong)( (uchar *) ctx->dataend - ptr) < len ) ) { // Get wrap-around case right
159 651 : return FD_BINCODE_ERR_UNDERFLOW;
160 651 : }
161 :
162 78944 : ctx->data = ptr + len;
163 :
164 78944 : return FD_BINCODE_SUCCESS;
165 79595 : }
166 :
167 : static inline void
168 : fd_bincode_bytes_decode_unsafe( uchar * self,
169 : ulong len,
170 5369748 : fd_bincode_decode_ctx_t * ctx ) {
171 5369748 : uchar * ptr = (uchar *) ctx->data;
172 5369748 : fd_memcpy(self, ptr, len);
173 5369748 : ctx->data = ptr + len;
174 5369748 : }
175 :
176 : static inline int
177 : fd_bincode_bytes_encode( uchar const * self,
178 : ulong len,
179 156259 : fd_bincode_encode_ctx_t * ctx ) {
180 156259 : fd_msan_check( self, len );
181 :
182 156259 : uchar * ptr = (uchar *)ctx->data;
183 156259 : if( FD_UNLIKELY( (void *)( ptr+len ) > ctx->dataend ) )
184 0 : return FD_BINCODE_ERR_OVERFLOW;
185 :
186 156259 : fd_memcpy( ptr, self, len );
187 156259 : ctx->data = ptr + len;
188 :
189 156259 : return FD_BINCODE_SUCCESS;
190 156259 : }
191 :
192 : /* Alternate versions of fd_cu16_dec to make the function signature more consistent with the
193 : other fd_bincode_decode functions. */
194 : static inline int
195 : fd_bincode_compact_u16_decode( ushort * self,
196 1303 : fd_bincode_decode_ctx_t * ctx ) {
197 1303 : const uchar * ptr = (const uchar*) ctx->data;
198 1303 : if( FD_UNLIKELY( ptr==NULL ) ) {
199 0 : return FD_BINCODE_ERR_UNDERFLOW;
200 0 : }
201 :
202 1303 : if( FD_LIKELY( (void *) (ptr + 1) <= ctx->dataend && !(0x80U & ptr[0]) ) ) {
203 1176 : *self = (ushort)ptr[0];
204 1176 : ctx->data = ptr + 1;
205 1176 : return FD_BINCODE_SUCCESS;
206 1176 : }
207 :
208 127 : if( FD_LIKELY( (void *) (ptr + 2) <= ctx->dataend && !(0x80U & ptr[1]) ) ) {
209 40 : if( FD_UNLIKELY( !ptr[1] ) ) /* Detect non-minimal encoding */
210 5 : return FD_BINCODE_ERR_ENCODING;
211 35 : *self = (ushort)((ulong)(ptr[0]&0x7FUL) + (((ulong)ptr[1])<<7));
212 35 : ctx->data = ptr + 2;
213 35 : return FD_BINCODE_SUCCESS;
214 40 : }
215 :
216 87 : if( FD_LIKELY( (void *) (ptr + 3) <= ctx->dataend && !(0xFCU & ptr[2]) ) ) {
217 21 : if( FD_UNLIKELY( !ptr[2] ) ) /* Detect non-minimal encoding */
218 6 : return FD_BINCODE_ERR_ENCODING;
219 15 : *self = (ushort)((ulong)(ptr[0]&0x7FUL) + (((ulong)(ptr[1]&0x7FUL))<<7) + (((ulong)ptr[2])<<14));
220 15 : ctx->data = ptr + 3;
221 15 : return FD_BINCODE_SUCCESS;
222 21 : }
223 :
224 66 : return FD_BINCODE_ERR_UNDERFLOW;
225 87 : }
226 :
227 : static inline void
228 : fd_bincode_compact_u16_decode_unsafe( ushort * self,
229 1022 : fd_bincode_decode_ctx_t * ctx ) {
230 1022 : const uchar * ptr = (const uchar*) ctx->data;
231 :
232 1022 : if( !(0x80U & ptr[0]) ) {
233 1021 : *self = (ushort)ptr[0];
234 1021 : ctx->data = ptr + 1;
235 1021 : return;
236 1021 : }
237 :
238 1 : if( !(0x80U & ptr[1]) ) {
239 1 : *self = (ushort)((ulong)(ptr[0]&0x7FUL) + (((ulong)ptr[1])<<7));
240 1 : ctx->data = ptr + 2;
241 1 : return;
242 1 : }
243 :
244 0 : *self = (ushort)((ulong)(ptr[0]&0x7FUL) + (((ulong)(ptr[1]&0x7FUL))<<7) + (((ulong)ptr[2])<<14));
245 0 : ctx->data = ptr + 3;
246 0 : }
247 :
248 : static inline int
249 : fd_bincode_compact_u16_encode( ushort const * self,
250 38448 : fd_bincode_encode_ctx_t * ctx ) {
251 38448 : uchar * ptr = (uchar*) ctx->data;
252 38448 : ulong val = *self;
253 :
254 38448 : if ( val < 0x80UL ) {
255 36641 : if ( FD_UNLIKELY((void *) (ptr + 1) > ctx->dataend ) )
256 0 : return FD_BINCODE_ERR_OVERFLOW;
257 36641 : *ptr = (uchar)val;
258 36641 : ctx->data = ptr + 1;
259 36641 : return FD_BINCODE_SUCCESS;
260 36641 : }
261 :
262 1812 : else if ( val < 0x4000UL ) {
263 1812 : if ( FD_UNLIKELY((void *) (ptr + 2) > ctx->dataend ) )
264 0 : return FD_BINCODE_ERR_OVERFLOW;
265 1812 : ptr[0] = (uchar)((val&0x7FUL)|0x80UL);
266 1812 : ptr[1] = (uchar)(val>>7);
267 1812 : ctx->data = ptr + 2;
268 1812 : return FD_BINCODE_SUCCESS;
269 1812 : }
270 :
271 >1844*10^16 : else {
272 >1844*10^16 : if ( FD_UNLIKELY((void *) (ptr + 3) > ctx->dataend ) )
273 0 : return FD_BINCODE_ERR_OVERFLOW;
274 >1844*10^16 : ptr[0] = (uchar)((val&0x7FUL)|0x80UL);
275 >1844*10^16 : ptr[1] = (uchar)(((val>>7)&0x7FUL)|0x80UL);
276 >1844*10^16 : ptr[2] = (uchar)(val>>14);
277 >1844*10^16 : ctx->data = ptr + 3;
278 >1844*10^16 : return FD_BINCODE_SUCCESS;
279 >1844*10^16 : }
280 38448 : }
281 :
282 : static inline ulong
283 0 : fd_bincode_compact_u16_size( ushort const * self ) {
284 0 : ulong val = *self;
285 :
286 0 : if ( val < 0x80UL ) {
287 0 : return 1;
288 0 : }
289 0 : else if ( val < 0x4000UL ) {
290 0 : return 2;
291 0 : }
292 0 : else {
293 0 : return 3;
294 0 : }
295 0 : }
296 :
297 : /* Decodes an integer encoded using the serde_varint algorithm:
298 : https://github.com/solana-labs/solana/blob/master/sdk/program/src/serde_varint.rs
299 :
300 : A variable number of bytes could have been used to encode the integer.
301 : The most significant bit of each byte indicates if more bytes have been used, so we keep consuming until
302 : we reach a byte where the most significant bit is 0.
303 : */
304 : static inline int
305 : fd_bincode_varint_decode( ulong * self,
306 0 : fd_bincode_decode_ctx_t * ctx ) {
307 0 : ulong out = 0UL;
308 0 : uint shift = 0U;
309 0 :
310 0 : while( FD_LIKELY( shift < 64U ) ) {
311 0 :
312 0 : if( FD_UNLIKELY( ctx->data >= ctx->dataend ) )
313 0 : return FD_BINCODE_ERR_UNDERFLOW;
314 0 :
315 0 : uint byte = *(uchar const *)ctx->data;
316 0 : ctx->data = (uchar const *)ctx->data + 1;
317 0 : out |= (byte & 0x7FUL) << shift;
318 0 :
319 0 : if( (byte & 0x80U) == 0U ) {
320 0 : if( (out>>shift) != byte )
321 0 : return FD_BINCODE_ERR_ENCODING;
322 0 : if( byte==0U && (shift!=0U || out!=0UL) )
323 0 : return FD_BINCODE_ERR_ENCODING;
324 0 : *self = out;
325 0 : return FD_BINCODE_SUCCESS;
326 0 : }
327 0 :
328 0 : shift += 7U;
329 0 :
330 0 : }
331 0 :
332 0 : return FD_BINCODE_ERR_ENCODING;
333 0 : }
334 :
335 : static inline int
336 21647 : fd_bincode_varint_decode_footprint( fd_bincode_decode_ctx_t * ctx ) {
337 21647 : ulong out = 0UL;
338 21647 : uint shift = 0U;
339 :
340 38320 : while( FD_LIKELY( shift < 64U ) ) {
341 :
342 38318 : if( FD_UNLIKELY( ctx->data >= ctx->dataend ) )
343 18 : return FD_BINCODE_ERR_UNDERFLOW;
344 :
345 38300 : uint byte = *(uchar const *)ctx->data;
346 38300 : ctx->data = (uchar const *)ctx->data + 1;
347 38300 : out |= (byte & 0x7FUL) << shift;
348 :
349 38300 : if( (byte & 0x80U) == 0U ) {
350 21627 : if( (out>>shift) != byte )
351 0 : return FD_BINCODE_ERR_ENCODING;
352 21627 : if( byte==0U && (shift!=0U || out!=0UL) )
353 25 : return FD_BINCODE_ERR_ENCODING;
354 21602 : return FD_BINCODE_SUCCESS;
355 21627 : }
356 :
357 16673 : shift += 7U;
358 :
359 16673 : }
360 :
361 2 : return FD_BINCODE_ERR_ENCODING;
362 21647 : }
363 :
364 : static inline void
365 : fd_bincode_varint_decode_unsafe( ulong * self,
366 23073 : fd_bincode_decode_ctx_t * ctx ) {
367 23073 : ulong out = 0UL;
368 23073 : uint shift = 0U;
369 :
370 28523 : for(;;) {
371 28523 : uint byte = *(uchar const *)ctx->data;
372 28523 : ctx->data = (uchar const *)ctx->data + 1;
373 28523 : out |= (byte & 0x7FUL) << shift;
374 :
375 28523 : if( (byte & 0x80U) == 0U ) {
376 23073 : *self = out;
377 23073 : return;
378 23073 : }
379 :
380 5450 : shift += 7U;
381 5450 : }
382 23073 : }
383 :
384 : static inline int
385 : fd_bincode_varint_encode( ulong val,
386 0 : fd_bincode_encode_ctx_t * ctx ) {
387 0 : uchar * ptr = (uchar *) ctx->data;
388 0 : while (1) {
389 0 : if ( FD_UNLIKELY((void *) (ptr + 1) > ctx->dataend ) )
390 0 : return FD_BINCODE_ERR_OVERFLOW;
391 0 : if ( val < 0x80UL ) {
392 0 : *(ptr++) = (uchar)val;
393 0 : ctx->data = ptr;
394 0 : return FD_BINCODE_SUCCESS;
395 0 : }
396 0 : *(ptr++) = (uchar)((val&0x7FUL)|0x80UL);
397 0 : val >>= 7;
398 0 : }
399 0 : }
400 :
401 : static inline ulong
402 0 : fd_bincode_varint_size( ulong val ) {
403 0 : ulong sz = 0;
404 0 : while (1) {
405 0 : if ( val < 0x80UL ) {
406 0 : return sz+1;
407 0 : }
408 0 : sz++;
409 0 : val >>= 7;
410 0 : }
411 0 : }
412 :
413 : /* Convenience API for deserializing */
414 :
415 : /* fd_bincode_decode_static decodes a statically-sized bincode type.
416 :
417 : Example usage:
418 :
419 : fd_epoch_schedule_t es[1];
420 : if( FD_UNLIKELY( fd_bincode_decode_static( epoch_schedule, es, buf, bufsz ) ) ) {
421 : ... parse fail ...
422 : return;
423 : }
424 : ... parse success ... */
425 :
426 : #define fd_bincode_decode_static1( type, suffix, out, buf, buf_sz ) \
427 9382 : __extension__({ \
428 9382 : void const * const buf_ = (buf); \
429 16942 : ulong const buf_sz_ = (buf_sz); \
430 9382 : fd_##type##suffix##_t * res = NULL; \
431 9382 : fd_bincode_decode_ctx_t ctx = {0}; \
432 9382 : ctx.data = (void const *)( buf_ ); \
433 9382 : ctx.dataend = (void const *)( (ulong)ctx.data + buf_sz_ ); \
434 9382 : ulong total_sz = 0UL; \
435 9382 : int err = fd_##type##_decode_footprint( &ctx, &total_sz ); \
436 9382 : if( FD_LIKELY( err==FD_BINCODE_SUCCESS ) ) { \
437 7577 : res = fd_##type##_decode##suffix( (out), &ctx ); \
438 7577 : } \
439 9382 : res; \
440 9382 : })
441 :
442 : #define fd_bincode_decode_static( t,o,b,s ) \
443 9382 : fd_bincode_decode_static1( t, , o, b, s )
444 :
445 : #define fd_bincode_decode_static_global( t,o,b,s ) \
446 : fd_bincode_decode_static1( t, _global, o, b, s )
447 :
448 : #define fd_bincode_decode_static_limited_deserialize( type, out, buf, buf_sz, limit ) \
449 8471 : fd_bincode_decode_static( type, out, buf, buf_sz>limit ? limit : buf_sz )
450 :
451 : #endif /* HEADER_fd_src_flamenco_types_fd_bincode_h */
|