mirror of
https://github.com/warmcat/libwebsockets.git
synced 2025-03-09 00:00:04 +01:00
lws_json_simple_find and lws_nstrstr
String helpers for scanning non-NUL-delimited strings safely, and very cheap simple string match based JSON parse for cases that make sense for it... for more complex cases, do a full JSON parse.
This commit is contained in:
parent
d4a7c7134a
commit
5a937fa830
3 changed files with 240 additions and 0 deletions
|
@ -180,6 +180,68 @@ lws_strncpy(char *dest, const char *src, size_t size);
|
|||
lws_strncpy(dest, src, (size_t)(size1 + 1) < (size_t)(destsize) ? \
|
||||
(size_t)(size1 + 1) : (size_t)(destsize))
|
||||
|
||||
/**
|
||||
* lws_nstrstr(): like strstr for length-based strings without terminating NUL
|
||||
*
|
||||
* \param buf: the string to search
|
||||
* \param len: the length of the string to search
|
||||
* \param name: the substring to search for
|
||||
* \param nl: the length of name
|
||||
*
|
||||
* Returns NULL if \p name is not present in \p buf. Otherwise returns the
|
||||
* address of the first instance of \p name in \p buf.
|
||||
*
|
||||
* Neither buf nor name need to be NUL-terminated.
|
||||
*/
|
||||
LWS_VISIBLE LWS_EXTERN const char *
|
||||
lws_nstrstr(const char *buf, size_t len, const char *name, size_t nl);
|
||||
|
||||
/**
|
||||
* lws_json_simple_find(): dumb JSON string parser
|
||||
*
|
||||
* \param buf: the JSON to search
|
||||
* \param len: the length of the JSON to search
|
||||
* \param name: the name field to search the JSON for, eg, "\"myname\":"
|
||||
* \param alen: set to the length of the argument part if non-NULL return
|
||||
*
|
||||
* Either returns NULL if \p name is not present in buf, or returns a pointer
|
||||
* to the argument body of the first instance of \p name, and sets *alen to the
|
||||
* length of the argument body.
|
||||
*
|
||||
* This can cheaply handle fishing out, eg, myarg from {"myname": "myarg"} by
|
||||
* searching for "\"myname\":". It will return a pointer to myarg and set *alen
|
||||
* to 5. It equally handles args like "myname": true, or "myname":false, and
|
||||
* null or numbers are all returned as delimited strings.
|
||||
*
|
||||
* Anything more complicated like the value is a subobject or array, you should
|
||||
* parse it using a full parser like lejp. This is suitable is the JSON is
|
||||
* and will remain short and simple, and contains well-known names amongst other
|
||||
* extensible JSON members.
|
||||
*/
|
||||
LWS_VISIBLE LWS_EXTERN const char *
|
||||
lws_json_simple_find(const char *buf, size_t len, const char *name, size_t *alen);
|
||||
|
||||
/**
|
||||
* lws_json_simple_strcmp(): dumb JSON string comparison
|
||||
*
|
||||
* \param buf: the JSON to search
|
||||
* \param len: the length of the JSON to search
|
||||
* \param name: the name field to search the JSON for, eg, "\"myname\":"
|
||||
* \param comp: return a strcmp of this and the discovered argument
|
||||
*
|
||||
* Helper that combines lws_json_simple_find() with strcmp() if it was found.
|
||||
* If the \p name was not found, returns -1. Otherwise returns a strcmp()
|
||||
* between what was found and \p comp, ie, return 0 if they match or something
|
||||
* else if they don't.
|
||||
*
|
||||
* If the JSON is relatively simple and you want to target constrained
|
||||
* devices, this can be a good choice. If the JSON may be complex, you
|
||||
* should use a full JSON parser.
|
||||
*/
|
||||
LWS_VISIBLE LWS_EXTERN int
|
||||
lws_json_simple_strcmp(const char *buf, size_t len, const char *name, const char *comp);
|
||||
|
||||
|
||||
/**
|
||||
* lws_hex_to_byte_array(): convert hex string like 0123456789ab into byte data
|
||||
*
|
||||
|
|
|
@ -345,6 +345,117 @@ lws_strdup(const char *s)
|
|||
return d;
|
||||
}
|
||||
|
||||
const char *
|
||||
lws_nstrstr(const char *buf, size_t len, const char *name, size_t nl)
|
||||
{
|
||||
const char *end = buf + len - nl + 1;
|
||||
size_t n;
|
||||
|
||||
if (nl > len)
|
||||
/* it cannot be found if the needle is longer than the haystack */
|
||||
return NULL;
|
||||
|
||||
while (buf < end) {
|
||||
if (*buf != name[0]) {
|
||||
buf++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (nl == 1)
|
||||
/* single char match, we are done */
|
||||
return buf;
|
||||
|
||||
if (buf[nl - 1] == name[nl - 1]) {
|
||||
/*
|
||||
* This is looking interesting then... the first
|
||||
* and last chars match, let's check the insides
|
||||
*/
|
||||
n = 1;
|
||||
while (n < nl && buf[n] == name[n])
|
||||
n++;
|
||||
|
||||
if (n == nl)
|
||||
/* it's a hit */
|
||||
return buf;
|
||||
}
|
||||
|
||||
buf++;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* name wants to be something like "\"myname\":"
|
||||
*/
|
||||
|
||||
const char *
|
||||
lws_json_simple_find(const char *buf, size_t len, const char *name, size_t *alen)
|
||||
{
|
||||
size_t nl = strlen(name);
|
||||
const char *np = lws_nstrstr(buf, len, name, nl),
|
||||
*end = buf + len, *as;
|
||||
int qu = 0;
|
||||
|
||||
if (!np)
|
||||
return NULL;
|
||||
|
||||
np += nl;
|
||||
|
||||
while (np < end && (*np == ' ' || *np == '\t'))
|
||||
np++;
|
||||
|
||||
if (np >= end - 1)
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* The arg could be lots of things after "name": with JSON, commonly a
|
||||
* string like "mystring", true, false, null, [...] or {...} ... we want
|
||||
* to handle common, simple cases cheaply with this; the user can choose
|
||||
* a full JSON parser like lejp if it's complicated. So if no opening
|
||||
* quote, return until a terminator like , ] }. If there's an opening
|
||||
* quote, return until closing quote, handling escaped quotes.
|
||||
*/
|
||||
|
||||
if (*np == '\"') {
|
||||
qu = 1;
|
||||
np++;
|
||||
}
|
||||
|
||||
as = np;
|
||||
while (np < end &&
|
||||
(!qu || *np != '\"') && /* end quote is EOT if quoted */
|
||||
(qu || (*np != '}' && *np != ']' && *np != ',')) /* delimiters */
|
||||
) {
|
||||
if (qu && *np == '\\') /* skip next char if quoted escape */
|
||||
np++;
|
||||
np++;
|
||||
}
|
||||
|
||||
if (np == end)
|
||||
return NULL;
|
||||
|
||||
*alen = lws_ptr_diff(np, as);
|
||||
|
||||
return as;
|
||||
}
|
||||
|
||||
int
|
||||
lws_json_simple_strcmp(const char *buf, size_t len, const char *name,
|
||||
const char *comp)
|
||||
{
|
||||
size_t al;
|
||||
const char *hit = lws_json_simple_find(buf, len, name, &al);
|
||||
|
||||
if (!hit)
|
||||
return -1;
|
||||
|
||||
if (al != strlen(comp))
|
||||
return -1;
|
||||
|
||||
return strncmp(hit, comp, al);
|
||||
}
|
||||
|
||||
static const char *hex = "0123456789ABCDEF";
|
||||
|
||||
const char *
|
||||
|
|
|
@ -417,6 +417,73 @@ int main(int argc, const char **argv)
|
|||
return 1;
|
||||
}
|
||||
|
||||
/* sanity check lws_nstrstr() */
|
||||
|
||||
{
|
||||
static const char *t1 = "abc123456";
|
||||
const char *mcp;
|
||||
|
||||
mcp = lws_nstrstr(t1, strlen(t1), "abc", 3);
|
||||
if (mcp != t1) {
|
||||
lwsl_err("%s: lws_nstrstr 1 failed\n", __func__);
|
||||
return 1;
|
||||
}
|
||||
mcp = lws_nstrstr(t1, strlen(t1), "def", 3);
|
||||
if (mcp != NULL) {
|
||||
lwsl_err("%s: lws_nstrstr 2 failed\n", __func__);
|
||||
return 1;
|
||||
}
|
||||
mcp = lws_nstrstr(t1, strlen(t1), "456", 3);
|
||||
if (mcp != t1 + 6) {
|
||||
lwsl_err("%s: lws_nstrstr 3 failed: %p\n", __func__, mcp);
|
||||
return 1;
|
||||
}
|
||||
mcp = lws_nstrstr(t1, strlen(t1), "1", 1);
|
||||
if (mcp != t1 + 3) {
|
||||
lwsl_err("%s: lws_nstrstr 4 failed\n", __func__);
|
||||
return 1;
|
||||
}
|
||||
mcp = lws_nstrstr(t1, strlen(t1), "abc1234567", 10);
|
||||
if (mcp != NULL) {
|
||||
lwsl_err("%s: lws_nstrstr 5 failed\n", __func__);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* sanity check lws_json_simple_find() */
|
||||
|
||||
{
|
||||
static const char *t1 = "{\"myname1\":true,"
|
||||
"\"myname2\":\"string\", "
|
||||
"\"myname3\": 123}";
|
||||
size_t alen;
|
||||
const char *mcp;
|
||||
|
||||
mcp = lws_json_simple_find(t1, strlen(t1), "\"myname1\":", &alen);
|
||||
if (mcp != t1 + 11 || alen != 4) {
|
||||
lwsl_err("%s: lws_json_simple_find 1 failed: (%d) %s\n", __func__, (int)alen, mcp);
|
||||
return 1;
|
||||
}
|
||||
|
||||
mcp = lws_json_simple_find(t1, strlen(t1), "\"myname2\":", &alen);
|
||||
if (mcp != t1 + 27 || alen != 6) {
|
||||
lwsl_err("%s: lws_json_simple_find 2 failed\n", __func__);
|
||||
return 1;
|
||||
}
|
||||
|
||||
mcp = lws_json_simple_find(t1, strlen(t1), "\"myname3\":", &alen);
|
||||
if (mcp != t1 + 47 || alen != 3) {
|
||||
lwsl_err("%s: lws_json_simple_find 3 failed\n", __func__);
|
||||
return 1;
|
||||
}
|
||||
|
||||
mcp = lws_json_simple_find(t1, strlen(t1), "\"nope\":", &alen);
|
||||
if (mcp != NULL) {
|
||||
lwsl_err("%s: lws_json_simple_find 4 failed\n", __func__);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
p = lws_cmdline_option(argc, argv, "-s");
|
||||
|
||||
for (n = 0; n < (int)LWS_ARRAY_SIZE(tests); n++) {
|
||||
|
|
Loading…
Add table
Reference in a new issue