feat: cJSON, Gerätenamen und HTTP-Parser
This commit is contained in:
+4
-1
@@ -43,7 +43,10 @@ set(NL_FW_SOURCES
|
||||
src/sys/nl_console_cmd.c
|
||||
src/sys/nl_console.c
|
||||
src/sys/fwup_hook_default.c
|
||||
src/sys/sdk_stubs.c)
|
||||
src/sys/sdk_stubs.c
|
||||
src/net/nl_names.c
|
||||
src/net/nl_http.c
|
||||
third_party/cJSON/cJSON.c)
|
||||
|
||||
set(NL_FW ninlight_${NINLIGHT_DEVICE})
|
||||
add_executable(${NL_FW} ${NL_SDK_BOOT_SOURCES} ${NL_FW_SOURCES} ${NL_SDK_OBJECTS})
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ include_directories(
|
||||
${SDK_DIR}/Platform/Common/crypto ${SDK_DIR}/Platform/Inc ${SDK_DIR}/Platform/Sys
|
||||
${SDK_DIR}/Src/App/OTA ${SDK_DIR}/Src/App/dhcpserver ${SDK_DIR}/Src/App/dnsserver ${SDK_DIR}/Src/App/httpclient
|
||||
${SDK_DIR}/Src/Network/lwip2.0.3/include ${SDK_DIR}/Src/Network/api2.0.3
|
||||
${CMAKE_SOURCE_DIR}/src)
|
||||
${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/third_party/cJSON)
|
||||
|
||||
foreach(dir Platform/Common Platform/Drivers Platform/Sys Src/Network Src/OS/RTOS
|
||||
Src/App/dhcpserver Src/App/dnsserver Src/App/httpclient Src/App/OTA)
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
#include "net/nl_http.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
|
||||
static const char *find(const char *buf, size_t len, const char *pat)
|
||||
{
|
||||
size_t n = strlen(pat);
|
||||
for (size_t i = 0; i + n <= len; i++)
|
||||
if (memcmp(buf + i, pat, n) == 0)
|
||||
return buf + i;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Case-insensitive "name:" match at the start of a header line; returns the value start. */
|
||||
static const char *header_value(const char *line, const char *line_end, const char *name)
|
||||
{
|
||||
size_t n = strlen(name);
|
||||
if ((size_t)(line_end - line) <= n || line[n] != ':')
|
||||
return NULL;
|
||||
for (size_t i = 0; i < n; i++)
|
||||
if (tolower((unsigned char)line[i]) != name[i])
|
||||
return NULL;
|
||||
line += n + 1;
|
||||
while (line < line_end && (*line == ' ' || *line == '\t'))
|
||||
line++;
|
||||
return line;
|
||||
}
|
||||
|
||||
nl_http_parse_t nl_http_parse(const char *buf, size_t len, nl_http_request_t *req)
|
||||
{
|
||||
const char *hdr_end = find(buf, len, "\r\n\r\n");
|
||||
const char *p, *line_end, *sp1, *sp2;
|
||||
size_t header_len, content_length = 0, plen;
|
||||
|
||||
if (!hdr_end)
|
||||
return len >= NL_HTTP_MAX_REQUEST ? NL_HTTP_TOO_LARGE : NL_HTTP_INCOMPLETE;
|
||||
|
||||
memset(req, 0, sizeof *req);
|
||||
header_len = (size_t)(hdr_end + 4 - buf);
|
||||
|
||||
/* request line: METHOD SP PATH SP HTTP/1.x */
|
||||
line_end = find(buf, header_len, "\r\n");
|
||||
sp1 = memchr(buf, ' ', (size_t)(line_end - buf));
|
||||
if (!sp1)
|
||||
return NL_HTTP_BAD_REQUEST;
|
||||
sp2 = memchr(sp1 + 1, ' ', (size_t)(line_end - sp1 - 1));
|
||||
if (!sp2 || (size_t)(line_end - sp2 - 1) < 8 || memcmp(sp2 + 1, "HTTP/1.", 7) != 0)
|
||||
return NL_HTTP_BAD_REQUEST;
|
||||
|
||||
if (sp1 - buf == 3 && memcmp(buf, "GET", 3) == 0)
|
||||
req->method = NL_HTTP_GET;
|
||||
else if (sp1 - buf == 4 && memcmp(buf, "POST", 4) == 0)
|
||||
req->method = NL_HTTP_POST;
|
||||
else if (sp1 - buf == 3 && memcmp(buf, "PUT", 3) == 0)
|
||||
req->method = NL_HTTP_PUT;
|
||||
else
|
||||
req->method = NL_HTTP_OTHER;
|
||||
|
||||
p = memchr(sp1 + 1, '?', (size_t)(sp2 - sp1 - 1));
|
||||
plen = (size_t)((p ? p : sp2) - (sp1 + 1));
|
||||
if (plen == 0 || plen >= sizeof req->path || sp1[1] != '/')
|
||||
return NL_HTTP_BAD_REQUEST;
|
||||
memcpy(req->path, sp1 + 1, plen);
|
||||
|
||||
/* headers */
|
||||
for (p = line_end + 2; p < hdr_end; p = line_end + 2) {
|
||||
const char *v;
|
||||
line_end = find(p, (size_t)(hdr_end + 2 - p), "\r\n");
|
||||
if ((v = header_value(p, line_end, "content-length")) != NULL) {
|
||||
if (v == line_end)
|
||||
return NL_HTTP_BAD_REQUEST;
|
||||
content_length = 0;
|
||||
for (; v < line_end; v++) {
|
||||
if (!isdigit((unsigned char)*v))
|
||||
return NL_HTTP_BAD_REQUEST;
|
||||
content_length = content_length * 10u + (size_t)(*v - '0');
|
||||
if (content_length > NL_HTTP_MAX_BODY)
|
||||
return NL_HTTP_TOO_LARGE;
|
||||
}
|
||||
} else if ((v = header_value(p, line_end, "authorization")) != NULL) {
|
||||
size_t n;
|
||||
if (line_end - v > 7 && strncasecmp(v, "Bearer ", 7) == 0)
|
||||
v += 7;
|
||||
n = (size_t)(line_end - v);
|
||||
if (n >= sizeof req->auth)
|
||||
n = sizeof req->auth - 1;
|
||||
memcpy(req->auth, v, n);
|
||||
req->auth[n] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
if (header_len + content_length > NL_HTTP_MAX_REQUEST)
|
||||
return NL_HTTP_TOO_LARGE;
|
||||
if (len < header_len + content_length)
|
||||
return NL_HTTP_INCOMPLETE;
|
||||
req->body = buf + header_len;
|
||||
req->body_len = content_length;
|
||||
return NL_HTTP_COMPLETE;
|
||||
}
|
||||
|
||||
const char *nl_http_reason(int status)
|
||||
{
|
||||
switch (status) {
|
||||
case 200: return "OK";
|
||||
case 202: return "Accepted";
|
||||
case 400: return "Bad Request";
|
||||
case 401: return "Unauthorized";
|
||||
case 404: return "Not Found";
|
||||
case 405: return "Method Not Allowed";
|
||||
case 413: return "Payload Too Large";
|
||||
case 500: return "Internal Server Error";
|
||||
case 501: return "Not Implemented";
|
||||
case 503: return "Service Unavailable";
|
||||
default: return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
size_t nl_http_response(char *out, size_t cap, int status, const char *json_body)
|
||||
{
|
||||
int n = snprintf(out, cap,
|
||||
"HTTP/1.1 %d %s\r\nContent-Type: application/json\r\nContent-Length: %u\r\n"
|
||||
"Connection: close\r\n\r\n%s",
|
||||
status, nl_http_reason(status), (unsigned)strlen(json_body), json_body);
|
||||
return (n < 0 || (size_t)n >= cap) ? 0 : (size_t)n;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
#ifndef NL_HTTP_H
|
||||
#define NL_HTTP_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "config/nl_settings.h"
|
||||
|
||||
#define NL_HTTP_MAX_REQUEST 2048 /* request line + headers + body */
|
||||
#define NL_HTTP_MAX_BODY 1024
|
||||
|
||||
typedef enum { NL_HTTP_GET = 0, NL_HTTP_POST, NL_HTTP_PUT, NL_HTTP_OTHER } nl_http_method_t;
|
||||
typedef enum { NL_HTTP_INCOMPLETE = 0, NL_HTTP_COMPLETE, NL_HTTP_BAD_REQUEST, NL_HTTP_TOO_LARGE } nl_http_parse_t;
|
||||
|
||||
typedef struct {
|
||||
nl_http_method_t method;
|
||||
char path[64]; /* without query string */
|
||||
char auth[NL_TOKEN_LEN]; /* Authorization value without "Bearer ", "" if absent */
|
||||
const char *body; /* points into the caller's buffer */
|
||||
size_t body_len;
|
||||
} nl_http_request_t;
|
||||
|
||||
/* Parses the first `len` bytes of `buf` (need not be NUL terminated). */
|
||||
nl_http_parse_t nl_http_parse(const char *buf, size_t len, nl_http_request_t *req);
|
||||
|
||||
/* Builds a complete response; returns its length or 0 when it does not fit into `cap`. */
|
||||
size_t nl_http_response(char *out, size_t cap, int status, const char *json_body);
|
||||
|
||||
const char *nl_http_reason(int status);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,20 @@
|
||||
#include "net/nl_names.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
void nl_names_mac_suffix(const uint8_t mac[6], char out[5])
|
||||
{
|
||||
snprintf(out, 5, "%02x%02x", mac[4], mac[5]);
|
||||
}
|
||||
|
||||
void nl_names_hostname(const uint8_t mac[6], char *out, size_t len)
|
||||
{
|
||||
char suffix[5];
|
||||
nl_names_mac_suffix(mac, suffix);
|
||||
snprintf(out, len, "ninlight-%s", suffix);
|
||||
}
|
||||
|
||||
void nl_names_mac_str(const uint8_t mac[6], char out[18])
|
||||
{
|
||||
snprintf(out, 18, "%02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#ifndef NL_NAMES_H
|
||||
#define NL_NAMES_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* Last two MAC bytes as lowercase hex, e.g. "681f". */
|
||||
void nl_names_mac_suffix(const uint8_t mac[6], char out[5]);
|
||||
/* "ninlight-<suffix>", used as mDNS hostname and onboarding AP SSID. */
|
||||
void nl_names_hostname(const uint8_t mac[6], char *out, size_t len);
|
||||
/* "28:6d:cd:5c:68:1f" */
|
||||
void nl_names_mac_str(const uint8_t mac[6], char out[18]);
|
||||
|
||||
#endif
|
||||
@@ -11,6 +11,11 @@ FetchContent_Declare(unity
|
||||
FetchContent_MakeAvailable(unity)
|
||||
|
||||
enable_testing()
|
||||
|
||||
# cJSON is third-party code: build it without -Werror.
|
||||
add_library(nl_cjson STATIC ${CMAKE_CURRENT_SOURCE_DIR}/../third_party/cJSON/cJSON.c)
|
||||
target_include_directories(nl_cjson PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../third_party/cJSON)
|
||||
|
||||
set(NL_SRC ${CMAKE_CURRENT_SOURCE_DIR}/../src)
|
||||
|
||||
function(nl_test name)
|
||||
@@ -35,3 +40,5 @@ nl_test(test_state_log test_state_log.c fake_flash.c ${NL_SRC}/config/nl_state_l
|
||||
${NL_SRC}/light/nl_boot_state.c ${NL_SRC}/util/nl_crc.c)
|
||||
nl_test(test_reset_counter test_reset_counter.c fake_flash.c ${NL_SRC}/config/nl_reset_counter.c)
|
||||
nl_test(test_console_cmd test_console_cmd.c ${NL_SRC}/sys/nl_console_cmd.c)
|
||||
nl_test(test_names test_names.c ${NL_SRC}/net/nl_names.c)
|
||||
nl_test(test_http test_http.c ${NL_SRC}/net/nl_http.c)
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
#include <string.h>
|
||||
#include "unity.h"
|
||||
#include "net/nl_http.h"
|
||||
|
||||
void setUp(void) {}
|
||||
void tearDown(void) {}
|
||||
|
||||
static nl_http_parse_t parse(const char *s, nl_http_request_t *r)
|
||||
{
|
||||
return nl_http_parse(s, strlen(s), r);
|
||||
}
|
||||
|
||||
static void test_simple_get(void)
|
||||
{
|
||||
nl_http_request_t r;
|
||||
TEST_ASSERT_EQUAL(NL_HTTP_COMPLETE, parse("GET /api/v1/device/state HTTP/1.1\r\nHost: x\r\n\r\n", &r));
|
||||
TEST_ASSERT_EQUAL(NL_HTTP_GET, r.method);
|
||||
TEST_ASSERT_EQUAL_STRING("/api/v1/device/state", r.path);
|
||||
TEST_ASSERT_EQUAL_STRING("", r.auth);
|
||||
TEST_ASSERT_EQUAL_UINT(0, r.body_len);
|
||||
}
|
||||
|
||||
static void test_post_body_arrives_in_parts(void)
|
||||
{
|
||||
const char *full = "POST /api/v1/device/state HTTP/1.1\r\nContent-Length: 11\r\n\r\n{\"on\":true}";
|
||||
nl_http_request_t r;
|
||||
TEST_ASSERT_EQUAL(NL_HTTP_INCOMPLETE, nl_http_parse(full, 20, &r));
|
||||
TEST_ASSERT_EQUAL(NL_HTTP_INCOMPLETE, nl_http_parse(full, strlen(full) - 1, &r));
|
||||
TEST_ASSERT_EQUAL(NL_HTTP_COMPLETE, nl_http_parse(full, strlen(full), &r));
|
||||
TEST_ASSERT_EQUAL(NL_HTTP_POST, r.method);
|
||||
TEST_ASSERT_EQUAL_UINT(11, r.body_len);
|
||||
TEST_ASSERT_EQUAL_MEMORY("{\"on\":true}", r.body, 11);
|
||||
}
|
||||
|
||||
static void test_headers_case_insensitive_and_bearer(void)
|
||||
{
|
||||
nl_http_request_t r;
|
||||
TEST_ASSERT_EQUAL(NL_HTTP_COMPLETE,
|
||||
parse("PUT /api/v1/system/command HTTP/1.1\r\nauthorization: Bearer abc123\r\n"
|
||||
"CONTENT-LENGTH: 2\r\n\r\n{}", &r));
|
||||
TEST_ASSERT_EQUAL(NL_HTTP_PUT, r.method);
|
||||
TEST_ASSERT_EQUAL_STRING("abc123", r.auth);
|
||||
TEST_ASSERT_EQUAL_UINT(2, r.body_len);
|
||||
}
|
||||
|
||||
static void test_plain_token_and_query_string(void)
|
||||
{
|
||||
nl_http_request_t r;
|
||||
TEST_ASSERT_EQUAL(NL_HTTP_COMPLETE,
|
||||
parse("GET /api/v1/system/info?x=1 HTTP/1.0\r\nAuthorization: aabbccddeeff0011223344\r\n\r\n", &r));
|
||||
TEST_ASSERT_EQUAL_STRING("/api/v1/system/info", r.path);
|
||||
TEST_ASSERT_EQUAL_STRING("aabbccddeeff0011223344", r.auth);
|
||||
}
|
||||
|
||||
static void test_other_method(void)
|
||||
{
|
||||
nl_http_request_t r;
|
||||
TEST_ASSERT_EQUAL(NL_HTTP_COMPLETE, parse("DELETE /api/v1/device/state HTTP/1.1\r\n\r\n", &r));
|
||||
TEST_ASSERT_EQUAL(NL_HTTP_OTHER, r.method);
|
||||
}
|
||||
|
||||
static void test_body_too_large(void)
|
||||
{
|
||||
nl_http_request_t r;
|
||||
TEST_ASSERT_EQUAL(NL_HTTP_TOO_LARGE, parse("POST / HTTP/1.1\r\nContent-Length: 1025\r\n\r\n", &r));
|
||||
}
|
||||
|
||||
static void test_headers_never_end(void)
|
||||
{
|
||||
static char buf[NL_HTTP_MAX_REQUEST];
|
||||
nl_http_request_t r;
|
||||
memset(buf, 'a', sizeof buf);
|
||||
memcpy(buf, "GET / HTTP/1.1\r\n", 16);
|
||||
TEST_ASSERT_EQUAL(NL_HTTP_TOO_LARGE, nl_http_parse(buf, sizeof buf, &r));
|
||||
TEST_ASSERT_EQUAL(NL_HTTP_INCOMPLETE, nl_http_parse(buf, 100, &r));
|
||||
}
|
||||
|
||||
static void test_bad_requests(void)
|
||||
{
|
||||
nl_http_request_t r;
|
||||
char longpath[200];
|
||||
TEST_ASSERT_EQUAL(NL_HTTP_BAD_REQUEST, parse("GET /x\r\n\r\n", &r));
|
||||
TEST_ASSERT_EQUAL(NL_HTTP_BAD_REQUEST, parse("GET /x FTP/1.1\r\n\r\n", &r));
|
||||
TEST_ASSERT_EQUAL(NL_HTTP_BAD_REQUEST, parse("\r\n\r\n", &r));
|
||||
TEST_ASSERT_EQUAL(NL_HTTP_BAD_REQUEST, parse("POST / HTTP/1.1\r\nContent-Length: abc\r\n\r\n", &r));
|
||||
strcpy(longpath, "GET /");
|
||||
memset(longpath + 5, 'p', 100);
|
||||
strcpy(longpath + 105, " HTTP/1.1\r\n\r\n");
|
||||
TEST_ASSERT_EQUAL(NL_HTTP_BAD_REQUEST, parse(longpath, &r));
|
||||
}
|
||||
|
||||
static void test_response(void)
|
||||
{
|
||||
char out[256];
|
||||
size_t n = nl_http_response(out, sizeof out, 404, "{\"error\":\"not found\"}");
|
||||
TEST_ASSERT_EQUAL_STRING("HTTP/1.1 404 Not Found\r\nContent-Type: application/json\r\n"
|
||||
"Content-Length: 21\r\nConnection: close\r\n\r\n{\"error\":\"not found\"}", out);
|
||||
TEST_ASSERT_EQUAL_UINT(strlen(out), n);
|
||||
TEST_ASSERT_EQUAL_UINT(0, nl_http_response(out, 20, 200, "{}"));
|
||||
TEST_ASSERT_EQUAL_STRING("Accepted", nl_http_reason(202));
|
||||
TEST_ASSERT_EQUAL_STRING("Payload Too Large", nl_http_reason(413));
|
||||
TEST_ASSERT_EQUAL_STRING("Not Implemented", nl_http_reason(501));
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
UNITY_BEGIN();
|
||||
RUN_TEST(test_simple_get);
|
||||
RUN_TEST(test_post_body_arrives_in_parts);
|
||||
RUN_TEST(test_headers_case_insensitive_and_bearer);
|
||||
RUN_TEST(test_plain_token_and_query_string);
|
||||
RUN_TEST(test_other_method);
|
||||
RUN_TEST(test_body_too_large);
|
||||
RUN_TEST(test_headers_never_end);
|
||||
RUN_TEST(test_bad_requests);
|
||||
RUN_TEST(test_response);
|
||||
return UNITY_END();
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
#include "unity.h"
|
||||
#include "net/nl_names.h"
|
||||
|
||||
void setUp(void) {}
|
||||
void tearDown(void) {}
|
||||
|
||||
static const uint8_t MAC[6] = {0x28, 0x6D, 0xCD, 0x5C, 0x68, 0x1F};
|
||||
|
||||
static void test_suffix(void)
|
||||
{
|
||||
char s[5];
|
||||
nl_names_mac_suffix(MAC, s);
|
||||
TEST_ASSERT_EQUAL_STRING("681f", s);
|
||||
}
|
||||
|
||||
static void test_hostname(void)
|
||||
{
|
||||
char h[24];
|
||||
nl_names_hostname(MAC, h, sizeof h);
|
||||
TEST_ASSERT_EQUAL_STRING("ninlight-681f", h);
|
||||
}
|
||||
|
||||
static void test_hostname_truncates_safely(void)
|
||||
{
|
||||
char h[8];
|
||||
nl_names_hostname(MAC, h, sizeof h);
|
||||
TEST_ASSERT_EQUAL_STRING("ninligh", h);
|
||||
}
|
||||
|
||||
static void test_mac_str(void)
|
||||
{
|
||||
char m[18];
|
||||
nl_names_mac_str(MAC, m);
|
||||
TEST_ASSERT_EQUAL_STRING("28:6d:cd:5c:68:1f", m);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
UNITY_BEGIN();
|
||||
RUN_TEST(test_suffix);
|
||||
RUN_TEST(test_hostname);
|
||||
RUN_TEST(test_hostname_truncates_safely);
|
||||
RUN_TEST(test_mac_str);
|
||||
return UNITY_END();
|
||||
}
|
||||
Vendored
+19
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
|
||||
|
||||
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.
|
||||
Vendored
+3143
File diff suppressed because it is too large
Load Diff
Vendored
+300
@@ -0,0 +1,300 @@
|
||||
/*
|
||||
Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
#ifndef cJSON__h
|
||||
#define cJSON__h
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32))
|
||||
#define __WINDOWS__
|
||||
#endif
|
||||
|
||||
#ifdef __WINDOWS__
|
||||
|
||||
/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 3 define options:
|
||||
|
||||
CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols
|
||||
CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default)
|
||||
CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol
|
||||
|
||||
For *nix builds that support visibility attribute, you can define similar behavior by
|
||||
|
||||
setting default visibility to hidden by adding
|
||||
-fvisibility=hidden (for gcc)
|
||||
or
|
||||
-xldscope=hidden (for sun cc)
|
||||
to CFLAGS
|
||||
|
||||
then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does
|
||||
|
||||
*/
|
||||
|
||||
#define CJSON_CDECL __cdecl
|
||||
#define CJSON_STDCALL __stdcall
|
||||
|
||||
/* export symbols by default, this is necessary for copy pasting the C and header file */
|
||||
#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS)
|
||||
#define CJSON_EXPORT_SYMBOLS
|
||||
#endif
|
||||
|
||||
#if defined(CJSON_HIDE_SYMBOLS)
|
||||
#define CJSON_PUBLIC(type) type CJSON_STDCALL
|
||||
#elif defined(CJSON_EXPORT_SYMBOLS)
|
||||
#define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL
|
||||
#elif defined(CJSON_IMPORT_SYMBOLS)
|
||||
#define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL
|
||||
#endif
|
||||
#else /* !__WINDOWS__ */
|
||||
#define CJSON_CDECL
|
||||
#define CJSON_STDCALL
|
||||
|
||||
#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY)
|
||||
#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type
|
||||
#else
|
||||
#define CJSON_PUBLIC(type) type
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* project version */
|
||||
#define CJSON_VERSION_MAJOR 1
|
||||
#define CJSON_VERSION_MINOR 7
|
||||
#define CJSON_VERSION_PATCH 18
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
/* cJSON Types: */
|
||||
#define cJSON_Invalid (0)
|
||||
#define cJSON_False (1 << 0)
|
||||
#define cJSON_True (1 << 1)
|
||||
#define cJSON_NULL (1 << 2)
|
||||
#define cJSON_Number (1 << 3)
|
||||
#define cJSON_String (1 << 4)
|
||||
#define cJSON_Array (1 << 5)
|
||||
#define cJSON_Object (1 << 6)
|
||||
#define cJSON_Raw (1 << 7) /* raw json */
|
||||
|
||||
#define cJSON_IsReference 256
|
||||
#define cJSON_StringIsConst 512
|
||||
|
||||
/* The cJSON structure: */
|
||||
typedef struct cJSON
|
||||
{
|
||||
/* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
|
||||
struct cJSON *next;
|
||||
struct cJSON *prev;
|
||||
/* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
|
||||
struct cJSON *child;
|
||||
|
||||
/* The type of the item, as above. */
|
||||
int type;
|
||||
|
||||
/* The item's string, if type==cJSON_String and type == cJSON_Raw */
|
||||
char *valuestring;
|
||||
/* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */
|
||||
int valueint;
|
||||
/* The item's number, if type==cJSON_Number */
|
||||
double valuedouble;
|
||||
|
||||
/* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
|
||||
char *string;
|
||||
} cJSON;
|
||||
|
||||
typedef struct cJSON_Hooks
|
||||
{
|
||||
/* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */
|
||||
void *(CJSON_CDECL *malloc_fn)(size_t sz);
|
||||
void (CJSON_CDECL *free_fn)(void *ptr);
|
||||
} cJSON_Hooks;
|
||||
|
||||
typedef int cJSON_bool;
|
||||
|
||||
/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them.
|
||||
* This is to prevent stack overflows. */
|
||||
#ifndef CJSON_NESTING_LIMIT
|
||||
#define CJSON_NESTING_LIMIT 1000
|
||||
#endif
|
||||
|
||||
/* returns the version of cJSON as a string */
|
||||
CJSON_PUBLIC(const char*) cJSON_Version(void);
|
||||
|
||||
/* Supply malloc, realloc and free functions to cJSON */
|
||||
CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks);
|
||||
|
||||
/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */
|
||||
/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */
|
||||
CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length);
|
||||
/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
|
||||
/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */
|
||||
CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated);
|
||||
|
||||
/* Render a cJSON entity to text for transfer/storage. */
|
||||
CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item);
|
||||
/* Render a cJSON entity to text for transfer/storage without any formatting. */
|
||||
CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item);
|
||||
/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */
|
||||
CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt);
|
||||
/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */
|
||||
/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format);
|
||||
/* Delete a cJSON entity and all subentities. */
|
||||
CJSON_PUBLIC(void) cJSON_Delete(cJSON *item);
|
||||
|
||||
/* Returns the number of items in an array (or object). */
|
||||
CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array);
|
||||
/* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */
|
||||
CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index);
|
||||
/* Get item "string" from object. Case insensitive. */
|
||||
CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string);
|
||||
/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
|
||||
CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void);
|
||||
|
||||
/* Check item type and return its value */
|
||||
CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item);
|
||||
CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item);
|
||||
|
||||
/* These functions check the type of an item */
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item);
|
||||
|
||||
/* These calls create a cJSON item of the appropriate type. */
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string);
|
||||
/* raw json */
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void);
|
||||
|
||||
/* Create a string where valuestring references a string so
|
||||
* it will not be freed by cJSON_Delete */
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string);
|
||||
/* Create an object/array that only references it's elements so
|
||||
* they will not be freed by cJSON_Delete */
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child);
|
||||
|
||||
/* These utilities create an Array of count items.
|
||||
* The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count);
|
||||
|
||||
/* Append item to the specified array/object. */
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);
|
||||
/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object.
|
||||
* WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before
|
||||
* writing to `item->string` */
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item);
|
||||
/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item);
|
||||
|
||||
/* Remove/Detach items from Arrays/Objects. */
|
||||
CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which);
|
||||
CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string);
|
||||
CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string);
|
||||
CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string);
|
||||
|
||||
/* Update array items. */
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem);
|
||||
|
||||
/* Duplicate a cJSON item */
|
||||
CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse);
|
||||
/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will
|
||||
* need to be released. With recurse!=0, it will duplicate any children connected to the item.
|
||||
* The item->next and ->prev pointers are always zero on return from Duplicate. */
|
||||
/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal.
|
||||
* case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive);
|
||||
|
||||
/* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings.
|
||||
* The input pointer json cannot point to a read-only address area, such as a string constant,
|
||||
* but should point to a readable and writable address area. */
|
||||
CJSON_PUBLIC(void) cJSON_Minify(char *json);
|
||||
|
||||
/* Helper functions for creating and adding items to an object at the same time.
|
||||
* They return the added item or NULL on failure. */
|
||||
CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name);
|
||||
CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name);
|
||||
CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name);
|
||||
CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean);
|
||||
CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number);
|
||||
CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string);
|
||||
CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw);
|
||||
CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name);
|
||||
CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name);
|
||||
|
||||
/* When assigning an integer value, it needs to be propagated to valuedouble too. */
|
||||
#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number))
|
||||
/* helper for the cJSON_SetNumberValue macro */
|
||||
CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number);
|
||||
#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number))
|
||||
/* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */
|
||||
CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring);
|
||||
|
||||
/* If the object is not a boolean type this does nothing and returns cJSON_Invalid else it returns the new type*/
|
||||
#define cJSON_SetBoolValue(object, boolValue) ( \
|
||||
(object != NULL && ((object)->type & (cJSON_False|cJSON_True))) ? \
|
||||
(object)->type=((object)->type &(~(cJSON_False|cJSON_True)))|((boolValue)?cJSON_True:cJSON_False) : \
|
||||
cJSON_Invalid\
|
||||
)
|
||||
|
||||
/* Macro for iterating over an array or object */
|
||||
#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next)
|
||||
|
||||
/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */
|
||||
CJSON_PUBLIC(void *) cJSON_malloc(size_t size);
|
||||
CJSON_PUBLIC(void) cJSON_free(void *object);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user