diff --git a/src/json_parse.c b/src/json_parse.c index 67f3a790..e0aaf039 100644 --- a/src/json_parse.c +++ b/src/json_parse.c @@ -14,22 +14,89 @@ #define JSON_LENGTH 16 #define DEFAULT_NUMTOK 16 +static int _json_createFromString(const char *jsonString, INOUT JSON_s *parsedData, ssize_t jsonLen) { + + jsmnerr_t errCount = JSMN_ERROR_NOMEM; + do { + jsmn_parser parser = { 0 }; + + if (!parsedData) { + break; + } + parsedData->jsonString = strdup(jsonString); + parsedData->jsonTokens = NULL; + + unsigned int numTokens = DEFAULT_NUMTOK; + do { + if (!parsedData->jsonTokens) { + parsedData->jsonTokens = CALLOC(numTokens, sizeof(jsmntok_t)); + if (!parsedData->jsonTokens) { + ERRLOG("WHOA3 : %s", strerror(errno)); + break; + } + } else { + //LOG("reallocating json tokens ..."); + numTokens <<= 1; + jsmntok_t *newTokens = REALLOC(parsedData->jsonTokens, numTokens * sizeof(jsmntok_t)); + if (!newTokens) { + ERRLOG("WHOA4 : %s", strerror(errno)); + break; + } + memset(newTokens, '\0', numTokens * sizeof(jsmntok_t)); + parsedData->jsonTokens = newTokens; + } + jsmn_init(&parser); + errCount = jsmn_parse(&parser, parsedData->jsonString, jsonLen, parsedData->jsonTokens, numTokens-(numTokens/2)); + } while (errCount == JSMN_ERROR_NOMEM); + + if (errCount == JSMN_ERROR_NOMEM) { + break; + } + + if (errCount < 0) { + ERRLOG("%s", "OOPS error parsing JSON : "); + switch (errCount) { + case JSMN_ERROR_NOMEM: + assert(0 && "should not happen"); + break; + case JSMN_ERROR_INVAL: + ERRLOG("%s", "Invalid character inside JSON string"); + break; + case JSMN_ERROR_PART: + ERRLOG("%s", "String is not a complete JSON packet, moar bytes expected"); + break; + default: + ERRLOG("UNKNOWN errCount : %d", errCount); + break; + } + break; + } + + parsedData->numTokens = errCount; + + } while (0); + + if (errCount < 0) { + if (parsedData) { + json_destroy(parsedData); + } + } + + return errCount; +} + int json_createFromFile(const char *filePath, INOUT JSON_s *parsedData) { int fd = -1; - jsmnerr_t errCount = JSMN_ERROR_NOMEM; ssize_t jsonIdx = 0; ssize_t jsonLen = 0; char *jsonString = NULL; - jsmntok_t *jsonTokens = NULL; do { if (!parsedData) { break; } - parsedData->jsonString = NULL; - parsedData->jsonTokens = NULL; TEMP_FAILURE_RETRY(fd = open(filePath, O_RDONLY)); if (fd < 0) { @@ -74,81 +141,152 @@ int json_createFromFile(const char *filePath, INOUT JSON_s *parsedData) { jsonLen = jsonIdx; TEMP_FAILURE_RETRY(close(fd)); - fd = 0; + fd = -1; // now parse the string + jsmnerr_t errCount = _json_createFromString(jsonString, parsedData, jsonLen); + FREE(jsonString); + return errCount; - jsmn_parser parser = { 0 }; - - unsigned int numTokens = DEFAULT_NUMTOK; - do { - if (!jsonTokens) { - jsonTokens = CALLOC(numTokens, sizeof(jsmntok_t)); - if (!jsonTokens) { - ERRLOG("WHOA3 : %s", strerror(errno)); - break; - } - } else { - //LOG("reallocating json tokens ..."); - numTokens <<= 1; - jsmntok_t *newTokens = REALLOC(jsonTokens, numTokens * sizeof(jsmntok_t)); - if (!newTokens) { - ERRLOG("WHOA4 : %s", strerror(errno)); - break; - } - memset(newTokens, '\0', numTokens * sizeof(jsmntok_t)); - jsonTokens = newTokens; - } - jsmn_init(&parser); - errCount = jsmn_parse(&parser, jsonString, jsonLen, jsonTokens, numTokens-(numTokens/2)); - } while (errCount == JSMN_ERROR_NOMEM); - - if (errCount == JSMN_ERROR_NOMEM) { - break; - } - - if (errCount < 0) { - ERRLOG("%s", "OOPS error parsing JSON : "); - switch (errCount) { - case JSMN_ERROR_NOMEM: - assert(0 && "should not happen"); - break; - case JSMN_ERROR_INVAL: - ERRLOG("%s", "Invalid character inside JSON string"); - break; - case JSMN_ERROR_PART: - ERRLOG("%s", "String is not a complete JSON packet, moar bytes expected"); - break; - default: - ERRLOG("UNKNOWN errCount : %d", errCount); - break; - } - break; - } - - parsedData->jsonString = jsonString; - parsedData->jsonTokens = jsonTokens; } while (0); if (fd > 0) { TEMP_FAILURE_RETRY(close(fd)); - fd = 0; + fd = -1; } - if (errCount < 0) { - if (jsonString) { - FREE(jsonString); - } - if (jsonTokens) { - FREE(jsonTokens); - } + if (jsonString) { + FREE(jsonString); } - return errCount; + return JSMN_ERROR_NOMEM; +} + +int json_createFromString(const char *jsonString, INOUT JSON_s *parsedData) { + return _json_createFromString(jsonString, parsedData, strlen(jsonString)); +} + +static bool _json_mapGetStringValue(const JSON_s *map, const char *key, INOUT char **val, INOUT int *len) { + bool foundMatch = false; + + do { + if (!val) { + break; + } + if (!len) { + break; + } + + *val = NULL; + *len = -1; + + int tokCount = map->numTokens; + if (tokCount < 0) { + break; + } + + int idx=0; + const int keySize = strlen(key); + + // should begin as map ... + if (map->jsonTokens[idx].type != JSMN_OBJECT) { + ERRLOG("Map JSON : must start with begin map token"); + break; + } + ++idx; + + // Linear parse/search for key ... + for (int skip=0; idxjsonTokens[idx]; + + assert(keyTok.parent == 0); + + if (keyTok.type != JSMN_STRING) { + ERRLOG("Map JSON : expecting a string key at map position %d", idx); + break; + } + + int start = keyTok.start; + int end = keyTok.end; + assert(end >= start && "bug"); + const int size = end - start; + + ++idx; + jsmntok_t valTok = map->jsonTokens[idx]; + if (size == keySize) { + foundMatch = (strncmp(key, &map->jsonString[start], size) == 0); + if (foundMatch) { + start = valTok.start; + end = valTok.end; + assert(end >= start && "bug"); + *len = end - start; + *val = &map->jsonString[start]; + break; + } + } + + skip = valTok.skip; + } + } while (0); + + return foundMatch; +} + +bool json_mapCopyStringValue(const JSON_s *map, const char *key, INOUT char **val) { + int len = 0; + bool foundMatch = _json_mapGetStringValue(map, key, val, &len); + if (foundMatch) { + *val = len>0 ? strndup(*val, len) : strdup(""); + } + return foundMatch; +} + +bool json_mapParseLongValue(const JSON_s *map, const char *key, INOUT long *val, const int base) { + bool foundMatch = false; + + do { + if (!val) { + break; + } + + int len = 0; + char *str = NULL; + foundMatch = _json_mapGetStringValue(map, key, &str, &len); + if (foundMatch) { + char ch = str[len]; + str[len] = '\0'; + *val = strtol(str, NULL, base); + str[len] = ch; + } + } while (0); + + return foundMatch; +} + +bool json_mapParseFloatValue(const JSON_s *map, const char *key, INOUT float *val) { + bool foundMatch = false; + + do { + if (!val) { + break; + } + + int len = 0; + char *str = NULL; + foundMatch = _json_mapGetStringValue(map, key, &str, &len); + if (foundMatch) { + char ch = str[len]; + str[len] = '\0'; + *val = strtof(str, NULL); + str[len] = ch; + } + } while (0); + + return foundMatch; } void json_destroy(JSON_s *parsedData) { - FREE(parsedData->jsonString); + STRDUP_FREE(parsedData->jsonString); FREE(parsedData->jsonTokens); } @@ -156,25 +294,12 @@ void json_destroy(JSON_s *parsedData) { bool do_logging = true; FILE *error_log = NULL; // Runs jsmn against an arbitrary a test_file.json -int main(int argc, char **argv) { +static void _dumpJSON(JSON_s parsedData) { - error_log = stderr; + fprintf(stderr, "TOK COUNT:%d\n", parsedData.numTokens); - if (argc < 2) { - fprintf(stderr, "Please specify a JSON file to parse on CLI, e.g. : %s path/to/some_file.json\n", argv[0]); - exit(1); - } - - JSON_s parsedData = { 0 }; - int tokCount = json_createFromFile(argv[1], &parsedData); - if (tokCount < 0) { - return 1; - } - - fprintf(stderr, "TOK COUNT:%d\n", tokCount); - - for (int i=0; i