diff --git a/HISTORY b/HISTORY index af272d3..777640d 100644 --- a/HISTORY +++ b/HISTORY @@ -1,9 +1,10 @@ -Version 1.40 2018-08-20 +Version 1.40 2018-08-22 * add function conn_pool_parse_server_info and conn_pool_load_server_info * support directive: #@add_annotation, for example: #@add_annotation CONFIG_GET /usr/lib/libshmcache.so /etc/libshmcache.conf * add function fc_split_string and fc_match_delim + * add json_parser.[hc] for parse json array and map Version 1.39 2018-07-31 * add #@function REPLACE_VARS diff --git a/src/Makefile.in b/src/Makefile.in index 8a471ec..8edccdd 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -14,7 +14,7 @@ FAST_SHARED_OBJS = hash.lo chain.lo shared_func.lo ini_file_reader.lo \ fast_buffer.lo multi_skiplist.lo flat_skiplist.lo \ system_info.lo fast_blocked_queue.lo id_generator.lo \ char_converter.lo char_convert_loader.lo common_blocked_queue.lo \ - multi_socket_client.lo skiplist_set.lo + multi_socket_client.lo skiplist_set.lo json_parser.lo FAST_STATIC_OBJS = hash.o chain.o shared_func.o ini_file_reader.o \ logger.o sockopt.o base64.o sched_thread.o \ @@ -25,7 +25,7 @@ FAST_STATIC_OBJS = hash.o chain.o shared_func.o ini_file_reader.o \ fast_buffer.o multi_skiplist.o flat_skiplist.o \ system_info.o fast_blocked_queue.o id_generator.o \ char_converter.o char_convert_loader.o common_blocked_queue.o \ - multi_socket_client.o skiplist_set.o + multi_socket_client.o skiplist_set.o json_parser.o HEADER_FILES = common_define.h hash.h chain.h logger.h base64.h \ shared_func.h pthread_func.h ini_file_reader.h _os_define.h \ @@ -37,7 +37,8 @@ HEADER_FILES = common_define.h hash.h chain.h logger.h base64.h \ skiplist_common.h system_info.h fast_blocked_queue.h \ php7_ext_wrapper.h id_generator.h char_converter.h \ char_convert_loader.h common_blocked_queue.h \ - multi_socket_client.h skiplist_set.h fc_list.h + multi_socket_client.h skiplist_set.h fc_list.h \ + json_parser.h ALL_OBJS = $(FAST_STATIC_OBJS) $(FAST_SHARED_OBJS) diff --git a/src/json_parser.c b/src/json_parser.c new file mode 100644 index 0000000..cbf071f --- /dev/null +++ b/src/json_parser.c @@ -0,0 +1,270 @@ +#include +#include +#include "shared_func.h" +#include "json_parser.h" + +#define JSON_SPACE(ch) \ + (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') + +int detect_json_type(const string_t *input) +{ + if (input->len < 2) { + return FC_JSON_TYPE_STRING; + } + + if (input->str[0] == '[' && input->str[input->len - 1] == ']') { + return FC_JSON_TYPE_ARRAY; + } + if (input->str[0] == '{' && input->str[input->len - 1] == '}') { + return FC_JSON_TYPE_MAP; + } + + return FC_JSON_TYPE_STRING; +} + +typedef struct { + const char *p; + const char *end; + string_t element; + char *error_info; + int error_size; +} ParseContext; + +static int next_json_element(ParseContext *context, const char delim) +{ + char *dest; + char quote_ch; + + dest = context->element.str; + quote_ch = *context->p; + if (quote_ch == '\"' || quote_ch == '\'') { + context->p++; + while (context->p < context->end && *context->p != quote_ch) { + if (*context->p == '\\') { + if (++context->p == context->end) { + snprintf(context->error_info, context->error_size, + "expect a character after \\"); + return EINVAL; + } + switch (*context->p) { + case '\\': + *dest++ = '\\'; + break; + case '/': + *dest++ = '/'; + break; + case 't': + *dest++ = '\t'; + break; + case 'r': + *dest++ = '\r'; + break; + case 'n': + *dest++ = '\n'; + break; + case 'b': + *dest++ = '\f'; + break; + case '"': + *dest++ = '\"'; + break; + case '\'': + *dest++ = '\''; + break; + default: + snprintf(context->error_info, context->error_size, + "invalid escaped character: %c(0x%x)", + *context->p, (unsigned char)*context->p); + return EINVAL; + } + context->p++; + } else { + *dest++ = *context->p++; + } + } + + if (context->p == context->end) { + snprintf(context->error_info, context->error_size, + "expect closed character: %c", quote_ch); + return EINVAL; + } + context->p++; //skip quote char + } else { + while (context->p < context->end && *context->p != delim) { + *dest++ = *context->p++; + } + } + + *dest = '\0'; + context->element.len = dest - context->element.str; + return 0; +} + +static int check_alloc_json_array(string_array_t *array, + char *error_info, const int error_size) +{ + int bytes; + if (array->count < array->alloc) { + return 0; + } + + if (array->alloc == 0) { + array->alloc = 32; + } else { + array->alloc *= 2; + } + + bytes = sizeof(string_t) * array->alloc; + array->elements = (string_t *)realloc(array->elements, bytes); + if (array->elements == NULL) { + snprintf(error_info, error_size, "malloc %d bytes fail", bytes); + return ENOMEM; + } + + return 0; +} + +int decode_json_array(const string_t *input, string_array_t *array, + char *error_info, const int error_size) +{ + ParseContext context; + int buff_len; + int result; + + array->elements = NULL; + array->count = array->alloc = 0; + array->buff = NULL; + + if (input->len < 2) { + snprintf(error_info, error_size, "invalid json array, " + "correct format: [e1, e2, ...]"); + return EINVAL; + } + + if (input->str[0] != '[') { + snprintf(error_info, error_size, "json array must start with ["); + return EINVAL; + } + if (input->str[input->len - 1] != ']') { + snprintf(error_info, error_size, "json array must end with ]"); + return EINVAL; + } + + buff_len = input->len - 2; + array->buff = (char *)malloc(buff_len + 1); + if (array->buff == NULL) { + snprintf(error_info, error_size, "malloc %d bytes fail", buff_len + 1); + return ENOMEM; + } + + context.error_info = error_info; + context.error_size = error_size; + context.element.str = array->buff; + context.element.len = 0; + context.p = input->str + 1; + context.end = input->str + input->len - 1; + result = 0; + while (context.p < context.end) { + while (context.p < context.end && JSON_SPACE(*context.p)) { + context.p++; + } + + if (context.p == context.end) { + break; + } + + fprintf(stderr, "start: %s\n", context.p); + + if (*context.p == ',') { + context.p++; + while (context.p < context.end && JSON_SPACE(*context.p)) { + context.p++; + } + if (context.p < context.end) { //ignore last comma + snprintf(error_info, error_size, "unexpect comma"); + result = EINVAL; + } + break; + } + + if ((result=next_json_element(&context, ',')) != 0) { + break; + } + + while (context.p < context.end && JSON_SPACE(*context.p)) { + context.p++; + } + if (context.p < context.end && *context.p == ',') { + context.p++; //skip comma + } + fprintf(stderr, "end: %s\n", context.p); + + if ((result=check_alloc_json_array(array, error_info, error_size)) != 0) { + array->count = 0; + break; + } + + array->elements[array->count++] = context.element; + context.element.str += context.element.len + 1; + } + + if (result != 0) { + free_json_array(array); + } + return result; +} + +void free_json_array(string_array_t *array) +{ + if (array->elements != NULL) { + free(array->elements); + array->elements = NULL; + array->count = 0; + } + + if (array->buff != NULL) { + free(array->buff); + array->buff = NULL; + } +} + +int encode_json_array(string_array_t *array, string_t *output, + char *error_info, const int error_size) +{ + string_t *el; + string_t *end; + char *p; + int size; + + end = array->elements + array->count; + size = 3; + for (el=array->elements; ellen + 3; + } + + output->str = (char *)malloc(size); + if (output->str == NULL) { + snprintf(error_info, error_size, "malloc %d bytes fail", size); + return ENOMEM; + } + + p = output->str; + *p++ = '['; + output->len = 1; + for (el=array->elements; el array->elements) { + *p++ = ','; + output->len++; + } + + *p++ = '"'; + memcpy(p, el->str, el->len); + p += el->len; + *p++ = '"'; + output->len += el->len + 2; + } + + *p++ = ']'; + *p = '\0'; + return 0; +} diff --git a/src/json_parser.h b/src/json_parser.h new file mode 100644 index 0000000..ab4c69d --- /dev/null +++ b/src/json_parser.h @@ -0,0 +1,53 @@ +//json_parser.h + +#ifndef _JSON_PARSER_H +#define _JSON_PARSER_H + +#include +#include +#include +#include +#include "common_define.h" + +#define FC_JSON_TYPE_STRING 1 +#define FC_JSON_TYPE_ARRAY 2 +#define FC_JSON_TYPE_MAP 3 + +typedef struct +{ + string_t *elements; + int count; + + int alloc; //for internal use + char *buff; //for internal use +} string_array_t; + +#ifdef __cplusplus +extern "C" { +#endif + + int detect_json_type(const string_t *input); + + int decode_json_array(const string_t *input, string_array_t *array, + char *error_info, const int error_size); + + int encode_json_array(string_array_t *array, string_t *output, + char *error_info, const int error_size); + + void free_json_array(string_array_t *array); + + static inline void free_json_string(string_t *buffer) + { + if (buffer->str != NULL) { + free(buffer->str); + buffer->str = NULL; + buffer->len = 0; + } + } + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/src/tests/Makefile b/src/tests/Makefile index b901b44..2b616b3 100644 --- a/src/tests/Makefile +++ b/src/tests/Makefile @@ -6,7 +6,8 @@ LIB_PATH = -lfastcommon -lpthread ALL_PRGS = test_allocator test_skiplist test_multi_skiplist test_mblock test_blocked_queue \ test_id_generator test_ini_parser test_char_convert test_char_convert_loader \ - test_logger test_skiplist_set test_crc32 test_thourands_seperator test_sched_thread + test_logger test_skiplist_set test_crc32 test_thourands_seperator test_sched_thread \ + test_json_parser all: $(ALL_PRGS) .c: diff --git a/src/tests/test_json_parser.c b/src/tests/test_json_parser.c new file mode 100644 index 0000000..e335af2 --- /dev/null +++ b/src/tests/test_json_parser.c @@ -0,0 +1,57 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "fastcommon/logger.h" +#include "fastcommon/shared_func.h" +#include "fastcommon/json_parser.h" + +int main(int argc, char *argv[]) +{ + int result; + int json_type; + char error_info[256]; + string_t input; + string_t output; + + if (argc < 2) { + fprintf(stderr, "Usage: %s \n", + argv[0]); + return EINVAL; + } + + log_init(); + + input.str = argv[1]; + input.len = strlen(input.str); + json_type = detect_json_type(&input); + if (json_type == FC_JSON_TYPE_ARRAY) { + string_array_t array; + + if ((result=decode_json_array(&input, &array, error_info, + sizeof(error_info))) != 0) + { + fprintf(stderr, "decode json array fail, %s\n", error_info); + return result; + } + + if ((result=encode_json_array(&array, &output, + error_info, sizeof(error_info))) != 0) + { + fprintf(stderr, "encode json array fail, %s\n", error_info); + return result; + } + + printf("%s\n", output.str); + free_json_string(&output); + free_json_array(&array); + } else if (json_type == FC_JSON_TYPE_MAP) { + } + + return 0; +}