commit 4d4ee8bcb4d066e5ffb0ff790b7ee9142c9125b6 Author: cfif Date: Mon May 26 14:41:46 2025 +0300 Init diff --git a/Inc/cvt.h b/Inc/cvt.h new file mode 100644 index 0000000..2aa95c7 --- /dev/null +++ b/Inc/cvt.h @@ -0,0 +1,10 @@ +// +// Created by cfif on 26.10.22. +// + +#ifndef ARTERY_PROJECT_CVT_H +#define ARTERY_PROJECT_CVT_H + +char *fromFloat(char *bufFloat, float *value); + +#endif //ARTERY_PROJECT_CVT_H diff --git a/Inc/mtojson.h b/Inc/mtojson.h new file mode 100644 index 0000000..d98acb8 --- /dev/null +++ b/Inc/mtojson.h @@ -0,0 +1,54 @@ +/* + SPDX-License-Identifier: BSD-2-Clause + This file is Copyright (c) 2020, 2021 by Rene Kita +*/ + +#ifndef RKTA_MTOJSON_H +#define RKTA_MTOJSON_H + +#include +#include + +enum json_to_type { + t_to_primitive, + t_to_array, + t_to_boolean, + t_to_hex, + t_to_hex_u8, + t_to_hex_u16, + t_to_hex_u32, + t_to_hex_u64, + t_to_int, + t_to_int8_t, + t_to_int16_t, + t_to_int32_t, + t_to_int64_t, + t_to_long, + t_to_longlong, + t_to_null, + t_to_object, + t_to_string, + t_to_uint, + t_to_uint8_t, + t_to_uint16_t, + t_to_uint32_t, + t_to_uint64_t, + t_to_ulong, + t_to_ulonglong, + t_to_value, + t_to_float, +}; + +struct to_json { + const char *name; + const void *value; + const uint8_t *lenStr; + const size_t *count; // Number of elements in a C array + size_t len; + enum json_to_type stype; // Type of the struct + enum json_to_type vtype; // Type of '.value' +}; + +/* Returns the length of the generated JSON text or 0 in case of an error. */ +size_t json_generate(char *out, const struct to_json *tjs, size_t len); +#endif diff --git a/Inc/tiny-json.h b/Inc/tiny-json.h new file mode 100644 index 0000000..2b527e7 --- /dev/null +++ b/Inc/tiny-json.h @@ -0,0 +1,176 @@ + +/* + + + + Licensed under the MIT License . + SPDX-License-Identifier: MIT + Copyright (c) 2016-2018 Rafa Garcia . + + 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 _TINY_JSON_H_ +#define _TINY_JSON_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include + +#define json_containerOf( ptr, type, member ) \ + ((type*)( (char*)ptr - offsetof( type, member ) )) + +/** @defgroup tinyJson Tiny JSON parser. + * @{ */ + +/** Enumeration of codes of supported JSON properties types. */ +typedef enum { + JSON_OBJ, JSON_ARRAY, JSON_TEXT, JSON_BOOLEAN, + JSON_INTEGER, JSON_REAL, JSON_NULL +} jsonType_t; + +/** Structure to handle JSON properties. */ +typedef struct json_s { + struct json_s* sibling; + char const* name; + union { + char const* value; + struct { + struct json_s* child; + struct json_s* last_child; + } c; + } u; + jsonType_t type; +} json_t; + +/** Parse a string to get a json. + * @param str String pointer with a JSON object. It will be modified. + * @param mem Array of json properties to allocate. + * @param qty Number of elements of mem. + * @retval Null pointer if any was wrong in the parse process. + * @retval If the parser process was successfully a valid handler of a json. + * This property is always unnamed and its type is JSON_OBJ. */ +json_t const* json_create( char* str, json_t mem[], unsigned int qty ); + +/** Get the name of a json property. + * @param json A valid handler of a json property. + * @retval Pointer to null-terminated if property has name. + * @retval Null pointer if the property is unnamed. */ +static inline char const* json_getName( json_t const* json ) { + return json->name; +} + +/** Get the value of a json property. + * The type of property cannot be JSON_OBJ or JSON_ARRAY. + * @param property A valid handler of a json property. + * @return Pointer to null-terminated string with the value. */ +static inline char const* json_getValue( json_t const* property ) { + return property->u.value; +} + +/** Get the type of a json property. + * @param json A valid handler of a json property. + * @return The code of type.*/ +static inline jsonType_t json_getType( json_t const* json ) { + return json->type; +} + +/** Get the next sibling of a JSON property that is within a JSON object or array. + * @param json A valid handler of a json property. + * @retval The handler of the next sibling if found. + * @retval Null pointer if the json property is the last one. */ +static inline json_t const* json_getSibling( json_t const* json ) { + return json->sibling; +} + +/** Search a property by its name in a JSON object. + * @param obj A valid handler of a json object. Its type must be JSON_OBJ. + * @param property The name of property to get. + * @retval The handler of the json property if found. + * @retval Null pointer if not found. */ +json_t const* json_getProperty( json_t const* obj, char const* property ); + + +/** Search a property by its name in a JSON object and return its value. + * @param obj A valid handler of a json object. Its type must be JSON_OBJ. + * @param property The name of property to get. + * @retval If found a pointer to null-terminated string with the value. + * @retval Null pointer if not found or it is an array or an object. */ +char const* json_getPropertyValue( json_t const* obj, char const* property ); + +/** Get the first property of a JSON object or array. + * @param json A valid handler of a json property. + * Its type must be JSON_OBJ or JSON_ARRAY. + * @retval The handler of the first property if there is. + * @retval Null pointer if the json object has not properties. */ +static inline json_t const* json_getChild( json_t const* json ) { + return json->u.c.child; +} + +/** Get the value of a json boolean property. + * @param property A valid handler of a json object. Its type must be JSON_BOOLEAN. + * @return The value stdbool. */ +static inline bool json_getBoolean( json_t const* property ) { + return *property->u.value == 't'; +} + +/** Get the value of a json integer property. + * @param property A valid handler of a json object. Its type must be JSON_INTEGER. + * @return The value stdint. */ +static inline int64_t json_getInteger( json_t const* property ) { + return strtoll( property->u.value,(char**)NULL, 10); +} + +/** Get the value of a json real property. + * @param property A valid handler of a json object. Its type must be JSON_REAL. + * @return The value. */ +static inline double json_getReal( json_t const* property ) { + return strtod( property->u.value,(char**)NULL ); +} + + + +/** Structure to handle a heap of JSON properties. */ +typedef struct jsonPool_s jsonPool_t; +struct jsonPool_s { + json_t* (*init)( jsonPool_t* pool ); + json_t* (*alloc)( jsonPool_t* pool ); +}; + +/** Parse a string to get a json. + * @param str String pointer with a JSON object. It will be modified. + * @param pool Custom json pool pointer. + * @retval Null pointer if any was wrong in the parse process. + * @retval If the parser process was successfully a valid handler of a json. + * This property is always unnamed and its type is JSON_OBJ. */ +json_t const* json_createWithPool( char* str, jsonPool_t* pool ); + +/** @ } */ + +#ifdef __cplusplus +} +#endif + +#endif /* _TINY_JSON_H_ */ diff --git a/Src/cvt.c b/Src/cvt.c new file mode 100644 index 0000000..d382be3 --- /dev/null +++ b/Src/cvt.c @@ -0,0 +1,317 @@ +// +// Created by cfif on 26.10.22. +// +#include + +static unsigned char * +cvtround (double fract, int *exp, unsigned char *start, unsigned char *end, unsigned char ch, + unsigned char *negp) +{ + double tmp; + + if (fract) { + modf (fract * 10, &tmp); + } else { + tmp = ch - '0'; + } + if (tmp > 4) { + for (;; --end) { + if (*end == '.') { + --end; + } + if (++*end <= '9') { + break; + } + *end = '0'; + if (end == start) { + if (exp) { /* e/E; increment exponent */ + *end = '1'; + ++*exp; + } else { /* f; add extra digit */ + *--end = '1'; + --start; + } + break; + } + } + } else if (*negp) { + /* + * ``"%.3f", (double)-0.0004'' gives you a negative 0. + */ + for (;; --end) { + if (*end == '.') { + --end; + } + if (*end != '0') { + break; + } + if (end == start) { + *negp = 0; + } + } + } + return start; +} + +static unsigned char * +exponent (unsigned char *p, int exp, unsigned char fmtch) +{ + unsigned char expbuf [8], *t; + + *p++ = fmtch; + if (exp < 0) { + exp = -exp; + *p++ = '-'; + } else { + *p++ = '+'; + } + t = expbuf + sizeof(expbuf); + if (exp > 9) { + do { + *--t = exp % 10 + '0'; + } while ((exp /= 10) > 9); + *--t = exp + '0'; + for (; t < expbuf + sizeof(expbuf); *p++ = *t++) + continue; + } else { + *p++ = '0'; + *p++ = exp + '0'; + } + return p; +} + + +static int +cvt (double number, int prec, int sharpflag, unsigned char *negp, unsigned char fmtch, + unsigned char *startp, unsigned char *endp) +{ + unsigned char *p, *t; + double fract; + int dotrim, expcnt, gformat; + double integer, tmp; + + expcnt = 0; + dotrim = expcnt = gformat = 0; + fract = modf (number, &integer); + + /* + * get an extra slot for rounding + */ + t = ++startp; + + /* + * get integer portion of number; put into the end of the buffer; the + * .01 is added for modf (356.0 / 10, &integer) returning .59999999... + */ + for (p = endp - 1; integer; ++expcnt) { + tmp = modf (integer / 10, &integer); + *p-- = (int) ((tmp + .01) * 10) + '0'; + } + switch (fmtch) { + case 'f': + /* reverse integer into beginning of buffer */ + if (expcnt) { + for (; ++p < endp; *t++ = *p); + } else { + *t++ = '0'; + } + + /* + * if precision required or alternate flag set, add in a + * decimal point. + */ + if (prec || sharpflag) { + *t++ = '.'; + } + + /* + * if requires more precision and some fraction left + */ + if (fract) { + if (prec) { + do { + fract = modf (fract * 10, &tmp); + *t++ = (int)tmp + '0'; + } while (--prec && fract); + } + if (fract) { + startp = cvtround (fract, 0, startp, + t - 1, '0', negp); + } + } + for (; prec--; *t++ = '0'); + break; + case 'e': + case 'E': + eformat: if (expcnt) { + *t++ = *++p; + if (prec || sharpflag) { + *t++ = '.'; + } + + /* + * if requires more precision and some integer left + */ + for (; prec && ++p < endp; --prec) { + *t++ = *p; + } + + /* + * if done precision and more of the integer component, + * round using it; adjust fract so we don't re-round + * later. + */ + if (! prec && ++p < endp) { + fract = 0; + startp = cvtround (0, &expcnt, startp, + t - 1, *p, negp); + } + /* + * adjust expcnt for digit in front of decimal + */ + --expcnt; + } + /* + * until first fractional digit, decrement exponent + */ + else if (fract) { + /* + * adjust expcnt for digit in front of decimal + */ + for (expcnt = -1;; --expcnt) { + fract = modf (fract * 10, &tmp); + if (tmp) { + break; + } + } + *t++ = (int)tmp + '0'; + if (prec || sharpflag) { + *t++ = '.'; + } + } else { + *t++ = '0'; + if (prec || sharpflag) { + *t++ = '.'; + } + } + /* + * if requires more precision and some fraction left + */ + if (fract) { + if (prec) { + do { + fract = modf (fract * 10, &tmp); + *t++ = (int)tmp + '0'; + } while (--prec && fract); + } + if (fract) { + startp = cvtround (fract, &expcnt, startp, + t - 1, '0', negp); + } + } + /* + * if requires more precision + */ + for (; prec--; *t++ = '0'); + + /* + * unless alternate flag, trim any g/G format trailing 0's + */ + if (gformat && ! sharpflag) { + while (t > startp && *--t == '0'); + if (*t == '.') { + --t; + } + ++t; + } + t = exponent (t, expcnt, fmtch); + break; + case 'g': + case 'G': + /* + * a precision of 0 is treated as a precision of 1 + */ + if (!prec) { + ++prec; + } + + /* + * ``The style used depends on the value converted; style e + * will be used only if the exponent resulting from the + * conversion is less than -4 or greater than the precision.'' + * -- ANSI X3J11 + */ + if (expcnt > prec || (! expcnt && fract && fract < .0001)) { + /* + * g/G format counts "significant digits, not digits of + * precision; for the e/E format, this just causes an + * off-by-one problem, i.e. g/G considers the digit + * before the decimal point significant and e/E doesn't + * count it as precision. + */ + --prec; + fmtch -= 2; /* G->E, g->e */ + gformat = 1; + goto eformat; + } + /* + * reverse integer into beginning of buffer, + * note, decrement precision + */ + if (expcnt) { + for (; ++p < endp; *t++ = *p, --prec); + } else { + *t++ = '0'; + } + /* + * if precision required or alternate flag set, add in a + * decimal point. If no digits yet, add in leading 0. + */ + if (prec || sharpflag) { + dotrim = 1; + *t++ = '.'; + } else { + dotrim = 0; + } + /* + * if requires more precision and some fraction left + */ + while (prec && fract) { + fract = modf (fract * 10, &tmp); + *t++ = (int)tmp + '0'; + prec--; + } + if (fract) { + startp = cvtround (fract, 0, startp, t - 1, '0', negp); + } + /* + * alternate format, adds 0's for precision, else trim 0's + */ + if (sharpflag) { + for (; prec--; *t++ = '0'); + } else if (dotrim) { + while (t > startp && *--t == '0'); + if (*t != '.') { + ++t; + } + } + } + return(t - startp); +} + +char *fromFloat(char *bufFloat, float *value) { + int flag = 0; + + if (*value < 0) { + flag = 1; + *value *= -1; + } + + cvt(*value, 6, 1, bufFloat, 'f',&bufFloat[0], &bufFloat[24]); + if (flag) { + bufFloat[0] = '-'; + } else { + bufFloat[0] = ' '; + } + return bufFloat; +} \ No newline at end of file diff --git a/Src/mtojson.c b/Src/mtojson.c new file mode 100644 index 0000000..df1bcdc --- /dev/null +++ b/Src/mtojson.c @@ -0,0 +1,759 @@ +/* + SPDX-License-Identifier: BSD-2-Clause + This file is Copyright (c) 2020, 2021 by Rene Kita +*/ + +#include "mtojson.h" +#include "cvt.h" + +#include +#include + +static char* gen_array(char *, const void *); +static char* gen_boolean(char *, const void *); +static char* gen_c_array(char *, const void *); +static char* gen_hex(char *, const void *); +static char* gen_hex_u8(char *, const void *); +static char* gen_hex_u16(char *, const void *); +static char* gen_hex_u32(char *, const void *); +static char* gen_hex_u64(char *, const void *); +static char* gen_int(char *, const void *); +static char* gen_int8_t(char *, const void *); +static char* gen_int16_t(char *, const void *); +static char* gen_int32_t(char *, const void *); +static char* gen_int64_t(char *, const void *); +static char* gen_long(char *, const void *); +static char* gen_longlong(char *, const void *); +static char* gen_null(char *, const void *); +static char* gen_object(char *, const void *); +static char* gen_primitive(char *, const void *); +static char* gen_string(char *, const void *); +static char* gen_uint(char *, const void *); +static char* gen_uint8_t(char *, const void *); +static char* gen_uint16_t(char *, const void *); +static char* gen_uint32_t(char *, const void *); +static char* gen_uint64_t(char *, const void *); +static char* gen_ulong(char *, const void *); +static char* gen_ulonglong(char *, const void *); +static char* gen_value(char *, const void *); +static char* gen_float(char *, const void *); + +static char* (* const gen_functions[])(char *, const void *) = { + gen_primitive, + gen_array, + gen_boolean, + gen_hex, + gen_hex_u8, + gen_hex_u16, + gen_hex_u32, + gen_hex_u64, + gen_int, + gen_int8_t, + gen_int16_t, + gen_int32_t, + gen_int64_t, + gen_long, + gen_longlong, + gen_null, + gen_object, + gen_string, + gen_uint, + gen_uint8_t, + gen_uint16_t, + gen_uint32_t, + gen_uint64_t, + gen_ulong, + gen_ulonglong, + gen_value, + gen_float +}; + +static char* strcpy_val(char *out, const char *val, size_t len); +extern char bufFloat[20]; + +static char* +gen_float(char *out, const void *val) +{ + char bufFloat[32]; + memset(bufFloat, 0, sizeof(bufFloat)); + + if (!val) + return gen_null(out, val); + + fromFloat(bufFloat, val); + + return strcpy_val(out, &bufFloat[0], strlen(bufFloat)); +} + +static size_t remaining_length; + +static int +reduce_rem_len(size_t len) +{ + if (remaining_length < len) + return 0; + remaining_length -= len; + return 1; +} + +static char* +strcpy_val(char *out, const char *val, size_t len) +{ + if (!reduce_rem_len(len)) + return NULL; + memcpy(out, val, len); + return out + len; +} + +static char* +gen_null(char *out, const void *val) +{ + (void)val; + return strcpy_val(out, "null", 4); +} + +static char* +gen_boolean(char *out, const void *val) +{ + if (!val) + return gen_null(out, val); + + if (*(_Bool*)val) + return strcpy_val(out, "true", 4); + else + return strcpy_val(out, "false", 5); +} + +static char* +gen_string(char *out, const void *val) +{ + if (!val) + return gen_null(out, val); + + if (!reduce_rem_len(2)) // 2 -> "" + return NULL; + + char chars_to_escape[] = "\"\\"; + const char *begin = (const char*)val; + const char *end = begin + strlen(begin); + + *out++ = '"'; + char *esc; + do { + esc = NULL; + for (int i = 0; i < 2; i++) { + char *tmp; + if ((tmp = strchr(begin, chars_to_escape[i]))) { + if (!esc) + esc = tmp; + else + esc = esc < tmp ? esc : tmp; + } + } + + size_t len = esc ? (size_t)(esc - begin) : (size_t)(end - begin); + + if (!(out = strcpy_val(out, begin, len))) + return NULL; + + if (esc) { + char s[2]; + s[0] = '\\'; + s[1] = *esc; + if (!(out = strcpy_val(out, s, 2))) + return NULL; + begin = esc + 1; + } + + } while (esc && *begin); + + *out++ = '"'; + return out; +} + +static char* +utoa(char *dst, unsigned n, unsigned base) +{ + char *s = dst; + char *e; + + for (unsigned m = n; m >= base; m /= base) + s++; + e = s + 1; + + size_t len = (size_t)(e - dst); + if (!reduce_rem_len(len)) + return NULL; + + for ( ; s >= dst; s--, n /= base) + *s = "0123456789ABCDEF"[n % base]; + return e; +} + +static char* +ultoa(char *dst, unsigned long n, unsigned base) +{ + char *s = dst; + char *e; + + for (unsigned long m = n; m >= base; m /= base) + s++; + e = s + 1; + + size_t len = (size_t)(e - dst); + if (!reduce_rem_len(len)) + return NULL; + + for ( ; s >= dst; s--, n /= base) + *s = "0123456789ABCDEF"[n % base]; + return e; +} + +static char* +ulltoa(char *dst, unsigned long long n, unsigned base) +{ + char *s = dst; + char *e; + + for (unsigned long long m = n; m >= base; m /= base) + s++; + e = s + 1; + + size_t len = (size_t)(e - dst); + if (!reduce_rem_len(len)) + return NULL; + + for ( ; s >= dst; s--, n /= base) + *s = "0123456789ABCDEF"[n % base]; + return e; +} + +static char* +gen_hex(char *out, const void *val) +{ + if (!val) + return gen_null(out, val); + + if (!reduce_rem_len(2)) // 2 -> "" + return NULL; + + *out++ = '"'; + if (!(out = utoa(out, *(unsigned*)val, 16))) + return NULL; + *out++ = '"'; + + return out; +} + +static char* +gen_hex_u8(char *out, const void *val) +{ + if (!val) + return gen_null(out, val); + + if (!reduce_rem_len(2)) // 2 -> "" + return NULL; + + *out++ = '"'; + if (!(out = utoa(out, *(uint8_t*)val, 16))) + return NULL; + *out++ = '"'; + + return out; +} + +static char* +gen_hex_u16(char *out, const void *val) +{ + if (!val) + return gen_null(out, val); + + if (!reduce_rem_len(2)) // 2 -> "" + return NULL; + + *out++ = '"'; + if (!(out = utoa(out, *(uint16_t*)val, 16))) + return NULL; + *out++ = '"'; + + return out; +} + +static char* +gen_hex_u32(char *out, const void *val) +{ + if (!val) + return gen_null(out, val); + + if (!reduce_rem_len(2)) // 2 -> "" + return NULL; + + *out++ = '"'; + if (!(out = ultoa(out, *(uint32_t*)val, 16))) + return NULL; + *out++ = '"'; + + return out; +} + +static char* +gen_hex_u64(char *out, const void *val) +{ + if (!val) + return gen_null(out, val); + + if (!reduce_rem_len(2)) // 2 -> "" + return NULL; + + *out++ = '"'; + if (!(out = ulltoa(out, *(uint64_t*)val, 16))) + return NULL; + *out++ = '"'; + + return out; +} + +static char* +gen_int(char *out, const void *val) +{ + if (!val) + return gen_null(out, val); + + int n = *(int*)val; + unsigned u = (unsigned)n; + if (n < 0){ + if (!reduce_rem_len(1)) + return NULL; + *out++ = '-'; + u = -(unsigned)n; + } + + if (!(out = utoa(out, u, 10))) + return NULL; + return out; +} + +static char* +gen_int8_t(char *out, const void *val) +{ + if (!val) + return gen_null(out, val); + + int n = *(int8_t*)val; + unsigned u = (unsigned)n; + if (n < 0){ + if (!reduce_rem_len(1)) + return NULL; + *out++ = '-'; + u = -(unsigned)n; + } + + if (!(out = utoa(out, u, 10))) + return NULL; + return out; +} + +static char* +gen_int16_t(char *out, const void *val) +{ + if (!val) + return gen_null(out, val); + + int n = *(int16_t*)val; + unsigned u = (unsigned)n; + if (n < 0){ + if (!reduce_rem_len(1)) + return NULL; + *out++ = '-'; + u = -(unsigned)n; + } + + if (!(out = utoa(out, u, 10))) + return NULL; + return out; +} + +static char* +gen_int32_t(char *out, const void *val) +{ + if (!val) + return gen_null(out, val); + + int n = *(int*)val; + unsigned u = (unsigned)n; + if (n < 0){ + if (!reduce_rem_len(1)) + return NULL; + *out++ = '-'; + u = -(unsigned)n; + } + + if (!(out = utoa(out, u, 10))) + return NULL; + return out; +} + +static char* +gen_int64_t(char *out, const void *val) +{ + if (!val) + return gen_null(out, val); + + int64_t n = *(int64_t*)val; + uint64_t u = (uint64_t)n; + if (n < 0){ + if (!reduce_rem_len(1)) + return NULL; + *out++ = '-'; + u = -(uint64_t)n; + } + + if (!(out = ulltoa(out, u, 10))) + return NULL; + return out; +} + +static char* +gen_uint(char *out, const void *val) +{ + if (!val) + return gen_null(out, val); + + return utoa(out, *(unsigned*)val, 10); +} + +static char* +gen_uint8_t(char *out, const void *val) +{ + if (!val) + return gen_null(out, val); + + return utoa(out, *(uint8_t*)val, 10); +} + +static char* +gen_uint16_t(char *out, const void *val) +{ + if (!val) + return gen_null(out, val); + + return utoa(out, *(uint16_t*)val, 10); +} + +static char* +gen_uint32_t(char *out, const void *val) +{ + if (!val) + return gen_null(out, val); + + return ultoa(out, *(uint32_t*)val, 10); +} + +static char* +gen_uint64_t(char *out, const void *val) +{ + if (!val) + return gen_null(out, val); + + return ulltoa(out, *(uint64_t*)val, 10); +} + +static char* +gen_long(char *out, const void *val) +{ + if (!val) + return gen_null(out, val); + + long n = *(long*)val; + unsigned long u = (unsigned long)n; + if (n < 0){ + if (!reduce_rem_len(1)) + return NULL; + *out++ = '-'; + u = -(unsigned long)n; + } + + if (!(out = ultoa(out, u, 10))) + return NULL; + return out; +} + +static char* +gen_longlong(char *out, const void *val) +{ + if (!val) + return gen_null(out, val); + + long long n = *(long long*)val; + unsigned long long u = (unsigned long long)n; + if (n < 0){ + if (!reduce_rem_len(1)) + return NULL; + *out++ = '-'; + u = -(unsigned long long)n; + } + + if (!(out = ulltoa(out, u, 10))) + return NULL; + return out; +} + + +static char* +gen_ulong(char *out, const void *val) +{ + if (!val) + return gen_null(out, val); + + return ultoa(out, *(unsigned long*)val, 10); +} + +static char* +gen_ulonglong(char *out, const void *val) +{ + if (!val) + return gen_null(out, val); + + return ulltoa(out, *(unsigned long long*)val, 10); +} + +static char* +gen_value(char *out, const void *val) +{ + return strcpy_val(out, (const char*)val, strlen((const char*)val)); +} + +static char* +gen_c_array(char *out, const void *val) +{ + const struct to_json *tjs = (const struct to_json*)val; + if (!reduce_rem_len(2)) // 2 -> [] + return NULL; + + *out++ = '['; + if (*tjs->count == 0){ + *out++ = ']'; + return out; + } + + size_t incr = 0; + char* (*func)(char *, const void *) = gen_functions[tjs->vtype]; + switch (tjs->vtype) { + case t_to_boolean: + incr = sizeof(_Bool); + break; + + case t_to_int: + incr = sizeof(int); + break; + + case t_to_int8_t: + incr = sizeof(int8_t); + break; + + case t_to_int16_t: + incr = sizeof(int16_t); + break; + + case t_to_int32_t: + incr = sizeof(int32_t); + break; + + case t_to_float: + incr = sizeof(float); + break; + + case t_to_int64_t: + incr = sizeof(int64_t); + break; + + case t_to_long: + incr = sizeof(unsigned long); + break; + + case t_to_longlong: + incr = sizeof(unsigned long long); + break; + + case t_to_object: + incr = sizeof(struct to_json); + break; + + case t_to_hex: + case t_to_uint: + incr = sizeof(unsigned); + break; + + case t_to_hex_u8: + case t_to_uint8_t: + incr = sizeof(uint8_t); + break; + + case t_to_hex_u16: + case t_to_uint16_t: + incr = sizeof(uint16_t); + break; + + case t_to_hex_u32: + case t_to_uint32_t: + incr = sizeof(uint32_t); + break; + + case t_to_hex_u64: + case t_to_uint64_t: + incr = sizeof(uint64_t); + break; + + case t_to_ulong: + incr = sizeof(unsigned long); + break; + + case t_to_ulonglong: + incr = sizeof(unsigned long long); + break; + + case t_to_array: + case t_to_null: + case t_to_primitive: + case t_to_string: + case t_to_value: + return NULL; + } + + const char *p = tjs->value; + for (size_t i = 0; i < *tjs->count - 1; i++){ + if (!(out = (*func)(out, p))) + return NULL; + if (!reduce_rem_len(1)) + return NULL; + *out++ = ','; + + p += incr; + } + + if (!(out = (*func)(out, p))) + return NULL; + + *out++ = ']'; + return out; +} + +static char* +gen_array(char *out, const void *val) +{ + if (!val) + return gen_null(out, val); + + const struct to_json *tjs = (const struct to_json*)val; + if (!reduce_rem_len(2)) // 2 -> [] + return NULL; + + *out++ = '['; + while (tjs->value){ + if (tjs->count) + out = gen_c_array(out, tjs); + else + out = gen_functions[tjs->vtype](out, tjs->value); + if (!out) + return NULL; + tjs++; + if (tjs->value){ + if (!reduce_rem_len(1)) + return NULL; + *out++ = ','; + } + } + *out++ = ']'; + return out; +} + +static char* +gen_object(char *out, const void *val) +{ + if (!val) + return gen_null(out, val); + + const struct to_json *tjs = (const struct to_json*)val; + + if (!reduce_rem_len(2)) // 2 -> {} + return NULL; + + *out++ = '{'; + while (tjs->name){ + const char *name = tjs->name; + size_t len = strlen(name) ; + if (!reduce_rem_len(len + 3)) // 3 -> "": + return NULL; + + *out++ = '"'; + memcpy(out, name, len); + out += len; + *out++ = '"'; + *out++ = ':'; + + if (tjs->count) + out = gen_c_array(out, tjs); + else + out = gen_functions[tjs->vtype](out, tjs->value); + + if (!out) + return NULL; + + tjs++; + if (tjs->name){ + if (!reduce_rem_len(1)) + return NULL; + *out++ = ','; + } + } + + *out++ = '}'; + return out; +} + +static char* +gen_primitive(char *out, const void *to_json) +{ + const struct to_json *tjs = (const struct to_json *)to_json; + if (tjs->count) + return gen_c_array(out, tjs); + + return gen_functions[tjs->vtype](out, tjs->value); +} + +size_t +json_generate(char *out, const struct to_json *tjs, size_t len) +{ + const char *start = out; + + remaining_length = len; + if (!reduce_rem_len(1)) // \0 + return 0; + + switch (tjs->stype) { + case t_to_array: + out = gen_array(out, tjs); + break; + case t_to_object: + out = gen_object(out, tjs); + break; + case t_to_primitive: + out = gen_primitive(out, tjs); + break; + /* These are not valid ctypes */ + case t_to_boolean: + case t_to_int: + case t_to_null: + case t_to_string: + case t_to_uint: + case t_to_value: + default: + return 0; + } + + if (!out) + return 0; + + *out = '\0'; + return (size_t)(out - start); +} diff --git a/Src/testJson.c b/Src/testJson.c new file mode 100644 index 0000000..15848f2 --- /dev/null +++ b/Src/testJson.c @@ -0,0 +1,132 @@ +// +// Created by cfif on 27.10.22. +// +#include +#include "tiny-json.h" +#include "mtojson.h" + +typedef struct { + float param1; + char param2[16]; + uint32_t param3; + uint32_t param4; + uint32_t param5; +} com1; + +typedef struct { + uint32_t param1; + uint32_t param2; + uint32_t param3; + uint32_t param4; + uint32_t param5; +} com2; + +com1 dataCom1 = {1.123456, "qwerty", 3, 4, 5}; +com2 dataCom2 = {6, 7,8, 9, 0}; + +const char *listComName[] = {"com1", "com2"}; +const void *listComStruct[] = { &dataCom1, &dataCom2}; +const int countListCom = sizeof(listComStruct) / 4; + +const struct to_json jsonDataArray1[] = { + {.value = &dataCom1.param1, .vtype = t_to_float, .stype = t_to_array}, + {.value = &dataCom1.param2, .vtype = t_to_string,}, + {.value = &dataCom1.param3, .vtype = t_to_uint32_t,}, + {.value = &dataCom1.param4, .vtype = t_to_uint32_t,}, + {.value = &dataCom1.param5, .vtype = t_to_uint32_t,}, + {NULL} +}; + +const struct to_json jsonDataArray2[] = { + {.value = &dataCom2.param1, .vtype = t_to_uint32_t, .stype = t_to_array}, + {.value = &dataCom2.param2, .vtype = t_to_uint32_t,}, + {.value = &dataCom2.param3, .vtype = t_to_uint32_t,}, + {.value = &dataCom2.param4, .vtype = t_to_uint32_t,}, + {.value = &dataCom2.param5, .vtype = t_to_uint32_t,}, + {NULL} + +}; + +const struct to_json jsonCom1 = {.name = "com1", .value = &jsonDataArray1, .vtype = t_to_array, .stype = t_to_object}; +const struct to_json jsonCom2 = {.name = "com2", .value = &jsonDataArray2, .vtype = t_to_array, .stype = t_to_object}; + +enum { MAX_STRING_LEN = 1000 }; +char json_text[MAX_STRING_LEN] = {0}; + +void testJson() { + + const struct to_json jsonDataObject[] = { + jsonCom1, + jsonCom2, + {NULL} + }; + + size_t json_len = json_generate(&json_text[0], jsonDataObject, MAX_STRING_LEN); + + + dataCom1.param1 = 0; + strcpy(dataCom1.param2, "qaz"); + dataCom1.param3 = 0; + dataCom1.param4 = 0; + dataCom1.param5 = 0; + + dataCom2.param1 = 0; + dataCom2.param2 = 0; + dataCom2.param3 = 0; + dataCom2.param4 = 0; + dataCom2.param5 = 0; + + + json_t pool[1000]; + unsigned const qty = sizeof pool / sizeof *pool; + char comName[20]; + + //char str[] = "{\"com1\":[ 11.12345, \"qwertyuiop\", 13, 14, 15 ], \"com2\":[ 16, 17, 18, 19, 20 ]}"; + + json_t const *json = json_create(json_text, pool, qty); + + json_t const *elementRoot = json_getChild(json); + while (elementRoot != 0) { + + strcpy(comName, json_getName(elementRoot)); + + void *data_p = NULL; + + for (int i = 0; i < countListCom; ++i) { + if (strncmp(comName, listComName[i], strlen(listComName[i])) == 0) { + data_p = listComStruct[i]; + break; + } + } + + if (data_p == NULL) + continue; + + json_t const *array = json_getProperty(json, comName); + json_t const *element = json_getChild(array); + while (element != 0) { + + if (json_getType(element) == JSON_REAL) { + ((float *) data_p)[0] = (float) json_getReal(element); + data_p += 4; + } + + if (json_getType(element) == JSON_INTEGER) { + ((uint32_t *) data_p)[0] = json_getInteger(element); + data_p += 4; + } + + if (json_getType(element) == JSON_TEXT) { + strcpy(data_p, json_getValue(element)); + data_p += 16; + } + + + element = json_getSibling(element); + } + + elementRoot = json_getSibling(elementRoot); + } + + dataCom2.param5 = 0; +} \ No newline at end of file diff --git a/Src/tiny-json.c b/Src/tiny-json.c new file mode 100644 index 0000000..795715c --- /dev/null +++ b/Src/tiny-json.c @@ -0,0 +1,461 @@ + +/* + + + + Licensed under the MIT License . + SPDX-License-Identifier: MIT + Copyright (c) 2016-2018 Rafa Garcia . + + 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. + +*/ + +#include +#include +#include "tiny-json.h" + +/** Structure to handle a heap of JSON properties. */ +typedef struct jsonStaticPool_s { + json_t* mem; /**< Pointer to array of json properties. */ + unsigned int qty; /**< Length of the array of json properties. */ + unsigned int nextFree; /**< The index of the next free json property. */ + jsonPool_t pool; +} jsonStaticPool_t; + +/* Search a property by its name in a JSON object. */ +json_t const* json_getProperty( json_t const* obj, char const* property ) { + json_t const* sibling; + for( sibling = obj->u.c.child; sibling; sibling = sibling->sibling ) + if ( sibling->name && !strcmp( sibling->name, property ) ) + return sibling; + return 0; +} + +/* Search a property by its name in a JSON object and return its value. */ +char const* json_getPropertyValue( json_t const* obj, char const* property ) { + json_t const* field = json_getProperty( obj, property ); + if ( !field ) return 0; + jsonType_t type = json_getType( field ); + if ( JSON_ARRAY >= type ) return 0; + return json_getValue( field ); +} + +/* Internal prototypes: */ +static char* goBlank( char* str ); +static char* goNum( char* str ); +static json_t* poolInit( jsonPool_t* pool ); +static json_t* poolAlloc( jsonPool_t* pool ); +static char* objValue( char* ptr, json_t* obj, jsonPool_t* pool ); +static char* setToNull( char* ch ); +static bool isEndOfPrimitive( char ch ); + +/* Parse a string to get a json. */ +json_t const* json_createWithPool( char *str, jsonPool_t *pool ) { + char* ptr = goBlank( str ); + if ( !ptr || (*ptr != '{' && *ptr != '[') ) return 0; + json_t* obj = pool->init( pool ); + obj->name = 0; + obj->sibling = 0; + obj->u.c.child = 0; + ptr = objValue( ptr, obj, pool ); + if ( !ptr ) return 0; + return obj; +} + +/* Parse a string to get a json. */ +json_t const* json_create( char* str, json_t mem[], unsigned int qty ) { + jsonStaticPool_t spool; + spool.mem = mem; + spool.qty = qty; + spool.pool.init = poolInit; + spool.pool.alloc = poolAlloc; + return json_createWithPool( str, &spool.pool ); +} + +/** Get a special character with its escape character. Examples: + * 'b' -> '\\b', 'n' -> '\\n', 't' -> '\\t' + * @param ch The escape character. + * @retval The character code. */ +static char getEscape( char ch ) { + static struct { char ch; char code; } const pair[] = { + { '\"', '\"' }, { '\\', '\\' }, + { '/', '/' }, { 'b', '\b' }, + { 'f', '\f' }, { 'n', '\n' }, + { 'r', '\r' }, { 't', '\t' }, + }; + unsigned int i; + for( i = 0; i < sizeof pair / sizeof *pair; ++i ) + if ( pair[i].ch == ch ) + return pair[i].code; + return '\0'; +} + +/** Parse 4 characters. + * @param str Pointer to first digit. + * @retval '?' If the four characters are hexadecimal digits. + * @retval '\0' In other cases. */ +static unsigned char getCharFromUnicode( unsigned char const* str ) { + unsigned int i; + for( i = 0; i < 4; ++i ) + if ( !isxdigit( str[i] ) ) + return '\0'; + return '?'; +} + +/** Parse a string and replace the scape characters by their meaning characters. + * This parser stops when finds the character '\"'. Then replaces '\"' by '\0'. + * @param str Pointer to first character. + * @retval Pointer to first non white space after the string. If success. + * @retval Null pointer if any error occur. */ +static char* parseString( char* str ) { + unsigned char* head = (unsigned char*)str; + unsigned char* tail = (unsigned char*)str; + for( ; *head; ++head, ++tail ) { + if ( *head == '\"' ) { + *tail = '\0'; + return (char*)++head; + } + if ( *head == '\\' ) { + if ( *++head == 'u' ) { + char const ch = getCharFromUnicode( ++head ); + if ( ch == '\0' ) return 0; + *tail = ch; + head += 3; + } + else { + char const esc = getEscape( *head ); + if ( esc == '\0' ) return 0; + *tail = esc; + } + } + else *tail = *head; + } + return 0; +} + +/** Parse a string to get the name of a property. + * @param ptr Pointer to first character. + * @param property The property to assign the name. + * @retval Pointer to first of property value. If success. + * @retval Null pointer if any error occur. */ +static char* propertyName( char* ptr, json_t* property ) { + property->name = ++ptr; + ptr = parseString( ptr ); + if ( !ptr ) return 0; + ptr = goBlank( ptr ); + if ( !ptr ) return 0; + if ( *ptr++ != ':' ) return 0; + return goBlank( ptr ); +} + +/** Parse a string to get the value of a property when its type is JSON_TEXT. + * @param ptr Pointer to first character ('\"'). + * @param property The property to assign the name. + * @retval Pointer to first non white space after the string. If success. + * @retval Null pointer if any error occur. */ +static char* textValue( char* ptr, json_t* property ) { + ++property->u.value; + ptr = parseString( ++ptr ); + if ( !ptr ) return 0; + property->type = JSON_TEXT; + return ptr; +} + +/** Compare two strings until get the null character in the second one. + * @param ptr sub string + * @param str main string + * @retval Pointer to next character. + * @retval Null pointer if any error occur. */ +static char* checkStr( char* ptr, char const* str ) { + while( *str ) + if ( *ptr++ != *str++ ) + return 0; + return ptr; +} + +/** Parser a string to get a primitive value. + * If the first character after the value is different of '}' or ']' is set to '\0'. + * @param ptr Pointer to first character. + * @param property Property handler to set the value and the type, (true, false or null). + * @param value String with the primitive literal. + * @param type The code of the type. ( JSON_BOOLEAN or JSON_NULL ) + * @retval Pointer to first non white space after the string. If success. + * @retval Null pointer if any error occur. */ +static char* primitiveValue( char* ptr, json_t* property, char const* value, jsonType_t type ) { + ptr = checkStr( ptr, value ); + if ( !ptr || !isEndOfPrimitive( *ptr ) ) return 0; + ptr = setToNull( ptr ); + property->type = type; + return ptr; +} + +/** Parser a string to get a true value. + * If the first character after the value is different of '}' or ']' is set to '\0'. + * @param ptr Pointer to first character. + * @param property Property handler to set the value and the type, (true, false or null). + * @retval Pointer to first non white space after the string. If success. + * @retval Null pointer if any error occur. */ +static char* trueValue( char* ptr, json_t* property ) { + return primitiveValue( ptr, property, "true", JSON_BOOLEAN ); +} + +/** Parser a string to get a false value. + * If the first character after the value is different of '}' or ']' is set to '\0'. + * @param ptr Pointer to first character. + * @param property Property handler to set the value and the type, (true, false or null). + * @retval Pointer to first non white space after the string. If success. + * @retval Null pointer if any error occur. */ +static char* falseValue( char* ptr, json_t* property ) { + return primitiveValue( ptr, property, "false", JSON_BOOLEAN ); +} + +/** Parser a string to get a null value. + * If the first character after the value is different of '}' or ']' is set to '\0'. + * @param ptr Pointer to first character. + * @param property Property handler to set the value and the type, (true, false or null). + * @retval Pointer to first non white space after the string. If success. + * @retval Null pointer if any error occur. */ +static char* nullValue( char* ptr, json_t* property ) { + return primitiveValue( ptr, property, "null", JSON_NULL ); +} + +/** Analyze the exponential part of a real number. + * @param ptr Pointer to first character. + * @retval Pointer to first non numerical after the string. If success. + * @retval Null pointer if any error occur. */ +static char* expValue( char* ptr ) { + if ( *ptr == '-' || *ptr == '+' ) ++ptr; + if ( !isdigit( (int)(*ptr) ) ) return 0; + ptr = goNum( ++ptr ); + return ptr; +} + +/** Analyze the decimal part of a real number. + * @param ptr Pointer to first character. + * @retval Pointer to first non numerical after the string. If success. + * @retval Null pointer if any error occur. */ +static char* fraqValue( char* ptr ) { + if ( !isdigit( (int)(*ptr) ) ) return 0; + ptr = goNum( ++ptr ); + if ( !ptr ) return 0; + return ptr; +} + +/** Parser a string to get a numerical value. + * If the first character after the value is different of '}' or ']' is set to '\0'. + * @param ptr Pointer to first character. + * @param property Property handler to set the value and the type: JSON_REAL or JSON_INTEGER. + * @retval Pointer to first non white space after the string. If success. + * @retval Null pointer if any error occur. */ +static char* numValue( char* ptr, json_t* property ) { + if ( *ptr == '-' ) ++ptr; + if ( !isdigit( (int)(*ptr) ) ) return 0; + if ( *ptr != '0' ) { + ptr = goNum( ptr ); + if ( !ptr ) return 0; + } + else if ( isdigit( (int)(*++ptr) ) ) return 0; + property->type = JSON_INTEGER; + if ( *ptr == '.' ) { + ptr = fraqValue( ++ptr ); + if ( !ptr ) return 0; + property->type = JSON_REAL; + } + if ( *ptr == 'e' || *ptr == 'E' ) { + ptr = expValue( ++ptr ); + if ( !ptr ) return 0; + property->type = JSON_REAL; + } + if ( !isEndOfPrimitive( *ptr ) ) return 0; + if ( JSON_INTEGER == property->type ) { + char const* value = property->u.value; + bool const negative = *value == '-'; + static char const min[] = "-9223372036854775808"; + static char const max[] = "9223372036854775807"; + unsigned int const maxdigits = ( negative? sizeof min: sizeof max ) - 1; + unsigned int const len = ( unsigned int const ) ( ptr - value ); + if ( len > maxdigits ) return 0; + if ( len == maxdigits ) { + char const tmp = *ptr; + *ptr = '\0'; + char const* const threshold = negative ? min: max; + if ( 0 > strcmp( threshold, value ) ) return 0; + *ptr = tmp; + } + } + ptr = setToNull( ptr ); + return ptr; +} + +/** Add a property to a JSON object or array. + * @param obj The handler of the JSON object or array. + * @param property The handler of the property to be added. */ +static void add( json_t* obj, json_t* property ) { + property->sibling = 0; + if ( !obj->u.c.child ){ + obj->u.c.child = property; + obj->u.c.last_child = property; + } else { + obj->u.c.last_child->sibling = property; + obj->u.c.last_child = property; + } +} + +/** Parser a string to get a json object value. + * @param ptr Pointer to first character. + * @param obj The handler of the JSON root object or array. + * @param pool The handler of a json pool for creating json instances. + * @retval Pointer to first character after the value. If success. + * @retval Null pointer if any error occur. */ +static char* objValue( char* ptr, json_t* obj, jsonPool_t* pool ) { + obj->type = *ptr == '{' ? JSON_OBJ : JSON_ARRAY; + obj->u.c.child = 0; + obj->sibling = 0; + ptr++; + for(;;) { + ptr = goBlank( ptr ); + if ( !ptr ) return 0; + if ( *ptr == ',' ) { + ++ptr; + continue; + } + char const endchar = ( obj->type == JSON_OBJ )? '}': ']'; + if ( *ptr == endchar ) { + *ptr = '\0'; + json_t* parentObj = obj->sibling; + if ( !parentObj ) return ++ptr; + obj->sibling = 0; + obj = parentObj; + ++ptr; + continue; + } + json_t* property = pool->alloc( pool ); + if ( !property ) return 0; + if( obj->type != JSON_ARRAY ) { + if ( *ptr != '\"' ) return 0; + ptr = propertyName( ptr, property ); + if ( !ptr ) return 0; + } + else property->name = 0; + add( obj, property ); + property->u.value = ptr; + switch( *ptr ) { + case '{': + property->type = JSON_OBJ; + property->u.c.child = 0; + property->sibling = obj; + obj = property; + ++ptr; + break; + case '[': + property->type = JSON_ARRAY; + property->u.c.child = 0; + property->sibling = obj; + obj = property; + ++ptr; + break; + case '\"': ptr = textValue( ptr, property ); break; + case 't': ptr = trueValue( ptr, property ); break; + case 'f': ptr = falseValue( ptr, property ); break; + case 'n': ptr = nullValue( ptr, property ); break; + default: ptr = numValue( ptr, property ); break; + } + if ( !ptr ) return 0; + } +} + +/** Initialize a json pool. + * @param pool The handler of the pool. + * @return a instance of a json. */ +static json_t* poolInit( jsonPool_t* pool ) { + jsonStaticPool_t *spool = json_containerOf( pool, jsonStaticPool_t, pool ); + spool->nextFree = 1; + return spool->mem; +} + +/** Create an instance of a json from a pool. + * @param pool The handler of the pool. + * @retval The handler of the new instance if success. + * @retval Null pointer if the pool was empty. */ +static json_t* poolAlloc( jsonPool_t* pool ) { + jsonStaticPool_t *spool = json_containerOf( pool, jsonStaticPool_t, pool ); + if ( spool->nextFree >= spool->qty ) return 0; + return spool->mem + spool->nextFree++; +} + +/** Checks whether an character belongs to set. + * @param ch Character value to be checked. + * @param set Set of characters. It is just a null-terminated string. + * @return true or false there is membership or not. */ +static bool isOneOfThem( char ch, char const* set ) { + while( *set != '\0' ) + if ( ch == *set++ ) + return true; + return false; +} + +/** Increases a pointer while it points to a character that belongs to a set. + * @param str The initial pointer value. + * @param set Set of characters. It is just a null-terminated string. + * @return The final pointer value or null pointer if the null character was found. */ +static char* goWhile( char* str, char const* set ) { + for(; *str != '\0'; ++str ) { + if ( !isOneOfThem( *str, set ) ) + return str; + } + return 0; +} + +/** Set of characters that defines a blank. */ +static char const* const blank = " \n\r\t\f"; + +/** Increases a pointer while it points to a white space character. + * @param str The initial pointer value. + * @return The final pointer value or null pointer if the null character was found. */ +static char* goBlank( char* str ) { + return goWhile( str, blank ); +} + +/** Increases a pointer while it points to a decimal digit character. + * @param str The initial pointer value. + * @return The final pointer value or null pointer if the null character was found. */ +static char* goNum( char* str ) { + for( ; *str != '\0'; ++str ) { + if ( !isdigit( (int)(*str) ) ) + return str; + } + return 0; +} + +/** Set of characters that defines the end of an array or a JSON object. */ +static char const* const endofblock = "}]"; + +/** Set a char to '\0' and increase its pointer if the char is different to '}' or ']'. + * @param ch Pointer to character. + * @return Final value pointer. */ +static char* setToNull( char* ch ) { + if ( !isOneOfThem( *ch, endofblock ) ) *ch++ = '\0'; + return ch; +} + +/** Indicate if a character is the end of a primitive value. */ +static bool isEndOfPrimitive( char ch ) { + return ch == ',' || isOneOfThem( ch, blank ) || isOneOfThem( ch, endofblock ); +} diff --git a/modular.json b/modular.json new file mode 100644 index 0000000..71971cd --- /dev/null +++ b/modular.json @@ -0,0 +1,10 @@ +{ + "cmake": { + "inc_dirs": [ + "Inc" + ], + "srcs": [ + "Src/**.c" + ] + } +} \ No newline at end of file