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 : static inline fd_bincode_decode_ctx_t
25 : fd_bincode_decode_ctx( void const * data,
26 18 : ulong sz ) {
27 18 : return (fd_bincode_decode_ctx_t) { .data=data, .dataend=(void *)( (ulong)data+sz ) };
28 18 : }
29 :
30 65301 : #define FD_BINCODE_SUCCESS ( 0)
31 0 : #define FD_BINCODE_ERR_UNDERFLOW (-1001) /* Attempted to read past end of buffer */
32 6 : #define FD_BINCODE_ERR_OVERFLOW (-1002) /* Attempted to write past end of buffer */
33 3 : #define FD_BINCODE_ERR_ENCODING (-1003) /* Invalid encoding */
34 :
35 : #define FD_BINCODE_PRIMITIVE_STUBS( name, type ) \
36 : static inline int \
37 : fd_bincode_##name##_decode( type * self, \
38 1065 : fd_bincode_decode_ctx_t * ctx ) { \
39 1065 : uchar const * ptr = (uchar const *) ctx->data; \
40 1065 : if ( FD_UNLIKELY((void const *)(ptr + sizeof(type)) > ctx->dataend ) ) \
41 1065 : return FD_BINCODE_ERR_UNDERFLOW; \
42 1065 : memcpy( self, ptr, sizeof(type) ); /* unaligned */ \
43 1065 : ctx->data = ptr + sizeof(type); \
44 1065 : return FD_BINCODE_SUCCESS; \
45 1065 : } \
46 : static inline int \
47 49974 : fd_bincode_##name##_decode_footprint( fd_bincode_decode_ctx_t * ctx ) { \
48 49974 : uchar const * ptr = (uchar const *) ctx->data; \
49 49974 : if ( FD_UNLIKELY((void const *)(ptr + sizeof(type)) > ctx->dataend ) ) \
50 49974 : return FD_BINCODE_ERR_UNDERFLOW; \
51 49974 : ctx->data = ptr + sizeof(type); \
52 49974 : return FD_BINCODE_SUCCESS; \
53 49974 : } \
54 : static inline void \
55 : fd_bincode_##name##_decode_unsafe( type * self, \
56 38937 : fd_bincode_decode_ctx_t * ctx ) { \
57 38937 : uchar const * ptr = (uchar const *) ctx->data; \
58 38937 : memcpy( self, ptr, sizeof(type) ); /* unaligned */ \
59 38937 : ctx->data = ptr + sizeof(type); \
60 38937 : } \
61 : static inline int \
62 : fd_bincode_##name##_encode( type self, \
63 7563 : fd_bincode_encode_ctx_t * ctx ) { \
64 7563 : uchar * ptr = (uchar *)ctx->data; \
65 7563 : if ( FD_UNLIKELY((void *)(ptr + sizeof(type)) > ctx->dataend ) ) \
66 7563 : return FD_BINCODE_ERR_OVERFLOW; \
67 7563 : memcpy( ptr, &self, sizeof(type) ); /* unaligned */ \
68 7560 : ctx->data = ptr + sizeof(type); \
69 7560 : return FD_BINCODE_SUCCESS; \
70 7563 : }
71 :
72 : /* fd_w_u128 is a wrapped "uint128" type providing basic 128-bit
73 : unsigned int functionality to fd_types, even if the compile target
74 : does not natively support uint128. */
75 :
76 : union __attribute__((aligned(16))) fd_w_u128 {
77 : uchar uc[16];
78 : ulong ul[2];
79 : # if FD_HAS_INT128
80 : uint128 ud;
81 : # endif
82 : };
83 :
84 : typedef union fd_w_u128 fd_w_u128_t;
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 : FD_BINCODE_PRIMITIVE_STUBS( uint128, fd_w_u128_t )
92 : FD_BINCODE_PRIMITIVE_STUBS( double, double )
93 :
94 : static inline int
95 : fd_bincode_bool_decode( uchar * self,
96 57 : fd_bincode_decode_ctx_t * ctx ) {
97 :
98 57 : uchar const * ptr = (uchar const *)ctx->data;
99 57 : if( FD_UNLIKELY( ptr+1 > (uchar const *)ctx->dataend ) )
100 0 : return FD_BINCODE_ERR_UNDERFLOW;
101 :
102 57 : if( FD_UNLIKELY( *ptr & (~1U) ) )
103 0 : return FD_BINCODE_ERR_ENCODING;
104 :
105 57 : *self = *ptr;
106 57 : ctx->data = ptr + 1;
107 :
108 57 : return FD_BINCODE_SUCCESS;
109 57 : }
110 :
111 : static inline int
112 174 : fd_bincode_bool_decode_footprint( fd_bincode_decode_ctx_t * ctx ) {
113 :
114 174 : uchar const * ptr = (uchar const *)ctx->data;
115 174 : if( FD_UNLIKELY( ptr+1 > (uchar const *)ctx->dataend ) )
116 0 : return FD_BINCODE_ERR_UNDERFLOW;
117 :
118 174 : if( FD_UNLIKELY( *ptr & (~1U) ) )
119 0 : return FD_BINCODE_ERR_ENCODING;
120 :
121 174 : ctx->data = ptr + 1;
122 :
123 174 : return FD_BINCODE_SUCCESS;
124 174 : }
125 :
126 : static inline void
127 : fd_bincode_bool_decode_unsafe( uchar * self,
128 186 : fd_bincode_decode_ctx_t * ctx ) {
129 186 : fd_bincode_uint8_decode_unsafe( self, ctx );
130 186 : }
131 :
132 : static inline int
133 : fd_bincode_bool_encode( uchar self,
134 375 : fd_bincode_encode_ctx_t * ctx ) {
135 :
136 375 : uchar * ptr = (uchar *)ctx->data;
137 375 : if ( FD_UNLIKELY( (void *)(ptr + 1) > ctx->dataend ) )
138 0 : return FD_BINCODE_ERR_OVERFLOW;
139 :
140 375 : *ptr = !!self;
141 375 : ctx->data = ptr + 1;
142 :
143 375 : return FD_BINCODE_SUCCESS;
144 375 : }
145 :
146 : static inline int
147 : fd_bincode_bytes_decode( uchar * self,
148 : ulong len,
149 0 : fd_bincode_decode_ctx_t * ctx ) {
150 0 : uchar * ptr = (uchar *) ctx->data;
151 0 : if ( FD_UNLIKELY((ulong)( (uchar *) ctx->dataend - ptr) < len ) ) // Get wrap-around case right
152 0 : return FD_BINCODE_ERR_UNDERFLOW;
153 0 :
154 0 : fd_memcpy(self, ptr, len);
155 0 : ctx->data = ptr + len;
156 0 :
157 0 : return FD_BINCODE_SUCCESS;
158 0 : }
159 :
160 : static inline int
161 : fd_bincode_bytes_decode_footprint( ulong len,
162 729 : fd_bincode_decode_ctx_t * ctx ) {
163 729 : uchar * ptr = (uchar *) ctx->data;
164 729 : if ( FD_UNLIKELY((ulong)( (uchar *) ctx->dataend - ptr) < len ) ) { // Get wrap-around case right
165 0 : return FD_BINCODE_ERR_UNDERFLOW;
166 0 : }
167 :
168 729 : ctx->data = ptr + len;
169 :
170 729 : return FD_BINCODE_SUCCESS;
171 729 : }
172 :
173 : static inline void
174 : fd_bincode_bytes_decode_unsafe( uchar * self,
175 : ulong len,
176 34788 : fd_bincode_decode_ctx_t * ctx ) {
177 34788 : uchar * ptr = (uchar *) ctx->data;
178 34788 : fd_memcpy(self, ptr, len);
179 34788 : ctx->data = ptr + len;
180 34788 : }
181 :
182 : static inline int
183 : fd_bincode_bytes_encode( uchar const * self,
184 : ulong len,
185 1461 : fd_bincode_encode_ctx_t * ctx ) {
186 1461 : fd_msan_check( self, len );
187 :
188 1461 : uchar * ptr = (uchar *)ctx->data;
189 1461 : if( FD_UNLIKELY( (void *)( ptr+len ) > ctx->dataend ) )
190 0 : return FD_BINCODE_ERR_OVERFLOW;
191 :
192 1461 : fd_memcpy( ptr, self, len );
193 1461 : ctx->data = ptr + len;
194 :
195 1461 : return FD_BINCODE_SUCCESS;
196 1461 : }
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 15 : fd_bincode_decode_ctx_t * ctx ) {
203 15 : const uchar * ptr = (const uchar*) ctx->data;
204 15 : if( FD_UNLIKELY( ptr==NULL ) ) {
205 0 : return FD_BINCODE_ERR_UNDERFLOW;
206 0 : }
207 :
208 15 : if( FD_LIKELY( (void *) (ptr + 1) <= ctx->dataend && !(0x80U & ptr[0]) ) ) {
209 15 : *self = (ushort)ptr[0];
210 15 : ctx->data = ptr + 1;
211 15 : return FD_BINCODE_SUCCESS;
212 15 : }
213 :
214 0 : if( FD_LIKELY( (void *) (ptr + 2) <= ctx->dataend && !(0x80U & ptr[1]) ) ) {
215 0 : if( FD_UNLIKELY( !ptr[1] ) ) /* Detect non-minimal encoding */
216 0 : return FD_BINCODE_ERR_ENCODING;
217 0 : *self = (ushort)((ulong)(ptr[0]&0x7FUL) + (((ulong)ptr[1])<<7));
218 0 : ctx->data = ptr + 2;
219 0 : return FD_BINCODE_SUCCESS;
220 0 : }
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 15 : fd_bincode_decode_ctx_t * ctx ) {
236 15 : const uchar * ptr = (const uchar*) ctx->data;
237 :
238 15 : if( !(0x80U & ptr[0]) ) {
239 15 : *self = (ushort)ptr[0];
240 15 : ctx->data = ptr + 1;
241 15 : return;
242 15 : }
243 :
244 0 : if( !(0x80U & ptr[1]) ) {
245 0 : *self = (ushort)((ulong)(ptr[0]&0x7FUL) + (((ulong)ptr[1])<<7));
246 0 : ctx->data = ptr + 2;
247 0 : return;
248 0 : }
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 249 : fd_bincode_encode_ctx_t * ctx ) {
257 249 : uchar * ptr = (uchar*) ctx->data;
258 249 : ulong val = *self;
259 :
260 249 : if ( val < 0x80UL ) {
261 249 : if ( FD_UNLIKELY((void *) (ptr + 1) > ctx->dataend ) )
262 0 : return FD_BINCODE_ERR_OVERFLOW;
263 249 : *ptr = (uchar)val;
264 249 : ctx->data = ptr + 1;
265 249 : return FD_BINCODE_SUCCESS;
266 249 : }
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 249 : }
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 0 : fd_bincode_varint_decode_footprint( fd_bincode_decode_ctx_t * ctx ) {
343 0 : ulong out = 0UL;
344 0 : uint shift = 0U;
345 :
346 0 : while( FD_LIKELY( shift < 64U ) ) {
347 :
348 0 : if( FD_UNLIKELY( ctx->data >= ctx->dataend ) )
349 0 : return FD_BINCODE_ERR_UNDERFLOW;
350 :
351 0 : uint byte = *(uchar const *)ctx->data;
352 0 : ctx->data = (uchar const *)ctx->data + 1;
353 0 : out |= (byte & 0x7FUL) << shift;
354 :
355 0 : if( (byte & 0x80U) == 0U ) {
356 0 : if( (out>>shift) != byte )
357 0 : return FD_BINCODE_ERR_ENCODING;
358 0 : if( byte==0U && (shift!=0U || out!=0UL) )
359 0 : return FD_BINCODE_ERR_ENCODING;
360 0 : return FD_BINCODE_SUCCESS;
361 0 : }
362 :
363 0 : shift += 7U;
364 :
365 0 : }
366 :
367 0 : return FD_BINCODE_ERR_ENCODING;
368 0 : }
369 :
370 : static inline void
371 : fd_bincode_varint_decode_unsafe( ulong * self,
372 0 : fd_bincode_decode_ctx_t * ctx ) {
373 0 : ulong out = 0UL;
374 0 : uint shift = 0U;
375 :
376 0 : for(;;) {
377 0 : uint byte = *(uchar const *)ctx->data;
378 0 : ctx->data = (uchar const *)ctx->data + 1;
379 0 : out |= (byte & 0x7FUL) << shift;
380 :
381 0 : if( (byte & 0x80U) == 0U ) {
382 0 : *self = out;
383 0 : return;
384 0 : }
385 :
386 0 : shift += 7U;
387 0 : }
388 0 : }
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 : /* Convenience API for deserializing */
420 :
421 : /* fd_bincode_decode_static decodes a statically-sized bincode type.
422 :
423 : Example usage:
424 :
425 : fd_epoch_schedule_t es[1];
426 : if( FD_UNLIKELY( fd_bincode_decode_static( epoch_schedule, es, buf, bufsz ) ) ) {
427 : ... parse fail ...
428 : return;
429 : }
430 : ... parse success ... */
431 :
432 : #define fd_bincode_decode_static1( type, suffix, out, buf, buf_sz ) \
433 276 : __extension__({ \
434 276 : void const * const buf_ = (buf); \
435 276 : ulong const buf_sz_ = (buf_sz); \
436 276 : fd_##type##suffix##_t * res = NULL; \
437 276 : fd_bincode_decode_ctx_t ctx = {0}; \
438 276 : ctx.data = (void const *)( buf_ ); \
439 276 : ctx.dataend = (void const *)( (ulong)ctx.data + buf_sz_ ); \
440 276 : ulong total_sz = 0UL; \
441 276 : int err = fd_##type##_decode_footprint( &ctx, &total_sz ); \
442 276 : if( FD_LIKELY( err==FD_BINCODE_SUCCESS ) ) { \
443 276 : res = fd_##type##_decode##suffix( (out), &ctx ); \
444 276 : } \
445 276 : res; \
446 276 : })
447 :
448 : #define fd_bincode_decode_static( t,o,b,s ) \
449 276 : fd_bincode_decode_static1( t, , o, b, s )
450 :
451 : #define fd_bincode_decode_static_global( t,o,b,s ) \
452 : fd_bincode_decode_static1( t, _global, o, b, s )
453 :
454 : #define fd_bincode_decode_static_limited_deserialize( type, out, buf, buf_sz, limit ) \
455 105 : fd_bincode_decode_static( type, out, buf, buf_sz>limit ? limit : buf_sz )
456 :
457 : #endif /* HEADER_fd_src_flamenco_types_fd_bincode_h */
|