310 lines
9.1 KiB
C
310 lines
9.1 KiB
C
/*
|
|
* libwebsockets - small server side websockets and web server implementation
|
|
*
|
|
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to
|
|
* deal in the Software without restriction, including without limitation the
|
|
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
* sell copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
* IN THE SOFTWARE.
|
|
*/
|
|
|
|
/** \defgroup lejp JSON parser
|
|
* ##JSON parsing related functions
|
|
* \ingroup lwsapi
|
|
*
|
|
* LEJP is an extremely lightweight JSON stream parser included in lws.
|
|
*/
|
|
//@{
|
|
struct lejp_ctx;
|
|
|
|
#if !defined(LWS_ARRAY_SIZE)
|
|
#define LWS_ARRAY_SIZE(_x) (sizeof(_x) / sizeof(_x[0]))
|
|
#endif
|
|
#define LEJP_FLAG_WS_KEEP 64
|
|
#define LEJP_FLAG_WS_COMMENTLINE 32
|
|
|
|
enum lejp_states {
|
|
LEJP_IDLE = 0,
|
|
LEJP_MEMBERS = 1,
|
|
LEJP_M_P = 2,
|
|
LEJP_MP_STRING = LEJP_FLAG_WS_KEEP | 3,
|
|
LEJP_MP_STRING_ESC = LEJP_FLAG_WS_KEEP | 4,
|
|
LEJP_MP_STRING_ESC_U1 = LEJP_FLAG_WS_KEEP | 5,
|
|
LEJP_MP_STRING_ESC_U2 = LEJP_FLAG_WS_KEEP | 6,
|
|
LEJP_MP_STRING_ESC_U3 = LEJP_FLAG_WS_KEEP | 7,
|
|
LEJP_MP_STRING_ESC_U4 = LEJP_FLAG_WS_KEEP | 8,
|
|
LEJP_MP_DELIM = 9,
|
|
LEJP_MP_VALUE = 10,
|
|
LEJP_MP_VALUE_NUM_INT = LEJP_FLAG_WS_KEEP | 11,
|
|
LEJP_MP_VALUE_NUM_EXP = LEJP_FLAG_WS_KEEP | 12,
|
|
LEJP_MP_VALUE_TOK = LEJP_FLAG_WS_KEEP | 13,
|
|
LEJP_MP_COMMA_OR_END = 14,
|
|
LEJP_MP_ARRAY_END = 15,
|
|
};
|
|
|
|
enum lejp_reasons {
|
|
LEJP_CONTINUE = -1,
|
|
LEJP_REJECT_IDLE_NO_BRACE = -2,
|
|
LEJP_REJECT_MEMBERS_NO_CLOSE = -3,
|
|
LEJP_REJECT_MP_NO_OPEN_QUOTE = -4,
|
|
LEJP_REJECT_MP_STRING_UNDERRUN = -5,
|
|
LEJP_REJECT_MP_ILLEGAL_CTRL = -6,
|
|
LEJP_REJECT_MP_STRING_ESC_ILLEGAL_ESC = -7,
|
|
LEJP_REJECT_ILLEGAL_HEX = -8,
|
|
LEJP_REJECT_MP_DELIM_MISSING_COLON = -9,
|
|
LEJP_REJECT_MP_DELIM_BAD_VALUE_START = -10,
|
|
LEJP_REJECT_MP_VAL_NUM_INT_NO_FRAC = -11,
|
|
LEJP_REJECT_MP_VAL_NUM_FORMAT = -12,
|
|
LEJP_REJECT_MP_VAL_NUM_EXP_BAD_EXP = -13,
|
|
LEJP_REJECT_MP_VAL_TOK_UNKNOWN = -14,
|
|
LEJP_REJECT_MP_C_OR_E_UNDERF = -15,
|
|
LEJP_REJECT_MP_C_OR_E_NOTARRAY = -16,
|
|
LEJP_REJECT_MP_ARRAY_END_MISSING = -17,
|
|
LEJP_REJECT_STACK_OVERFLOW = -18,
|
|
LEJP_REJECT_MP_DELIM_ISTACK = -19,
|
|
LEJP_REJECT_NUM_TOO_LONG = -20,
|
|
LEJP_REJECT_MP_C_OR_E_NEITHER = -21,
|
|
LEJP_REJECT_UNKNOWN = -22,
|
|
LEJP_REJECT_CALLBACK = -23
|
|
};
|
|
|
|
#define LEJP_FLAG_CB_IS_VALUE 64
|
|
|
|
enum lejp_callbacks {
|
|
LEJPCB_CONSTRUCTED = 0,
|
|
LEJPCB_DESTRUCTED = 1,
|
|
|
|
LEJPCB_START = 2,
|
|
LEJPCB_COMPLETE = 3,
|
|
LEJPCB_FAILED = 4,
|
|
|
|
LEJPCB_PAIR_NAME = 5,
|
|
|
|
LEJPCB_VAL_TRUE = LEJP_FLAG_CB_IS_VALUE | 6,
|
|
LEJPCB_VAL_FALSE = LEJP_FLAG_CB_IS_VALUE | 7,
|
|
LEJPCB_VAL_NULL = LEJP_FLAG_CB_IS_VALUE | 8,
|
|
LEJPCB_VAL_NUM_INT = LEJP_FLAG_CB_IS_VALUE | 9,
|
|
LEJPCB_VAL_NUM_FLOAT = LEJP_FLAG_CB_IS_VALUE | 10,
|
|
LEJPCB_VAL_STR_START = 11, /* notice handle separately */
|
|
LEJPCB_VAL_STR_CHUNK = LEJP_FLAG_CB_IS_VALUE | 12,
|
|
LEJPCB_VAL_STR_END = LEJP_FLAG_CB_IS_VALUE | 13,
|
|
|
|
LEJPCB_ARRAY_START = 14,
|
|
LEJPCB_ARRAY_END = 15,
|
|
|
|
LEJPCB_OBJECT_START = 16,
|
|
LEJPCB_OBJECT_END = 17,
|
|
|
|
LEJPCB_USER_START = 32,
|
|
};
|
|
|
|
/**
|
|
* _lejp_callback() - User parser actions
|
|
* \param ctx: LEJP context
|
|
* \param reason: Callback reason
|
|
*
|
|
* Your user callback is associated with the context at construction time,
|
|
* and receives calls as the parsing progresses.
|
|
*
|
|
* All of the callbacks may be ignored and just return 0.
|
|
*
|
|
* The reasons it might get called, found in @reason, are:
|
|
*
|
|
* LEJPCB_CONSTRUCTED: The context was just constructed... you might want to
|
|
* perform one-time allocation for the life of the context.
|
|
*
|
|
* LEJPCB_DESTRUCTED: The context is being destructed... if you made any
|
|
* allocations at construction-time, you can free them now
|
|
*
|
|
* LEJPCB_START: Parsing is beginning at the first byte of input
|
|
*
|
|
* LEJPCB_COMPLETE: Parsing has completed successfully. You'll get a 0 or
|
|
* positive return code from lejp_parse indicating the
|
|
* amount of unused bytes left in the input buffer
|
|
*
|
|
* LEJPCB_FAILED: Parsing failed. You'll get a negative error code
|
|
* returned from lejp_parse
|
|
*
|
|
* LEJPCB_PAIR_NAME: When a "name":"value" pair has had the name parsed,
|
|
* this callback occurs. You can find the new name at
|
|
* the end of ctx->path[]
|
|
*
|
|
* LEJPCB_VAL_TRUE: The "true" value appeared
|
|
*
|
|
* LEJPCB_VAL_FALSE: The "false" value appeared
|
|
*
|
|
* LEJPCB_VAL_NULL: The "null" value appeared
|
|
*
|
|
* LEJPCB_VAL_NUM_INT: A string representing an integer is in ctx->buf
|
|
*
|
|
* LEJPCB_VAL_NUM_FLOAT: A string representing a float is in ctx->buf
|
|
*
|
|
* LEJPCB_VAL_STR_START: We are starting to parse a string, no data yet
|
|
*
|
|
* LEJPCB_VAL_STR_CHUNK: We filled the string buffer in the ctx, but it's not
|
|
* the end of the string. We produce this to spill the
|
|
* intermediate buffer to the user code, so we can handle
|
|
* huge JSON strings using only the small buffer in the
|
|
* ctx. If the whole JSON string fits in the ctx buffer,
|
|
* you won't get these callbacks.
|
|
*
|
|
* LEJPCB_VAL_STR_END: String parsing has completed, the last chunk of the
|
|
* string is in ctx->buf.
|
|
*
|
|
* LEJPCB_ARRAY_START: An array started
|
|
*
|
|
* LEJPCB_ARRAY_END: An array ended
|
|
*
|
|
* LEJPCB_OBJECT_START: An object started
|
|
*
|
|
* LEJPCB_OBJECT_END: An object ended
|
|
*/
|
|
LWS_EXTERN signed char _lejp_callback(struct lejp_ctx *ctx, char reason);
|
|
|
|
typedef signed char (*lejp_callback)(struct lejp_ctx *ctx, char reason);
|
|
|
|
#ifndef LEJP_MAX_PARSING_STACK_DEPTH
|
|
#define LEJP_MAX_PARSING_STACK_DEPTH 8
|
|
#endif
|
|
#ifndef LEJP_MAX_DEPTH
|
|
#define LEJP_MAX_DEPTH 16
|
|
#endif
|
|
#ifndef LEJP_MAX_INDEX_DEPTH
|
|
#define LEJP_MAX_INDEX_DEPTH 12
|
|
#endif
|
|
#ifndef LEJP_MAX_PATH
|
|
#define LEJP_MAX_PATH 192
|
|
#endif
|
|
#ifndef LEJP_STRING_CHUNK
|
|
/* must be >= 30 to assemble floats */
|
|
#define LEJP_STRING_CHUNK 254
|
|
#endif
|
|
|
|
enum num_flags {
|
|
LEJP_SEEN_MINUS = (1 << 0),
|
|
LEJP_SEEN_POINT = (1 << 1),
|
|
LEJP_SEEN_POST_POINT = (1 << 2),
|
|
LEJP_SEEN_EXP = (1 << 3)
|
|
};
|
|
|
|
struct _lejp_stack {
|
|
char s; /* lejp_state stack*/
|
|
char p; /* path length */
|
|
char i; /* index array length */
|
|
char b; /* user bitfield */
|
|
};
|
|
|
|
struct _lejp_parsing_stack {
|
|
void *user; /* private to the stack level */
|
|
signed char (*callback)(struct lejp_ctx *ctx, char reason);
|
|
const char * const *paths;
|
|
uint8_t count_paths;
|
|
uint8_t ppos;
|
|
uint8_t path_match;
|
|
};
|
|
|
|
struct lejp_ctx {
|
|
|
|
/* sorted by type for most compact alignment
|
|
*
|
|
* pointers
|
|
*/
|
|
void *user;
|
|
|
|
/* arrays */
|
|
|
|
struct _lejp_parsing_stack pst[LEJP_MAX_PARSING_STACK_DEPTH];
|
|
struct _lejp_stack st[LEJP_MAX_DEPTH];
|
|
uint16_t i[LEJP_MAX_INDEX_DEPTH]; /* index array */
|
|
uint16_t wild[LEJP_MAX_INDEX_DEPTH]; /* index array */
|
|
char path[LEJP_MAX_PATH];
|
|
char buf[LEJP_STRING_CHUNK + 1];
|
|
|
|
/* size_t */
|
|
|
|
size_t path_stride; /* 0 means default ptr size, else stride */
|
|
|
|
/* int */
|
|
|
|
uint32_t line;
|
|
|
|
/* short */
|
|
|
|
uint16_t uni;
|
|
#define LEJP_FLAG_FEAT_OBJECT_INDEXES (1 << 0)
|
|
#define LEJP_FLAG_FEAT_LEADING_WC (1 << 1)
|
|
#define LEJP_FLAG_LATEST \
|
|
(LEJP_FLAG_FEAT_OBJECT_INDEXES | \
|
|
LEJP_FLAG_FEAT_LEADING_WC)
|
|
uint16_t flags;
|
|
|
|
/* char */
|
|
|
|
uint8_t npos;
|
|
uint8_t dcount;
|
|
uint8_t f;
|
|
uint8_t sp; /* stack head */
|
|
uint8_t ipos; /* index stack depth */
|
|
uint8_t count_paths;
|
|
uint8_t path_match;
|
|
uint8_t path_match_len;
|
|
uint8_t wildcount;
|
|
uint8_t pst_sp; /* parsing stack head */
|
|
uint8_t outer_array;
|
|
};
|
|
|
|
LWS_VISIBLE LWS_EXTERN void
|
|
lejp_construct(struct lejp_ctx *ctx,
|
|
signed char (*callback)(struct lejp_ctx *ctx, char reason),
|
|
void *user, const char * const *paths, unsigned char paths_count);
|
|
|
|
LWS_VISIBLE LWS_EXTERN void
|
|
lejp_destruct(struct lejp_ctx *ctx);
|
|
|
|
LWS_VISIBLE LWS_EXTERN int
|
|
lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len);
|
|
|
|
LWS_VISIBLE LWS_EXTERN void
|
|
lejp_change_callback(struct lejp_ctx *ctx,
|
|
signed char (*callback)(struct lejp_ctx *ctx, char reason));
|
|
|
|
/*
|
|
* push the current paths / paths_count and lejp_cb to a stack in the ctx, and
|
|
* start using the new ones
|
|
*/
|
|
LWS_VISIBLE LWS_EXTERN int
|
|
lejp_parser_push(struct lejp_ctx *ctx, void *user, const char * const *paths,
|
|
unsigned char paths_count, lejp_callback lejp_cb);
|
|
|
|
/*
|
|
* pop the previously used paths / paths_count and lejp_cb, and continue
|
|
* parsing using those as before
|
|
*/
|
|
LWS_VISIBLE LWS_EXTERN int
|
|
lejp_parser_pop(struct lejp_ctx *ctx);
|
|
|
|
/* exported for use when reevaluating a path for use with a subcontext */
|
|
LWS_VISIBLE LWS_EXTERN void
|
|
lejp_check_path_match(struct lejp_ctx *ctx);
|
|
|
|
LWS_VISIBLE LWS_EXTERN int
|
|
lejp_get_wildcard(struct lejp_ctx *ctx, int wildcard, char *dest, int len);
|
|
|
|
LWS_VISIBLE LWS_EXTERN const char *
|
|
lejp_error_to_string(int e);
|
|
//@}
|