Line data Source code
1 : #ifndef HEADER_fd_src_ballet_json_fd_jtok_h 2 : #define HEADER_fd_src_ballet_json_fd_jtok_h 3 : 4 : /* fd_jtok is a JSON tokenizer. 5 : 6 : Example: 7 : 8 : fd_jtok_t j[1]; 9 : fd_jtok_init( j, buf, buf_sz ); 10 : fd_jtok_str_t key; 11 : fd_jtok_obj_enter( j ); 12 : while( fd_jtok_obj_next( j, &key ) ) { 13 : if( fd_jtok_str_eq( &key, "commitment" ) ) fd_jtok_cstr ( j, cfg->commitment, sizeof(cfg->commitment) ); 14 : else if( fd_jtok_str_eq( &key, "minContextSlot" ) ) fd_jtok_ulong( j, &cfg->min_ctx_slot ); 15 : } 16 : if( fd_jtok_fini( j ) ) reject( fd_jtok_err_off( j ) ); 17 : 18 : Errors are sticky. After the first error every call returns 0. 19 : 20 : obj_next / arr_next leave the cursor on the member's value. If the 21 : caller does not read it, the next obj_next / arr_next / fini skips 22 : it. 23 : 24 : To read a value whose meaning depends on a later sibling (JSON-RPC 25 : "params" before "method"), capture it with fd_jtok_raw and tokenize 26 : the slice with a second cursor after the loop. 27 : 28 : Input must be RFC 8259 JSON. Strings must be valid UTF-8 with no 29 : raw control characters. Bytes after the top-level value are an 30 : error. If a key repeats, the last one wins. */ 31 : 32 : #include "../fd_ballet_base.h" 33 : 34 : /* FD_JTOK_ERR_* give error codes */ 35 : 36 0 : #define FD_JTOK_ERR_SYNTAX (1) /* malformed JSON */ 37 0 : #define FD_JTOK_ERR_TYPE (2) /* value has the wrong kind */ 38 0 : #define FD_JTOK_ERR_RANGE (3) /* number or string does not fit */ 39 0 : #define FD_JTOK_ERR_DEPTH (4) /* skipped value nests deeper than FD_JTOK_SKIP_DEPTH_MAX */ 40 0 : #define FD_JTOK_ERR_TRAIL (5) /* bytes after the top-level value */ 41 0 : #define FD_JTOK_ERR_USAGE (6) /* getter with no pending value, or fini inside a container */ 42 : 43 : /* FD_JTOK_* give value kinds (fd_jtok_peek) */ 44 : 45 0 : #define FD_JTOK_OBJ (1) /* { ... } */ 46 0 : #define FD_JTOK_ARR (2) /* [ ... ] */ 47 0 : #define FD_JTOK_STR (3) /* " ... " */ 48 0 : #define FD_JTOK_INT (4) /* number without fraction or exponent */ 49 0 : #define FD_JTOK_NUM (5) /* any other number */ 50 0 : #define FD_JTOK_BOOL (6) /* false/true */ 51 0 : #define FD_JTOK_NULL (7) /* null */ 52 : 53 : /* FD_JTOK_SKIP_DEPTH_MAX is the max nesting of a value consumed by 54 : skip, raw, or auto-skip. */ 55 : 56 0 : #define FD_JTOK_SKIP_DEPTH_MAX (64UL) 57 : 58 : /* FD_JTOK_NUM_SZ_MAX is the max length of a number literal accepted by 59 : fd_jtok_double. */ 60 : 61 : #define FD_JTOK_NUM_SZ_MAX (64UL) 62 : 63 : /* fd_jtok_str_t spans encoded string bytes from the input buffer. */ 64 : 65 : struct fd_jtok_str { 66 : char const * ptr; 67 : ulong sz; 68 : }; 69 : 70 : typedef struct fd_jtok_str fd_jtok_str_t; 71 : 72 : /* fd_jtok_t is the parser cursor. */ 73 : 74 : struct fd_jtok { 75 : char const * beg; 76 : char const * cur; 77 : char const * end; 78 : int err; 79 : int pending; 80 : int first; 81 : ulong depth; 82 : }; 83 : 84 : typedef struct fd_jtok fd_jtok_t; 85 : 86 : FD_PROTOTYPES_BEGIN 87 : 88 : /* fd_jtok_init starts tokenizing the sz bytes at buf. buf must 89 : outlive any fd_jtok_str_t taken from it. Returns j with the 90 : top-level value pending. An empty input (sz 0, buf may then be 91 : NULL) is not valid JSON and starts out with FD_JTOK_ERR_SYNTAX. */ 92 : 93 : fd_jtok_t * 94 : fd_jtok_init( fd_jtok_t * j, 95 : void const * buf, 96 : ulong sz ); 97 : 98 : /* fd_jtok_fini skips the top-level value if still pending and checks 99 : that only whitespace remains. Returns the error code, 0 on 100 : success. Call it at top level, after the last obj_next / arr_next 101 : returned 0. */ 102 : 103 : int 104 : fd_jtok_fini( fd_jtok_t * j ); 105 : 106 : FD_FN_PURE static inline int 107 0 : fd_jtok_err( fd_jtok_t const * j ) { 108 0 : return j->err; 109 0 : } 110 : 111 : /* fd_jtok_err_off returns the input offset at which the error was 112 : detected. */ 113 : 114 : FD_FN_PURE static inline ulong 115 0 : fd_jtok_err_off( fd_jtok_t const * j ) { 116 0 : return (ulong)( j->cur - j->beg ); 117 0 : } 118 : 119 : /* fd_jtok_peek returns the FD_JTOK_* kind of the pending value, 0 on 120 : error. The value stays pending. */ 121 : 122 : int 123 : fd_jtok_peek( fd_jtok_t * j ); 124 : 125 : /* fd_jtok_obj_enter consumes the opening brace of the pending object. 126 : FD_JTOK_ERR_TYPE if it is not an object. */ 127 : 128 : void 129 : fd_jtok_obj_enter( fd_jtok_t * j ); 130 : 131 : /* fd_jtok_obj_next advances to the next member of the innermost 132 : object, skipping the previous value if it was not read. Returns 1 133 : with *key set and the value pending, or 0 after the closing brace 134 : (or on error). */ 135 : 136 : int 137 : fd_jtok_obj_next( fd_jtok_t * j, 138 : fd_jtok_str_t * key ); 139 : 140 : /* Array equivalents of obj_enter / obj_next. */ 141 : 142 : void 143 : fd_jtok_arr_enter( fd_jtok_t * j ); 144 : 145 : int 146 : fd_jtok_arr_next( fd_jtok_t * j ); 147 : 148 : /* Scalar getters consume the pending value into *out. Wrong kind is 149 : FD_JTOK_ERR_TYPE, out of range is FD_JTOK_ERR_RANGE. *out is 150 : written on success only. ulong and long require an INT. double 151 : accepts INT or NUM up to FD_JTOK_NUM_SZ_MAX chars and fails with 152 : FD_JTOK_ERR_RANGE if the literal overflows or underflows a finite 153 : double (e.g. 1e999, 1e-999). bool stores 0 or 1. */ 154 : 155 : void fd_jtok_ulong ( fd_jtok_t * j, ulong * out ); 156 : void fd_jtok_long ( fd_jtok_t * j, long * out ); 157 : void fd_jtok_double( fd_jtok_t * j, double * out ); 158 : void fd_jtok_bool ( fd_jtok_t * j, int * out ); 159 : void fd_jtok_null ( fd_jtok_t * j ); 160 : 161 : /* fd_jtok_cstr consumes the pending string, decodes escapes to UTF-8 162 : and writes it NUL-terminated to the out_sz bytes at out. 163 : FD_JTOK_ERR_RANGE if it does not fit or contains an escaped NUL. 164 : On failure out[0] is '\0' (if out_sz>0). */ 165 : 166 : void 167 : fd_jtok_cstr( fd_jtok_t * j, 168 : char * out, 169 : ulong out_sz ); 170 : 171 : /* fd_jtok_str consumes the pending string as a zero-copy view. */ 172 : 173 : void 174 : fd_jtok_str( fd_jtok_t * j, 175 : fd_jtok_str_t * out ); 176 : 177 : /* fd_jtok_skip consumes the pending value. */ 178 : 179 : void 180 : fd_jtok_skip( fd_jtok_t * j ); 181 : 182 : /* fd_jtok_raw consumes the pending value and returns the input bytes 183 : from its first to its last character. */ 184 : 185 : void 186 : fd_jtok_raw( fd_jtok_t * j, 187 : char const ** ptr, 188 : ulong * sz ); 189 : 190 : /* fd_jtok_str_decode decodes the escapes in s to UTF-8 and writes the 191 : result NUL-terminated to the out_sz bytes at out. Returns the 192 : decoded length excluding the terminator, or -1 if it does not fit or 193 : s contains an escaped NUL (out[0] is then '\0' if out_sz>0). */ 194 : 195 : long 196 : fd_jtok_str_decode( fd_jtok_str_t const * s, 197 : char * out, 198 : ulong out_sz ); 199 : 200 : /* fd_jtok_str_eq returns 1 if s decodes to exactly lit. Escapes in s 201 : are decoded before comparing. */ 202 : 203 : FD_FN_PURE int 204 : fd_jtok_str_eq( fd_jtok_str_t const * s, 205 : char const * lit ); 206 : 207 : FD_PROTOTYPES_END 208 : 209 : #endif /* HEADER_fd_src_ballet_json_fd_jtok_h */