Line data Source code
1 : #ifndef HEADER_fd_src_pod_fd_pod_h
2 : #define HEADER_fd_src_pod_fd_pod_h
3 :
4 : /* pod is a set of APIs for managing flexible hierarchies of typed
5 : key-val pairs. A pod is a data structure for holds these in memory
6 : contiguously and compactly such that it can be easily saved to
7 : permanent storage, sent over networks, distributed between different
8 : hosts / architectures / address spaces.
9 :
10 : It is trivial to make a pod.
11 :
12 : It is trivial to query a pod.
13 :
14 : It is trivial to import different config file formats into a pod
15 : (including JSON, YAML, TOML, etc).
16 :
17 : It is trivial to serialize / deserialize / save / restore a pod.
18 :
19 : Multiple value types are supported with builtin coverage of all
20 : primitive datatypes. In particular, a value itself can be a pod and
21 : it is easy to lookup deeply nested values in a pod via their key
22 : path. (Essentially, a pod a simple in-memory file system.)
23 :
24 : As such pods are an incredible useful building blocks for dealing
25 : with heterogeneous distributed environment / configuration,
26 : checkpointing, etc.
27 :
28 : The current implementation of POD below assumes little endian
29 : architecture and that the platform can reasonably efficient access
30 : unaligned primitive types. These restrictions can be removed if
31 : necessary.
32 :
33 : A pod starts with 3 svw (symmetric-variable-width) encoded ulongs:
34 :
35 : - max: The max size of the pod in bytes (including the header)
36 : - used: The number of bytes currently used in the pod in bytes
37 : (including the header), <= max.
38 : - cnt: The number of key-val pairs in the pod (a key-subpod pair is
39 : considered as a single pair from the header POV regardless of
40 : the number of keys the subpod might hold). As a key val pair
41 : requires at least 1 byte to represent practically, this is
42 : <=used (and more typically << used).
43 :
44 : Since cnt<=used<=max, the svw encoded size of all these are bounded
45 : by the encoded max and we can thus use the same size encoding to
46 : facilitate fast operations on encoded headers.
47 :
48 : This header is followed by cnt key-val pairs. A pair is represented
49 : in a pod as:
50 :
51 : - key_sz: strlen(key)+1 to facilitate fast iteration and fast key
52 : string ops
53 : - key: key_sz_bytes holds the key cstr, key does not contain a
54 : '.' (to facilitate recursive querying), does include the
55 : '\0' to facilitate zero-copy user operation /
56 : interoperability with standard cstr handling APIs
57 : - val_type: 1 byte, a FD_POD_VAL_TYPE_* for extensibility
58 : - val_sz: number of bytes in the pod encoded representation of val
59 : - val: val_sz bytes, interpreted as specified by val_type
60 :
61 : key_sz and val_sz are both svw encoded. There are no theoretical
62 : restrictions (up to the size of a ulong) on the size of a key or a
63 : val. */
64 :
65 : #include "../cstr/fd_cstr.h"
66 :
67 : /* FD_POD_ERR_* gives a number of error codes used by fd_pod APIs. */
68 :
69 43864311 : #define FD_POD_SUCCESS ( 0) /* Operation was successful */
70 171 : #define FD_POD_ERR_INVAL (-1) /* Operation failed because input args were invalid */
71 1430658 : #define FD_POD_ERR_TYPE (-2) /* Operation failed because the path contained a key of an unexpected type */
72 2515368 : #define FD_POD_ERR_RESOLVE (-3) /* Operation failed because the path did not resolve to a key */
73 6027 : #define FD_POD_ERR_FULL (-4) /* Operation failed because the pod did not have enough space to complete it */
74 :
75 : /* FD_POD_VAL_TYPE_* gives a type of value stored in a pod key-val pair.
76 : These must be in [0,255]. Values in [16,127] are reserved for
77 : potentially additional primitive types. Vales in [128,255] are
78 : reserved for user defined types. */
79 :
80 768796590 : #define FD_POD_VAL_TYPE_SUBPOD ( 0) /* Val is a RAW encoded pod */
81 99756 : #define FD_POD_VAL_TYPE_BUF ( 1) /* Val is a RAW encoded buffer */
82 23125167 : #define FD_POD_VAL_TYPE_CSTR ( 2) /* Val is a RAW encoded '\0'-terminated string */
83 98835 : #define FD_POD_VAL_TYPE_CHAR ( 3) /* Val is a RAW encoded 8-bit char (indeterminant sign) */
84 99228 : #define FD_POD_VAL_TYPE_SCHAR ( 4) /* Val is a RAW encoded 8-bit signed int (twos complement) */
85 99186 : #define FD_POD_VAL_TYPE_SHORT ( 5) /* Val is a SVW encoded 16-bit signed int (twos complement) */
86 99453 : #define FD_POD_VAL_TYPE_INT ( 6) /* Val is a SVW encoded 32-bit signed int (twos complement) */
87 99522 : #define FD_POD_VAL_TYPE_LONG ( 7) /* Val is a SVW encoded 64-bit signed int (twos complement) */
88 99498 : #define FD_POD_VAL_TYPE_INT128 ( 8) /* Val is a SVW encoded 128-bit signed int (twos complement) */
89 100659 : #define FD_POD_VAL_TYPE_UCHAR ( 9) /* Val is a RAW encoded 8-bit unsigned int */
90 99804 : #define FD_POD_VAL_TYPE_USHORT (10) /* Val is a SVW encoded 16-bit unsigned int */
91 100842 : #define FD_POD_VAL_TYPE_UINT (11) /* Val is a SVW encoded 32-bit unsigned int */
92 100074 : #define FD_POD_VAL_TYPE_ULONG (12) /* Val is a SVW encoded 64-bit unsigned int */
93 99021 : #define FD_POD_VAL_TYPE_UINT128 (13) /* Val is a SVW encoded 128-bit unsigned int */
94 100758 : #define FD_POD_VAL_TYPE_FLOAT (14) /* Val is a RAW IEEE-754 float (little endian) */
95 98511 : #define FD_POD_VAL_TYPE_DOUBLE (15) /* Val is a RAW IEEE-754 double (little endian) */
96 :
97 : /* FD_POD_VAL_TYPE_CSTR_MAX is the maximum number of bytes (including
98 : terminating '\0') required for hold the cstr representation of a
99 : value type. */
100 :
101 1488 : #define FD_POD_VAL_TYPE_CSTR_MAX (8UL)
102 :
103 : /* FD_POD_FOOTPRINT_MIN gives the minimum pod byte footprint possible */
104 :
105 4125267 : #define FD_POD_FOOTPRINT_MIN (3UL)
106 :
107 : /* A fd_pod_info_t is used when listing the contents of a pod. It is
108 : not stored explicitly in the pod itself. The lifetime guarantees of
109 : all pointers in an info is that of the pod itself or any invalidating
110 : operation on that pod. */
111 :
112 : struct fd_pod_info;
113 : typedef struct fd_pod_info fd_pod_info_t;
114 :
115 : struct fd_pod_info {
116 : ulong key_sz; /* Size of key in pod (includes terminating '\0') */
117 : char const * key; /* Pointer to first byte of this pod key cstr */
118 : int val_type; /* Type of val (in [0,255], a FD_POD_VAL_TYPE_*) */
119 : ulong val_sz; /* Size of val in bytes (pod encoded form) */
120 : void const * val; /* Pointer to first byte of val (in pod encoded form). For a cstr type, if val_sz==0, ignore this and
121 : treat as NULL (FIXME: CONSIDER HANDLING THIS UNDER THE HOOD?). */
122 : fd_pod_info_t * parent; /* For a recursive listing, NULL if the key is not in a subpod of the pod getting listed. Otherwise,
123 : points to an (earlier) info with details about the subpod. For a non-recursive listing or query,
124 : NULL. */
125 : };
126 :
127 : typedef struct fd_pod_info fd_pod_info_t;
128 :
129 : /* FD_POD_{ALIGN,FOOTPRINT} return the alignment and footprint required
130 : for a memory region to be used as a pod. ALIGN will 1 (no alignment
131 : requirements) and FOOTPRINT will be max. Max is assumed to be at
132 : least FD_POD_FOOTPRINT_MIN. These are provided to facilitate compile
133 : time construction and for consistency with other constructors. */
134 :
135 : #define FD_POD_ALIGN (1UL)
136 : #define FD_POD_FOOTPRINT( max ) (max)
137 :
138 : FD_PROTOTYPES_BEGIN
139 :
140 : /* Constructors *******************************************************/
141 :
142 : /* fd_pod_{align,footprint,new,join,leave,delete} are the distributed
143 : shared memory constructors for a pod and have the usual semantics.
144 :
145 : Note max is the number of bytes available for the whole pod.
146 : Further, there is no actual alignment requirement. This allows
147 : flexibly storing pods into all sorts of places with arbitrary size
148 : and alignment constraints.
149 :
150 : The only practical constraint is a pod can not be squeezed into a
151 : region smaller than FD_POD_FOOTPRINT_MIN. Note further that, from
152 : the point of view of distribution, a pod is just a bag of up to max
153 : bytes. Only bytes [0,used) are needed to encode the exact state of
154 : pod. Setting max==used effectively seals up a pod such that no more
155 : key-val pairs can be added to it. */
156 :
157 0 : FD_FN_CONST static inline ulong fd_pod_align ( void ) { return 1UL; }
158 30018 : FD_FN_CONST static inline ulong fd_pod_footprint( ulong max ) { return fd_ulong_if( max>=FD_POD_FOOTPRINT_MIN, max, 0UL ); }
159 :
160 : static inline void *
161 : fd_pod_new( void * shmem,
162 4090323 : ulong max ) {
163 4090323 : if( FD_UNLIKELY( !shmem ) ) return NULL;
164 4090323 : ulong footprint = fd_pod_footprint( max );
165 4090323 : if( FD_UNLIKELY( !footprint ) ) return NULL;
166 4090323 : uchar * pod = (uchar *)shmem;
167 4090323 : ulong csz = fd_ulong_svw_enc_sz( max );
168 4090323 : fd_ulong_svw_enc_fixed( pod, csz, max );
169 4090323 : fd_ulong_svw_enc_fixed( pod + csz, csz, 3UL*csz ); /* used */
170 4090323 : fd_ulong_svw_enc_fixed( pod + csz*2UL, csz, 0UL );
171 4090323 : return shmem;
172 4090323 : }
173 :
174 4090608 : static inline uchar * fd_pod_join ( void * shpod ) { return (uchar *)shpod; }
175 393 : static inline void * fd_pod_leave ( uchar const * pod ) { return (void *)pod; }
176 108 : static inline void * fd_pod_delete( void * shpod ) { return shpod; }
177 :
178 : /* Accessors **********************************************************/
179 :
180 : /* fd_pod_{max,used,cnt,avail} returns the maximum number of bytes /
181 : number of used bytes / number of keys / number of bytes available for
182 : storing key-val pairs in the pod. Assumes pod is a current local
183 : join. */
184 :
185 : FD_FN_PURE static inline ulong
186 167725575 : fd_pod_max( uchar const * pod ) {
187 167725575 : ulong csz = fd_ulong_svw_dec_sz( pod );
188 167725575 : return fd_ulong_svw_dec_fixed( pod, csz );
189 167725575 : }
190 :
191 : FD_FN_PURE static inline ulong
192 3048 : fd_pod_used( uchar const * pod ) {
193 3048 : ulong csz = fd_ulong_svw_dec_sz( pod );
194 3048 : return fd_ulong_svw_dec_fixed( pod + csz, csz );
195 3048 : }
196 :
197 : FD_FN_UNUSED FD_FN_PURE static ulong /* Work around -Winline */
198 939672 : fd_pod_cnt( uchar const * pod ) {
199 939672 : ulong csz = fd_ulong_svw_dec_sz( pod );
200 939672 : return fd_ulong_svw_dec_fixed( pod + 2UL*csz, csz );
201 939672 : }
202 :
203 : FD_FN_UNUSED FD_FN_PURE static ulong /* Work around -Winline */
204 3009 : fd_pod_avail( uchar const * pod ) {
205 3009 : ulong csz = fd_ulong_svw_dec_sz( pod );
206 3009 : return fd_ulong_svw_dec_fixed( pod, csz ) - fd_ulong_svw_dec_fixed( pod + csz, csz );
207 3009 : }
208 :
209 : /* fd_pod_list returns the details about the current key-val pairs in
210 : the pod. info is indexed [0,fd_pod_cnt(pod)). Does not recurse into
211 : any subpods in the pod. E.g. for the pod:
212 :
213 : int foo 1
214 : pod bar {
215 : pod baz {
216 : int bay 2
217 : int bax 3
218 : }
219 : int baw 4
220 : }
221 : int bav 5
222 :
223 : list will return 3 key-val pairs:
224 :
225 : 0: int foo 1 (no parent)
226 : 1: pod bar { ... } (no parent)
227 : 2: int bav 5 (no parent)
228 :
229 : Returns info. The indices used for the current pairs will be stable
230 : for the pod's lifetime or the next invalidating operation. Returns
231 : info on success and NULL on failure (i.e. pod is NULL). */
232 :
233 : fd_pod_info_t *
234 : fd_pod_list( uchar const * FD_RESTRICT pod,
235 : fd_pod_info_t * FD_RESTRICT info );
236 :
237 : /* fd_pod_cnt_subpod returns the number of subpods in the pod. Does not
238 : recurse into any subpods in the pod. E.g. for the above example,
239 : returns 1. This operation is O(fd_pod_cnt(pod)) in pod. NULL
240 : returns 0. */
241 :
242 : FD_FN_PURE ulong
243 : fd_pod_cnt_subpod( uchar const * pod );
244 :
245 : /* fd_pod_list_recursive is the same as fd_pod_list but will depth-first
246 : recurse into subpods. info is indexed [0,fd_pod_cnt_recursive(pod)).
247 : E.g. for the above example, list_recursive will return 7 key-val
248 : pairs:
249 :
250 : 0: int foo 1 (no parent)
251 : 1: pod bar { ... } (no parent)
252 : 2: pod baz { ... } (parent bar)
253 : 3: int bay 2 (parent baz)
254 : 4: int bax 3 (parent baz)
255 : 5: int baw 4 (parent bar)
256 : 6: int bav 5 (no parent) */
257 :
258 : FD_FN_PURE ulong
259 : fd_pod_cnt_recursive( uchar const * pod );
260 :
261 : fd_pod_info_t *
262 : fd_pod_list_recursive( uchar const * FD_RESTRICT pod,
263 : fd_pod_info_t * FD_RESTRICT info );
264 :
265 : /* fd_pod_query queries the pod for information about path. Path is a
266 : cstr that consists of one or more keys delimited with a '.' such
267 : that, for example, the path:
268 :
269 : "foo.bar.baz"
270 :
271 : indicates the query should find the key foo in the pod, recurse into
272 : the foo's subpod val, find the key bar in the subpod, recurse in
273 : bar's subpod val and then find the key baz in the subsubpod and then
274 : extract information about baz as requested. Returns 0 on success or
275 : a non-zero (FD_POD_ERR_*) error code on failure:
276 :
277 : SUCCESS - the query was successful. If opt_info was non-NULL,
278 : *opt_info will contain details about the found key.
279 : See fd_pod_info_t for more details.
280 : INVAL - bad input args (e.g. NULL pod and/or NULL path was NULL)
281 : opt_info ignored
282 : TYPE - one of the path prefixes resolved to a non-subpod
283 : (e.g. "foo.bar" doesn't refer to a subpod)
284 : opt_info ignored (FIXME: CONSIDER DETAILS IN OPT_INFO?)
285 : RESOLVE - the path did not resolve to a key
286 : (e.g. pod doesn't contain a key "foo" or subpod "foo"
287 : doesn't contain a key named bar or "foo.bar"
288 : doesn't contain a key baz)
289 : opt_info ignored (FIXME: CONSIDER DETAILS IN OPT_INFO?)
290 :
291 : info parent will be NULL (even if path nested) */
292 :
293 : int
294 : fd_pod_query( uchar const * FD_RESTRICT pod,
295 : char const * FD_RESTRICT path,
296 : fd_pod_info_t * FD_RESTRICT opt_info );
297 :
298 : /* Iterator ***********************************************************/
299 :
300 : /* Typical usage of this iterator:
301 :
302 : for( fd_pod_iter_t iter = fd_pod_iter_init( pod ); !fd_pod_iter_done( iter ); iter = fd_pod_iter_next( iter ) ) {
303 : fd_pod_info_t info = fd_pod_iter_info( iter );
304 :
305 : ... At this point, info.* contains the usual details about the next
306 : ... key-val pair in the pod (info.parent will be NULL). There is
307 : ... no guarantee about the order in which key-val pairs will be
308 : ... provided (other than it will be the same for each iteration
309 : ... provided the pod itself hasn't been changed and the same order
310 : ... given by fd_pod_list). Iteration does not recurse into any
311 : ... subpods. Assumes the pod will not be changed during the
312 : ... iteration. It is okay to pass NULL for pod to the init (no
313 : ... iteration will be done as there are ... no key-val pairs to
314 : ... iterate over).
315 :
316 : ... This process is algorithmically efficient but the
317 : ... implementation is not as fast as it could be. But iterating
318 : ... over pods is typically only done during non-critical path
319 : ... initialization processes.
320 : }
321 : */
322 :
323 : /* fd_pod_iter_t is an opaque handle for iterating over all the key-val
324 : pairs in a pod. This is exposed here to facilitate inlining iteration
325 : operations. */
326 :
327 : struct fd_pod_iter_private {
328 : uchar const * cursor;
329 : uchar const * stop;
330 : };
331 :
332 : typedef struct fd_pod_iter_private fd_pod_iter_t;
333 :
334 : /* fd_pod_iter_init starts an iteration over the given pod (pod can be
335 : nested inside another pod). Assumes pod points to the first byte of
336 : a well-formed static pod for iteration duration in the caller's local
337 : address space or is NULL. */
338 :
339 : FD_FN_UNUSED static fd_pod_iter_t /* Work around -Winline */
340 457482 : fd_pod_iter_init( uchar const * pod ) {
341 457482 : if( FD_UNLIKELY( !pod ) ) { fd_pod_iter_t iter; iter.cursor = NULL; iter.stop = NULL; return iter; }
342 457479 : ulong csz = fd_ulong_svw_dec_sz( pod );
343 457479 : fd_pod_iter_t iter;
344 457479 : iter.cursor = pod + csz*3UL;
345 457479 : iter.stop = pod + fd_ulong_svw_dec_fixed( pod + csz, csz ); /* used */
346 457479 : return iter;
347 457482 : }
348 :
349 : /* fd_pod_iter_done returns 0 if there are more key-val pairs to iterate
350 : over or non-zero if not. Assumes iter was either returned by
351 : fd_pod_iter_init or fd_pod_iter_next. */
352 :
353 : static inline int
354 16818258 : fd_pod_iter_done( fd_pod_iter_t iter ) {
355 16818258 : return iter.cursor>=iter.stop;
356 16818258 : }
357 :
358 : /* fd_pod_iter_next advances the iterator to the next key-val pair in
359 : the pod (if any). Assumes !fd_pod_iter_done(iter). */
360 :
361 : FD_FN_UNUSED static fd_pod_iter_t /* Work around -Winline */
362 16360776 : fd_pod_iter_next( fd_pod_iter_t iter ) {
363 16360776 : uchar const * cursor = iter.cursor;
364 :
365 : /* Skip over current key */
366 16360776 : ulong ksz = fd_ulong_svw_dec_sz( cursor );
367 16360776 : ulong key_sz = fd_ulong_svw_dec_fixed( cursor, ksz );
368 16360776 : cursor += ksz + key_sz;
369 :
370 : /* Skip over current type */
371 16360776 : cursor++;
372 :
373 : /* Skip over current val */
374 16360776 : ulong vsz = fd_ulong_svw_dec_sz( cursor );
375 16360776 : ulong val_sz = fd_ulong_svw_dec_fixed( cursor, vsz );
376 16360776 : cursor += vsz + val_sz;
377 :
378 16360776 : iter.cursor = cursor;
379 16360776 : return iter;
380 16360776 : }
381 :
382 : /* fd_pod_iter_info returns information about the current iteration
383 : key-val pair. Assumes !fd_pod_iter_done( iter ). The usual lifetime
384 : restrictions about info.key and info.val apply (which, since the pod
385 : is fixed for the iteration duration, mean the lifetime of these
386 : pointers is at least the iteration). info.parent will be NULL. */
387 :
388 : FD_FN_UNUSED static fd_pod_info_t /* Work around -Winline */
389 16360776 : fd_pod_iter_info( fd_pod_iter_t iter ) {
390 16360776 : uchar const * cursor = iter.cursor;
391 :
392 16360776 : fd_pod_info_t info;
393 :
394 : /* Unpack key */
395 16360776 : ulong ksz = fd_ulong_svw_dec_sz( cursor );
396 16360776 : info.key_sz = fd_ulong_svw_dec_fixed( cursor, ksz ); cursor += ksz;
397 16360776 : info.key = (char const *)cursor; cursor += info.key_sz;
398 :
399 : /* Unpack type */
400 16360776 : info.val_type = (int)cursor[0]; cursor++;
401 :
402 : /* Unpack val */
403 16360776 : ulong vsz = fd_ulong_svw_dec_sz( cursor );
404 16360776 : info.val_sz = fd_ulong_svw_dec_fixed( cursor, vsz ); cursor += vsz;
405 16360776 : info.val = (void const *)cursor; cursor += info.val_sz;
406 :
407 16360776 : info.parent = NULL;
408 :
409 16360776 : return info;
410 16360776 : }
411 :
412 : /* Miscellaneous APIs *************************************************/
413 :
414 : /* fd_pod_strerror converts an FD_POD_SUCCESS / FD_POD_ERR_* code into
415 : a human readable cstr. The lifetime of the returned pointer is
416 : infinite. The returned pointer is always to a non-NULL cstr. */
417 :
418 : FD_FN_CONST char const *
419 : fd_pod_strerror( int err );
420 :
421 : /* fd_pod_reset throws away all key-val pairs in pod. (This also throws
422 : away any key-val pairs in any subpods in the pod.) Returns pod on
423 : success and NULL on failure.
424 :
425 : IMPORTANT! THIS IS AN INVALIDATING OPERATION */
426 :
427 : static inline uchar *
428 3003 : fd_pod_reset( uchar * pod ) {
429 3003 : if( FD_UNLIKELY( !pod ) ) return NULL;
430 3003 : ulong csz = fd_ulong_svw_dec_sz( pod );
431 3003 : fd_ulong_svw_enc_fixed( pod + csz, csz, 3UL*csz ); /* used */
432 3003 : fd_ulong_svw_enc_fixed( pod + csz*2UL, csz, 0UL ); /* cnt */
433 3003 : return pod;
434 3003 : }
435 :
436 : /* fd_pod_resize resizes a pod to the largest possible value <= new_max.
437 : Returns the achieved max on success and 0 on failure (pod is NULL,
438 : new_max<pod used). Achieved max is usually new_max but there are
439 : rare edge cases. E.g. pod_max==64, pod_used==64, new_max==65 ... the
440 : pod header needs to be expanded by 3 bytes to to accommodate new_max
441 : (and potentially wider pod_used and pod_cnt) but that leaves 2 few
442 : bytes space to encode the existing pod key-val pairs.
443 :
444 : The difference between requested new_max and the achieved new_max is
445 : typically so small in these edge cases as to be programmatically
446 : irrelevant (e.g. there wouldn't be enough room to add additional
447 : key-val pairs to the pod for example). Users can trap if the return
448 : value != new_max on return to detect such edge cases if desired
449 : though.
450 :
451 : That is, if the pod points to the first byte of a pod currently held
452 : in memory region of new_max bytes in size (where pod used<=new_max),
453 : this will adjust pod max to make as much of the new memory region as
454 : possible available to the pod for adding new key-val pairs.
455 :
456 : This operation is O(pod_used) worst case.
457 :
458 : IMPORTANT! THIS IS AN INVALIDATING OPERATION */
459 :
460 : ulong
461 : fd_pod_resize( uchar * pod,
462 : ulong new_max );
463 :
464 : /* fd_pod_compact eliminates any internal padding in the pod. Assumes
465 : pod is a current local join. If full is non-zero, a full compaction
466 : is done such that the pod_max is reduced to be equal to pod_used and
467 : the pod header is accordingly compacted (otherwise, the pod_max will
468 : be unchanged on return).
469 :
470 : Regardless of full, all subpods will be recursively fully compacted
471 : and all cstrs in the pod will have had their padding removed (they
472 : will be still be '\0' terminated if originally correctly '\0'
473 : terminated). Returns the compacted size of the pod on success and 0
474 : on failure (e.g. pod is NULL).
475 :
476 : IMPORTANT! THIS IS AN INVALIDATING OPERATION
477 :
478 : IMPORTANT! DOING A COMPACT FOLLOWED BY A RESIZE IS NOT GUARANTEED TO
479 : RESTORE THESE ORIGINAL OFFSETS. */
480 :
481 : ulong
482 : fd_pod_compact( uchar * pod,
483 : int full );
484 :
485 : /* fd_cstr_to_pod_val_type: Convert a cstr pointed to by cstr into a
486 : FD_POD_VAL_TYPE_*. On success, returns the val type (will be in
487 : 0:255) and on failure returns a negative value (an FD_POD_ERR_*
488 : code). */
489 :
490 : FD_FN_PURE int
491 : fd_cstr_to_pod_val_type( char const * cstr );
492 :
493 : /* fd_pod_val_type_to_cstr: Populate the buffer cstr (which has enough
494 : room for FD_POD_VAL_TYPE_CSTR_MAX bytes) with the cstr corresponding
495 : to val_type (should be in 0:255). Returns cstr on success and NULL
496 : on failure (cstr is untouched on failure). */
497 :
498 : char *
499 : fd_pod_val_type_to_cstr( int val_type,
500 : char * cstr );
501 :
502 : /* General alloc APIs *************************************************/
503 :
504 : /* fd_pod_alloc allocates space in the pod for a key at the end of the
505 : given path with the given val_type whose encoded size is val_sz.
506 : Returns offset in pod where val should be stored (room for val_sz
507 : bytes), 0 on failure. Failure reasons include NULL pod, NULL path,
508 : one of the path prefixes resolved to a non-subpod, path is already in
509 : the pod, invalid val_type or no room in pod for val_sz.
510 :
511 : If subpods along the path do not exist, they will be created in the
512 : process.
513 :
514 : IMPORTANT! THIS IS AN INVALIDATING OPERATION
515 :
516 : IMPORTANT! In the current implementation, it is possible for one or
517 : more subpods along the path to be created and the call to fail. The
518 : last subpod created in a string of such will be empty.
519 :
520 : Usage with val_types in one of the preexisting FD_POD_VAL_TYPE_*
521 : probably should use the specific APIs already provided for these
522 : types instead of this. This is more to support custom user types. */
523 :
524 : ulong
525 : fd_pod_alloc( uchar * FD_RESTRICT pod,
526 : char const * FD_RESTRICT path,
527 : int val_type,
528 : ulong val_sz );
529 :
530 : /* fd_pod_insert is same as the above but also populates the allocated
531 : space with the val_sz bytes pointed to by val. Assumes that val_type
532 : / val_sz / val encoding is sensible. */
533 :
534 : FD_FN_UNUSED static ulong /* Work around -Winline */
535 : fd_pod_insert( uchar * FD_RESTRICT pod,
536 : char const * FD_RESTRICT path,
537 : int val_type,
538 : ulong val_sz,
539 344865 : void const * FD_RESTRICT val ) {
540 344865 : ulong off = fd_pod_alloc( pod, path, val_type, val_sz );
541 344865 : if( FD_LIKELY( off ) ) fd_memcpy( pod + off, val, val_sz );
542 344865 : return off;
543 344865 : }
544 :
545 : /* fd_pod_remove removes a key from the pod. The key is at the end
546 : of the given path. E.g. if path is:
547 :
548 : "foo.bar.baz"
549 :
550 : The key "baz" will be remove from subsubpod bar (which in turn is in
551 : subpod foo). The pod and/or any subpods on the path WILL NOT be
552 : compacted after remove.
553 :
554 : If a path ends on a subpod, that subpod and all its keys (and
555 : subpods) it might contain will be removed.
556 :
557 : Currently, if the removal results in any empty subpod, that subpod
558 : will be preserved. (FIXME: CONSIDER OPTION TO REMOVE CREATED EMPTY
559 : SUBPODS RECURSIVELY TOO?)
560 :
561 : Returns a 0 (FD_POD_SUCCESS) on success or a negative value
562 : (FD_POD_ERR_*) on failure. Reasons for failure are:
563 :
564 : INVAL - bad input args
565 : (e.g. pod or path was NULL)
566 : TYPE - one of the path prefixes resolved to a non-subpod
567 : (e.g. "foo.bar" above was had a cstr value)
568 : RESOLVE - the path did not resolve to a key
569 : (e.g. subsubpod bar did not contain a key baz)
570 :
571 : IMPORTANT! THIS IS AN INVALIDATING OPERATION */
572 :
573 : int
574 : fd_pod_remove( uchar * FD_RESTRICT pod,
575 : char const * FD_RESTRICT path );
576 :
577 : /* Specific alloc APIs ************************************************/
578 :
579 : /* fd_pod_alloc_subpod creates a empty subpod at path with space for up
580 : to max bytes in the given pod. Returns offset of subpod within the
581 : pod on success (e.g. pod + off is the location of an unjoined pod)
582 : and 0 on failure. The user can add key-val pairs within this subpod
583 : as it would any pod with created with max storage. This offset is
584 : valid for the pod's lifetime or an invalidating operation is done on
585 : the pod.
586 :
587 : IMPORTANT! THIS IS AN INVALIDATING OPERATION */
588 :
589 : static inline ulong
590 : fd_pod_alloc_subpod( uchar * FD_RESTRICT pod,
591 : char const * FD_RESTRICT path,
592 0 : ulong max ) { /* Assumes max>=FD_POD_FOOTPRINT_MIN */
593 0 : ulong off = fd_pod_alloc( pod, path, FD_POD_VAL_TYPE_SUBPOD, fd_pod_footprint( max ) );
594 0 : if( FD_UNLIKELY( !off ) ) return 0UL;
595 0 : fd_pod_new( pod + off, max );
596 0 : return off;
597 0 : }
598 :
599 : /* fd_pod_alloc_buf creates a empty buffer at path with space for up to
600 : val_sz bytes in the given pod. Returns offset of buf on success
601 : (e.g. pod + off is the location of first byte of buf) and 0 on
602 : failure. This offset is valid for the pod's lifetime or an
603 : invalidating operation is done on the pod.
604 :
605 : IMPORTANT! THIS IS AN INVALIDATING OPERATION */
606 :
607 : static inline ulong
608 : fd_pod_alloc_buf( uchar * FD_RESTRICT pod,
609 : char const * FD_RESTRICT path,
610 0 : ulong val_sz ) { /* Bound of final buffer sz */
611 0 : return fd_pod_alloc( pod, path, FD_POD_VAL_TYPE_BUF, val_sz );
612 0 : }
613 :
614 : /* fd_pod_alloc_cstr creates a space for cstr value at path with space
615 : for up to val_sz bytes (including terminating '\0'). Returns offset
616 : of cstr on success (e.g. pod + off is the location of first byte of
617 : cstr) and 0 on failure. val_sz of 0 indicates that val is the NULL
618 : pointer. This offset is valid for the pod's lifetime or an
619 : invalidating operation is done on the pod.
620 :
621 : IMPORTANT! THIS IS AN INVALIDATING OPERATION */
622 :
623 : static inline ulong
624 : fd_pod_alloc_cstr( uchar * FD_RESTRICT pod,
625 : char const * FD_RESTRICT path,
626 0 : ulong val_sz ) { /* Bound of final length of cstr, including terminating '\0' */
627 0 : return fd_pod_alloc( pod, path, FD_POD_VAL_TYPE_CSTR, val_sz );
628 0 : }
629 :
630 : /* Specific insert APIs ***********************************************/
631 :
632 : /* fd_pod_insert_subpod inserts the subpod into the pod at the given
633 : path. It is up to the user to do compaction of the subpod and/or
634 : overall pod as desired. Returns offset where subpod inserted, 0 on
635 : failure. This offset is valid for the pod's lifetime or an
636 : invalidating operation is done on the pod.
637 :
638 : IMPORTANT! THIS IS AN INVALIDATING OPERATION */
639 :
640 : static inline ulong
641 : fd_pod_insert_subpod( uchar * FD_RESTRICT pod,
642 : char const * FD_RESTRICT path,
643 0 : uchar const * FD_RESTRICT subpod ) {
644 0 : return fd_pod_insert( pod, path, FD_POD_VAL_TYPE_SUBPOD, fd_pod_max( subpod ), subpod );
645 0 : }
646 :
647 : /* fd_pod_insert_buf inserts the size val_sz buffer val into the pod
648 : at the given path. Returns offset where subpod inserted, 0 on
649 : failure. This offset is valid for the pod's lifetime or an
650 : invalidating operation is done on the pod.
651 :
652 : IMPORTANT! THIS IS AN INVALIDATING OPERATION */
653 :
654 : static inline ulong
655 : fd_pod_insert_buf( uchar * FD_RESTRICT pod,
656 : char const * FD_RESTRICT path,
657 : void const * FD_RESTRICT val,
658 49395 : ulong val_sz ) {
659 49395 : return fd_pod_insert( pod, path, FD_POD_VAL_TYPE_BUF, val_sz, val );
660 49395 : }
661 :
662 : /* fd_pod_insert_cstr inserts the cstr val into the pod at the given
663 : path. It is fine to insert NULL for val and/or the empty string
664 : (they will be recovered as such too). Returns offset where cstr
665 : inserted, 0 on failure. This offset is valid for the pod's lifetime
666 : or an invalidating operation is done on the pod.
667 :
668 : IMPORTANT! THIS IS AN INVALIDATING OPERATION */
669 :
670 : static inline ulong
671 : fd_pod_insert_cstr( uchar * FD_RESTRICT pod,
672 : char const * FD_RESTRICT path,
673 49254 : char const * FD_RESTRICT val ) {
674 49254 : return fd_pod_insert( pod, path, FD_POD_VAL_TYPE_CSTR, val ? (strlen( val ) + 1UL) : 0UL, val );
675 49254 : }
676 :
677 : /* fd_pod_insert_[type] inserts the [type] val into the pod at the given
678 : path. Returns offset where val was inserted, 0 on failure. The
679 : inserted representation might be compressed. This offset is valid
680 : for the pod's lifetime or an invalidating operation is done on the
681 : pod.
682 :
683 : IMPORTANT! THIS IS AN INVALIDATING OPERATION */
684 :
685 : #define FD_POD_IMPL(type,TYPE) \
686 : static inline ulong /* offset where val stored in pod, 0 on failure */ \
687 : fd_pod_insert_##type( uchar * FD_RESTRICT pod, \
688 : char const * FD_RESTRICT path, \
689 246018 : type val ) { \
690 246018 : return fd_pod_insert( pod, path, FD_POD_VAL_TYPE_##TYPE, sizeof(type), &val ); \
691 246018 : }
692 :
693 : FD_POD_IMPL( char, CHAR )
694 : FD_POD_IMPL( schar, SCHAR )
695 : FD_POD_IMPL( uchar, UCHAR )
696 : FD_POD_IMPL( float, FLOAT )
697 : #if FD_HAS_DOUBLE
698 : FD_POD_IMPL( double, DOUBLE )
699 : #endif
700 :
701 : #undef FD_POD_IMPL
702 :
703 : #define FD_POD_IMPL(type,TYPE) \
704 : static inline ulong \
705 : fd_pod_insert_##type( uchar * FD_RESTRICT pod, \
706 : char const * FD_RESTRICT path, \
707 148716 : type val ) { \
708 148716 : ulong val_sz = fd_ulong_svw_enc_sz( (ulong)val ); \
709 148716 : ulong off = fd_pod_alloc( pod, path, FD_POD_VAL_TYPE_##TYPE, val_sz ); \
710 148716 : if( FD_UNLIKELY( !off ) ) return 0UL; \
711 148716 : fd_ulong_svw_enc( pod + off, (ulong)val ); \
712 112824 : return off; \
713 148716 : }
714 :
715 : FD_POD_IMPL( ushort, USHORT )
716 : FD_POD_IMPL( uint, UINT )
717 : FD_POD_IMPL( ulong, ULONG )
718 :
719 : #undef FD_POD_IMPL
720 :
721 : #define FD_POD_IMPL(type,TYPE) \
722 : static inline ulong \
723 : fd_pod_insert_##type( uchar * FD_RESTRICT pod, \
724 : char const * FD_RESTRICT path, \
725 147336 : type val ) { \
726 147336 : ulong zz_val = fd_long_zz_enc( (long)val ); \
727 147336 : ulong val_sz = fd_ulong_svw_enc_sz( zz_val ); \
728 147336 : ulong off = fd_pod_alloc( pod, path, FD_POD_VAL_TYPE_##TYPE, val_sz ); \
729 147336 : if( FD_UNLIKELY( !off ) ) return 0UL; \
730 147336 : fd_ulong_svw_enc( pod + off, zz_val ); \
731 111930 : return off; \
732 147336 : }
733 :
734 : FD_POD_IMPL( short, SHORT )
735 : FD_POD_IMPL( int, INT )
736 : FD_POD_IMPL( long, LONG )
737 :
738 : #undef FD_POD_IMPL
739 :
740 : #if FD_HAS_INT128
741 : static inline ulong
742 : fd_pod_insert_uint128( uchar * FD_RESTRICT pod,
743 : char const * FD_RESTRICT path,
744 48945 : uint128 val ) {
745 48945 : ulong lo = (ulong) val;
746 48945 : ulong hi = (ulong)(val>>64);
747 48945 : ulong val_sz = fd_ulong_svw_enc_sz( lo ) + fd_ulong_svw_enc_sz( hi );
748 48945 : ulong off = fd_pod_alloc( pod, path, FD_POD_VAL_TYPE_UINT128, val_sz );
749 48945 : if( FD_UNLIKELY( !off ) ) return 0UL;
750 36873 : fd_ulong_svw_enc( fd_ulong_svw_enc( pod + off, lo ), hi );
751 36873 : return off;
752 48945 : }
753 :
754 : static inline ulong
755 : fd_pod_insert_int128( uchar * FD_RESTRICT pod,
756 : char const * FD_RESTRICT path,
757 49209 : int128 val ) {
758 49209 : uint128 zz_val = fd_int128_zz_enc( val );
759 49209 : ulong lo = (ulong) zz_val;
760 49209 : ulong hi = (ulong)(zz_val>>64);
761 49209 : ulong val_sz = fd_ulong_svw_enc_sz( lo ) + fd_ulong_svw_enc_sz( hi );
762 49209 : ulong off = fd_pod_alloc( pod, path, FD_POD_VAL_TYPE_INT128, val_sz );
763 49209 : if( FD_UNLIKELY( !off ) ) return 0UL;
764 37161 : fd_ulong_svw_enc( fd_ulong_svw_enc( pod + off, lo ), hi );
765 37161 : return off;
766 49209 : }
767 : #endif
768 :
769 : /* Specific query APIs ************************************************/
770 :
771 : /* fd_pod_query_subpod queries for the subpod in pod at path. Returns a
772 : pointer to the pod in the local address space on success or NULL on
773 : failure. The return pointer's lifetime is the pod's local join
774 : lifetime or an invalidating operation is done on the pod. */
775 :
776 : FD_FN_UNUSED static uchar const * /* Work around -Winline */
777 : fd_pod_query_subpod( uchar const * FD_RESTRICT pod,
778 96 : char const * FD_RESTRICT path ) {
779 96 : fd_pod_info_t info[1];
780 96 : if( FD_UNLIKELY( fd_pod_query( pod, path, info ) ) ||
781 96 : FD_UNLIKELY( info->val_type!=FD_POD_VAL_TYPE_SUBPOD ) ) return NULL;
782 0 : return (uchar const *)info->val;
783 96 : }
784 :
785 : /* fd_pod_query_buf queries for the buffer in pod at path. Returns the
786 : pointer to the buffer in the local address space on success or NULL
787 : on failure. On success, if opt_buf_sz is non-NULL, *opt_buf_sz will
788 : have the size of the buffer in bytes on return. *opt_buf_sz is
789 : untouched otherwise. The return pointer's lifetime is the pod's
790 : local join lifetime or an invalidating operation is done on the pod. */
791 :
792 : static inline void const *
793 : fd_pod_query_buf( uchar const * FD_RESTRICT pod,
794 : char const * FD_RESTRICT path,
795 86700 : ulong * FD_RESTRICT opt_buf_sz ) {
796 86700 : fd_pod_info_t info[1];
797 86700 : if( FD_UNLIKELY( fd_pod_query( pod, path, info ) ) ||
798 86700 : FD_UNLIKELY( info->val_type!=FD_POD_VAL_TYPE_BUF ) ) return NULL;
799 37308 : if( opt_buf_sz ) *opt_buf_sz = info->val_sz;
800 37308 : return info->val;
801 86700 : }
802 :
803 : /* fd_pod_query_cstr queries for the cstr in pod at path. Returns the
804 : pointer to the cstr in the local address on success or def on
805 : failure. The return pointer's lifetime is the pod's local join
806 : lifetime or an invalidating operation is done on the pod. */
807 :
808 : FD_FN_UNUSED FD_FN_PURE static char const * /* Work around -Winline */
809 : fd_pod_query_cstr( uchar const * FD_RESTRICT pod,
810 : char const * FD_RESTRICT path,
811 86538 : char const * FD_RESTRICT def ) {
812 86538 : fd_pod_info_t info[1];
813 86538 : if( FD_UNLIKELY( fd_pod_query( pod, path, info ) ) ||
814 86538 : FD_UNLIKELY( info->val_type!=FD_POD_VAL_TYPE_CSTR ) ) return def;
815 37317 : return info->val_sz ? (char const *)info->val : NULL;
816 86538 : }
817 :
818 : /* fd_pod_query_[type] queries for the [type] in pod at path. Returns
819 : the query result on success or def on failure. */
820 :
821 : #define FD_POD_IMPL(type,TYPE) \
822 : FD_FN_UNUSED FD_FN_PURE static type /* Work around -Winline */ \
823 : fd_pod_query_##type( uchar const * FD_RESTRICT pod, \
824 : char const * FD_RESTRICT path, \
825 258921 : type def ) { \
826 258921 : fd_pod_info_t info[1]; \
827 258921 : if( FD_UNLIKELY( fd_pod_query( pod, path, info ) ) || \
828 258921 : FD_UNLIKELY( info->val_type!=FD_POD_VAL_TYPE_##TYPE ) ) return def; \
829 258921 : return *(type const *)(info->val); \
830 258921 : }
831 :
832 : FD_POD_IMPL( char, CHAR )
833 : FD_POD_IMPL( schar, SCHAR )
834 : FD_POD_IMPL( uchar, UCHAR )
835 :
836 : #undef FD_POD_IMPL
837 :
838 : #define FD_POD_IMPL(type,TYPE) \
839 : FD_FN_UNUSED FD_FN_PURE static type /* Work around -Winline */ \
840 : fd_pod_query_##type( uchar const * FD_RESTRICT pod, \
841 : char const * FD_RESTRICT path, \
842 173082 : type def ) { \
843 173082 : fd_pod_info_t info[1]; \
844 173082 : if( FD_UNLIKELY( fd_pod_query( pod, path, info ) ) || \
845 173082 : FD_UNLIKELY( info->val_type!=FD_POD_VAL_TYPE_##TYPE ) ) return def; \
846 173082 : return FD_LOAD( type, info->val ); \
847 173082 : }
848 :
849 : FD_POD_IMPL( float, FLOAT )
850 : #if FD_HAS_DOUBLE
851 : FD_POD_IMPL( double, DOUBLE )
852 : #endif
853 :
854 : #undef FD_POD_IMPL
855 :
856 : #define FD_POD_IMPL(type,TYPE) \
857 : FD_FN_UNUSED FD_FN_PURE static type /* Work around -Winline */ \
858 : fd_pod_query_##type( uchar const * FD_RESTRICT pod, \
859 : char const * FD_RESTRICT path, \
860 261435 : type def ) { \
861 261435 : fd_pod_info_t info[1]; \
862 261435 : if( FD_UNLIKELY( fd_pod_query( pod, path, info ) ) || \
863 261435 : FD_UNLIKELY( info->val_type!=FD_POD_VAL_TYPE_##TYPE ) ) return def; \
864 261435 : ulong u; fd_ulong_svw_dec( (uchar const *)info->val, &u ); \
865 112836 : return (type)u; \
866 261435 : }
867 :
868 : FD_POD_IMPL( ushort, USHORT )
869 : FD_POD_IMPL( uint, UINT )
870 : FD_POD_IMPL( ulong, ULONG )
871 :
872 : #undef FD_POD_IMPL
873 :
874 : #define FD_POD_IMPL(type,TYPE) \
875 : FD_FN_UNUSED FD_FN_PURE static type /* Work around -Winline */ \
876 : fd_pod_query_##type( uchar const * FD_RESTRICT pod, \
877 : char const * FD_RESTRICT path, \
878 258768 : type def ) { \
879 258768 : fd_pod_info_t info[1]; \
880 258768 : if( FD_UNLIKELY( fd_pod_query( pod, path, info ) ) || \
881 258768 : FD_UNLIKELY( info->val_type!=FD_POD_VAL_TYPE_##TYPE ) ) return def; \
882 258768 : ulong u; fd_ulong_svw_dec( (uchar const *)info->val, &u ); \
883 111753 : return (type)fd_long_zz_dec( u ); \
884 258768 : }
885 :
886 : FD_POD_IMPL( short, SHORT )
887 : FD_POD_IMPL( int, INT )
888 : FD_POD_IMPL( long, LONG )
889 :
890 : #undef FD_POD_IMPL
891 :
892 : #if FD_HAS_INT128
893 : FD_FN_UNUSED FD_FN_PURE static uint128 /* Work around -Winline */
894 : fd_pod_query_uint128( uchar const * FD_RESTRICT pod,
895 : char const * FD_RESTRICT path,
896 85818 : uint128 def ) {
897 85818 : fd_pod_info_t info[1];
898 85818 : if( FD_UNLIKELY( fd_pod_query( pod, path, info ) ) ||
899 85818 : FD_UNLIKELY( info->val_type!=FD_POD_VAL_TYPE_UINT128 ) ) return def;
900 36885 : union { ulong w[2]; uint128 u; } tmp;
901 36885 : fd_ulong_svw_dec( fd_ulong_svw_dec( (uchar const *)info->val, tmp.w ), tmp.w+1 );
902 36885 : return tmp.u;
903 85818 : }
904 :
905 : FD_FN_UNUSED FD_FN_PURE static int128 /* Work around -Winline */
906 : fd_pod_query_int128( uchar const * FD_RESTRICT pod,
907 : char const * FD_RESTRICT path,
908 86370 : int128 def ) {
909 86370 : fd_pod_info_t info[1];
910 86370 : if( FD_UNLIKELY( fd_pod_query( pod, path, info ) ) ||
911 86370 : FD_UNLIKELY( info->val_type!=FD_POD_VAL_TYPE_INT128 ) ) return def;
912 37179 : union { ulong w[2]; uint128 u; } tmp;
913 37179 : fd_ulong_svw_dec( fd_ulong_svw_dec( (uchar const *)info->val, tmp.w ), tmp.w+1 );
914 37179 : return fd_int128_zz_dec( tmp.u );
915 86370 : }
916 : #endif
917 :
918 : FD_PROTOTYPES_END
919 :
920 : #endif /* HEADER_fd_src_pod_fd_pod_h */
|