Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3349468e07 | ||
|
|
339c3af4f4 | ||
|
|
25c3c018f6 | ||
|
|
f367993ad4 | ||
|
|
2d94783ee7 | ||
|
|
f9a36a5c37 | ||
|
|
d3c99152fc | ||
|
|
dc0c003785 | ||
|
|
4577836f8e | ||
|
|
50c7a055ec | ||
|
|
6771d00c6e | ||
|
|
71661cd9ad | ||
|
|
46b7b56852 | ||
|
|
fd7d9988b8 | ||
|
|
280e24a8e7 |
@@ -4,7 +4,7 @@ include(GNUInstallDirs)
|
||||
cmake_minimum_required(VERSION 3.0.2)
|
||||
project(rjp)
|
||||
set(rjp_VERSION_MAJOR 0)
|
||||
set(rjp_VERSION_MINOR 5)
|
||||
set(rjp_VERSION_MINOR 7)
|
||||
set(rjp_VERSION_REVISION 0)
|
||||
set(INCLUDE_PATH ${CMAKE_SOURCE_DIR}/include)
|
||||
configure_file(
|
||||
@@ -19,12 +19,12 @@ option(ENABLE_SHARED "Build shared library" OFF)
|
||||
set(SOURCE_LIST "src/input.c" "src/output.c" "src/strings.c" "src/memory.c" "src/rjp.c")
|
||||
if(ENABLE_SHARED)
|
||||
add_library(rjp SHARED ${SOURCE_LIST})
|
||||
set_target_properties(rjp PROPERTIES SOVERSION "${rjp_VERSION_MAJOR}.${rjp_VERSION_MINOR}.${rjp_VERSION_REVISION}")
|
||||
else()
|
||||
add_library(rjp STATIC ${SOURCE_LIST})
|
||||
endif()
|
||||
|
||||
set_target_properties(rjp PROPERTIES PUBLIC_HEADER ${INCLUDE_PATH}/rjp.h)
|
||||
set_target_properties(rjp PROPERTIES SOVERSION "${rjp_VERSION_MAJOR}.${rjp_VERSION_MINOR}.${rjp_VERSION_REVISION}")
|
||||
target_compile_options(rjp PRIVATE -Wall -Wextra -pedantic)
|
||||
if(ENABLE_DIAGNOSTICS)
|
||||
target_compile_definitions(rjp PRIVATE RJP_DIAGNOSTICS)
|
||||
|
||||
3
TODO
Normal file
3
TODO
Normal file
@@ -0,0 +1,3 @@
|
||||
Change string handling to work with chunked reading
|
||||
Change numeral handling to work with chunked reading
|
||||
handle scientific notation
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
rjp
|
||||
Copyright (C) 2018 rexy712
|
||||
Copyright (C) 2018-2019 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
rjp
|
||||
Copyright (C) 2018 rexy712
|
||||
Copyright (C) 2018-2019 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -25,6 +25,15 @@
|
||||
extern "C"{
|
||||
#endif
|
||||
|
||||
#ifndef RJP_int
|
||||
#include <stdint.h>
|
||||
#define RJP_int int64_t
|
||||
#endif
|
||||
#ifndef RJP_float
|
||||
#include <stdint.h>
|
||||
#define RJP_float double
|
||||
#endif
|
||||
|
||||
//type of data
|
||||
typedef enum RJP_type{
|
||||
json_object = 0,
|
||||
@@ -64,8 +73,8 @@ typedef struct RJP_array{
|
||||
//hold any json data type
|
||||
typedef struct RJP_value{
|
||||
union{
|
||||
long integer;
|
||||
double dfloat;
|
||||
RJP_int integer;
|
||||
RJP_float dfloat;
|
||||
char boolean;
|
||||
struct RJP_object object;
|
||||
struct RJP_string string;
|
||||
@@ -75,8 +84,16 @@ typedef struct RJP_value{
|
||||
enum RJP_type type; //flag to determine active member of union
|
||||
}RJP_value;
|
||||
|
||||
//Result of an rjp search operation
|
||||
typedef struct RJP_search_res{
|
||||
RJP_value* value; //pointer to match
|
||||
size_t searchindex; //index in the search list
|
||||
size_t objindex; //index in the searched object
|
||||
}RJP_search_res;
|
||||
|
||||
//Convert C string consisting of json data into RJP's format
|
||||
RJP_value* rjp_parse(const char* str);
|
||||
//RJP_value* rjp_parse_chunked(const char* str, RJP_value* prev_chunk);
|
||||
|
||||
//Initialize a root RJP_value to NULL
|
||||
RJP_value* rjp_init_json(void);
|
||||
@@ -104,14 +121,20 @@ RJP_value* rjp_add_member_no_alloc(RJP_value* dest, char* key, size_t keylen, RJ
|
||||
//add an element to a json array
|
||||
RJP_value* rjp_add_element(RJP_value* dest, RJP_value value);
|
||||
|
||||
//set existing object member's key
|
||||
void rjp_set_key(RJP_value* dest, const char* key, size_t keylen);
|
||||
//set existing object member's key without allocation. key must be allocated using rjp_alloc/rjp_calloc
|
||||
void rjp_set_key_no_alloc(RJP_value* dest, char* key, size_t keylen);
|
||||
|
||||
//set existing value
|
||||
void rjp_set_value(RJP_value* dest, RJP_value value);
|
||||
|
||||
//initialize a RJP_value to the requested type and value
|
||||
RJP_value rjp_integer(long i);
|
||||
RJP_value rjp_integer(RJP_int i);
|
||||
RJP_value rjp_boolean(char b);
|
||||
RJP_value rjp_dfloat(double d);
|
||||
RJP_value rjp_dfloat(RJP_float d);
|
||||
RJP_value rjp_string(char* c, size_t len);
|
||||
RJP_value rjp_string_copy(const char* c);
|
||||
RJP_value rjp_null(void);
|
||||
RJP_value rjp_object(void);
|
||||
RJP_value rjp_array(void);
|
||||
@@ -125,15 +148,15 @@ size_t rjp_num_members(const RJP_value* object);
|
||||
//Return the object member's key name
|
||||
char* rjp_member_name(const RJP_value* member);
|
||||
//Return the object member's key name length excluding null terminator
|
||||
size_t rjp_member_name_length(RJP_value* member);
|
||||
size_t rjp_member_name_length(const RJP_value* member);
|
||||
//Search for an object member with given key
|
||||
RJP_value* rjp_search_member(const RJP_value* object, const char* search, size_t* index, size_t skip);
|
||||
RJP_search_res rjp_search_member(const RJP_value* object, const char* search, size_t skip);
|
||||
//Search for first occurance of multiple keys. Returns first occurance of each.
|
||||
//Assumes dest is large enough to hold maximum num items.
|
||||
size_t rjp_search_members(const RJP_value* object, size_t num, const char** searches, RJP_value** dest, size_t skip);
|
||||
size_t rjp_search_members(const RJP_value* object, size_t num, const char* const* searches, RJP_search_res* dest, size_t skip);
|
||||
//Search for multiple occurances of multiple keys. Returns pointer to list of matches.
|
||||
//Returned pointer must be freed using rjp_free
|
||||
RJP_value** rjp_search_members_multi(const RJP_value* object, size_t num, const char** searches, size_t* rsize, size_t skip);
|
||||
RJP_search_res* rjp_search_members_multi(const RJP_value* object, size_t num, const char* const* searches, size_t* rsize, size_t skip);
|
||||
//Access first element of a json array
|
||||
RJP_value* rjp_get_element(const RJP_value* array);
|
||||
//Access next element of a json array given the previous element
|
||||
@@ -143,16 +166,17 @@ size_t rjp_num_elements(const RJP_value* array);
|
||||
|
||||
|
||||
//Return handle to parent of given value
|
||||
RJP_value* rjp_value_parent(RJP_value* element);
|
||||
RJP_value* rjp_value_parent(const RJP_value* element);
|
||||
//Return type of value
|
||||
RJP_type rjp_value_type(RJP_value* value);
|
||||
RJP_type rjp_value_type(const RJP_value* value);
|
||||
|
||||
//value accessors
|
||||
double rjp_value_dfloat(RJP_value* value);
|
||||
int rjp_value_integer(RJP_value* value);
|
||||
char rjp_value_boolean(RJP_value* value);
|
||||
RJP_float rjp_value_dfloat(const RJP_value* value);
|
||||
RJP_int rjp_value_integer(const RJP_value* value);
|
||||
char rjp_value_boolean(const RJP_value* value);
|
||||
char* rjp_value_string(RJP_value* value);
|
||||
size_t rjp_value_string_length(RJP_value* value);
|
||||
const char* rjp_value_cstring(const RJP_value* value);
|
||||
size_t rjp_value_string_length(const RJP_value* value);
|
||||
|
||||
|
||||
//copy input string and add json escape sequences
|
||||
@@ -162,11 +186,11 @@ size_t rjp_escape_strcpy(char* dest, const char* src);
|
||||
size_t rjp_escape_strlen(const char* str);
|
||||
|
||||
//Convert RJP_object to json string
|
||||
size_t rjp_dump_object(RJP_value* root, char* dest);
|
||||
size_t rjp_dump_object(const RJP_value* root, char* dest);
|
||||
//Convert RJP_array to json string
|
||||
size_t rjp_dump_array(RJP_value* arr, char* dest);
|
||||
size_t rjp_dump_array(const RJP_value* arr, char* dest);
|
||||
//Convert root rjp value into json string
|
||||
char* rjp_to_json(RJP_value* root);
|
||||
char* rjp_to_json(const RJP_value* root);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
rjp
|
||||
Copyright (C) 2018 rexy712
|
||||
Copyright (C) 2018-2019 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
rjp
|
||||
Copyright (C) 2018 rexy712
|
||||
Copyright (C) 2018-2019 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -23,10 +23,10 @@
|
||||
#include <stddef.h> //size_t
|
||||
|
||||
int _rjp__is_whitespace(char c);
|
||||
char* _rjp__parse_string(RJP_value* root, const char* str, int* len, int* row, int* column);
|
||||
size_t _rjp__array_strlen(RJP_value* arr);
|
||||
size_t _rjp__object_strlen(RJP_value* root);
|
||||
size_t _rjp__value_strlen(RJP_value* root);
|
||||
char* _rjp__parse_string(RJP_value* root, const char* str, int* inclen, int* len, int* row, int* column);
|
||||
size_t _rjp__array_strlen(const RJP_value* arr);
|
||||
size_t _rjp__object_strlen(const RJP_value* root);
|
||||
size_t _rjp__value_strlen(const RJP_value* root);
|
||||
void _rjp__strcpy(RJP_string* dest, const RJP_string* src);
|
||||
|
||||
#endif
|
||||
|
||||
532
src/input.c
532
src/input.c
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
rjp
|
||||
Copyright (C) 2018 rexy712
|
||||
Copyright (C) 2018-2019 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -24,14 +24,17 @@
|
||||
#include "memory.h"
|
||||
#include <stdlib.h> //strtod, strtol
|
||||
#include <stdio.h> //fprintf, stderr
|
||||
#include <string.h> //memset
|
||||
|
||||
//types of searches in the text
|
||||
typedef enum json_search_target{
|
||||
json_key,
|
||||
json_colon,
|
||||
json_comma,
|
||||
json_value,
|
||||
json_none
|
||||
json_target_key,
|
||||
json_target_colon,
|
||||
json_target_comma,
|
||||
json_target_value,
|
||||
json_target_string,
|
||||
json_target_numeral,
|
||||
json_target_none
|
||||
}json_search_target;
|
||||
|
||||
static RJP_value* _rjp__add_value(RJP_value* curr, RJP_value new_val){
|
||||
@@ -49,233 +52,324 @@ static RJP_value* _rjp__add_value(RJP_value* curr, RJP_value new_val){
|
||||
curr->object.last->value = new_val;
|
||||
return &curr->object.last->value;
|
||||
}
|
||||
#define syntax_error(msg, row, column)\
|
||||
do{DIAG_PRINT(stderr, "Syntax error! %s (%i:%i)\n", msg, row, column);rjp_free_value(root);return NULL;}while(0)
|
||||
|
||||
#define MAX_DEPTH 16
|
||||
RJP_value* rjp_parse(const char* str){
|
||||
RJP_value* root = 0;
|
||||
RJP_value* curr = 0;
|
||||
int row = 1, column = 0;
|
||||
int in_line_comment = 0;
|
||||
int in_block_comment = 0;
|
||||
|
||||
//keep track of where we are in a given subobject
|
||||
int state_stack[MAX_DEPTH] = {0},*top = state_stack;
|
||||
typedef struct RJP_string_state{
|
||||
int escaped;
|
||||
int in_utf_sequence;
|
||||
char* buffer; //store partial string here only when chunked reading and chunk ends mid string
|
||||
}RJP_string_state;
|
||||
|
||||
typedef struct RJP_numeral_state{
|
||||
int numlen;
|
||||
char* buffer; //store partial number string here only when chunked reading and chunk ends mid number
|
||||
}RJP_numeral_state;
|
||||
|
||||
typedef struct RJP_parse_state{
|
||||
RJP_value* root;
|
||||
RJP_value* curr;
|
||||
union{
|
||||
RJP_string_state str_state;
|
||||
RJP_numeral_state num_state;
|
||||
};
|
||||
int row, column;
|
||||
int in_line_comment;
|
||||
int in_block_comment;
|
||||
int target_stack[MAX_DEPTH];
|
||||
int* target;
|
||||
}RJP_parse_state;
|
||||
|
||||
void _rjp__init_parse_state(RJP_parse_state* state){
|
||||
state->root = NULL;
|
||||
state->curr = NULL;
|
||||
state->row = state->column = 0;
|
||||
state->in_line_comment = 0;
|
||||
state->in_block_comment = 0;
|
||||
memset(state->target_stack, 0, MAX_DEPTH*sizeof(int));
|
||||
state->target = state->target_stack;
|
||||
}
|
||||
|
||||
static void syntax_error(const char* msg, RJP_parse_state* state){
|
||||
DIAG_PRINT(stderr, "Syntax error! %s (%i:%i)\n", msg, state->row, state->column);
|
||||
rjp_free_value(state->root);
|
||||
}
|
||||
|
||||
//Return number of characters handled while processing comment
|
||||
int _rjp__handle_comment(const char* str, RJP_parse_state* state){
|
||||
char c = *str;
|
||||
if(state->in_line_comment){
|
||||
if(c == '\n')
|
||||
state->in_line_comment = 0;
|
||||
return 1;
|
||||
}else if(state->in_block_comment){
|
||||
if(c == '*' && *(str+1) == '/'){
|
||||
state->in_block_comment = 0;
|
||||
return 2;
|
||||
}
|
||||
return 1;
|
||||
}else if(c == '/' && *(str+1) == '/'){
|
||||
state->in_block_comment = 1;
|
||||
return 2;
|
||||
}else if(c == '/' && *(str+1) == '/'){
|
||||
state->in_line_comment = 1;
|
||||
return 2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
int _rjp__handle_key(const char* str, RJP_parse_state* state){
|
||||
char c = *str;
|
||||
//start of key
|
||||
if(c == '"'){
|
||||
if(state->curr == NULL){
|
||||
syntax_error("Key found outside of object definition!", state);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int keylen;
|
||||
int inclen;
|
||||
char* new_string = _rjp__parse_string(state->root, str+1, &inclen, &keylen, &state->row, &state->column);
|
||||
if(!new_string){
|
||||
if(!keylen)
|
||||
syntax_error("Cannot have empty key name!", state);
|
||||
return -1;
|
||||
}
|
||||
_rjp__add_member_no_alloc(&(state->curr->object), new_string, keylen);
|
||||
*state->target = json_target_colon;
|
||||
return inclen+2;
|
||||
//end of this object (object is empty)
|
||||
}else if(c == '}'){
|
||||
state->curr = state->curr->parent;
|
||||
if(state->target != state->target_stack)
|
||||
--state->target;
|
||||
return 1;
|
||||
|
||||
//unrecognized character
|
||||
}else if(!_rjp__is_whitespace(c)){
|
||||
syntax_error("Unexpected character, expected '\"'!", state);
|
||||
return -1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int _rjp__handle_colon(const char* str, RJP_parse_state* state){
|
||||
char c = *str;
|
||||
//colon after a key
|
||||
if(c == ':'){
|
||||
*state->target = json_target_value;
|
||||
//unrecognized character
|
||||
}else if(!_rjp__is_whitespace(c)){
|
||||
syntax_error( "Unexpected character, expected ':'!", state);
|
||||
return -1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
int _rjp__handle_comma(const char* str, RJP_parse_state* state){
|
||||
char c = *str;
|
||||
//comma separating keys in an object or values in an array
|
||||
if(c == ','){
|
||||
*state->target = (state->curr->type == json_array ? json_target_value : json_target_key);
|
||||
|
||||
//end of object
|
||||
}else if(c == '}'){
|
||||
if(state->curr->type == json_array){
|
||||
syntax_error("Unexpected end of object within array!", state);
|
||||
return -1;
|
||||
}
|
||||
state->curr = state->curr->parent;
|
||||
if(state->target != state->target_stack)
|
||||
--state->target;
|
||||
//end of array
|
||||
}else if(c == ']' && state->curr->type == json_array){
|
||||
state->curr = state->curr->parent;
|
||||
//unrecognized character
|
||||
}else if(!_rjp__is_whitespace(c)){
|
||||
syntax_error("Unexpected character, expected ','!", state);
|
||||
return -1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int _rjp__handle_value(const char* str, RJP_parse_state* state){
|
||||
//object
|
||||
char c = *str;
|
||||
if(c == '{'){
|
||||
if(!state->root){
|
||||
state->root = _rjp__add_value(NULL, rjp_object());
|
||||
state->curr = state->root;
|
||||
*state->target = json_target_key;
|
||||
}else{
|
||||
state->curr = _rjp__add_value(state->curr, rjp_object());
|
||||
*state->target = json_target_comma;
|
||||
++state->target;
|
||||
*state->target = json_target_key;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
else if(c == '['){
|
||||
if(!state->root){
|
||||
state->root = _rjp__add_value(NULL, rjp_array());
|
||||
state->curr = state->root;
|
||||
|
||||
}else{
|
||||
state->curr = _rjp__add_value(state->curr, rjp_array());
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
else if(c == ']' && state->curr->type == json_array){ //empty array
|
||||
*state->target = json_target_comma;
|
||||
state->curr = state->curr->parent;
|
||||
return 1;
|
||||
}
|
||||
//strings
|
||||
else if(c == '"'){
|
||||
int vallen, inclen;
|
||||
char* new_string = _rjp__parse_string(state->root, str+1, &inclen, &vallen, &state->row, &state->column);
|
||||
if(!new_string){
|
||||
if(vallen == 0){
|
||||
new_string = rjp_calloc(1, 1);
|
||||
}else{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
_rjp__add_value(state->curr, rjp_string(new_string, vallen));
|
||||
*state->target = json_target_comma;
|
||||
return inclen+2;
|
||||
}
|
||||
//numbers
|
||||
else if((c >= '0' && c <= '9') || c == '-'){
|
||||
if(!state->curr)
|
||||
*state->target = json_target_none;
|
||||
else
|
||||
*state->target = json_target_comma;
|
||||
int numlen;
|
||||
int floating = 0; //is an int or a double
|
||||
for(numlen = 1;*(str+numlen) >= '0' && *(str+numlen) <= '9';++numlen);
|
||||
if(*(str+numlen) == '.'){ //if we have a decimal, make it a double and continue parsing as a number
|
||||
int i = ++numlen;
|
||||
for(;*(str+numlen) >= '0' && *(str+numlen) <= '9';++numlen);
|
||||
if(i == numlen){ //no number after decimal
|
||||
syntax_error("Missing numerals after decimal place!", state);
|
||||
return -1;
|
||||
}
|
||||
floating = 1;
|
||||
}
|
||||
if(*(str+numlen) == '\0' && state->curr){ //hit EOF early
|
||||
syntax_error("Unexpected EOF before end of object!", state);
|
||||
return -1;
|
||||
}
|
||||
if(c == '-' && numlen == 1){ //only have a '-' with no numbers
|
||||
syntax_error("Missing numerals after '-' sign!", state);
|
||||
return -1;
|
||||
}
|
||||
if(floating){
|
||||
if(!state->root){
|
||||
state->root = state->curr = _rjp__add_value(NULL, rjp_dfloat(strtod(str, NULL)));
|
||||
}else{
|
||||
_rjp__add_value(state->curr, rjp_dfloat(strtod(str, NULL)));
|
||||
}
|
||||
}else{
|
||||
if(!state->root){
|
||||
state->root = state->curr = _rjp__add_value(NULL, rjp_integer(strtoll(str, NULL, 10)));
|
||||
}else{
|
||||
_rjp__add_value(state->curr, rjp_integer(strtoll(str, NULL, 10)));
|
||||
}
|
||||
}
|
||||
state->column += numlen;
|
||||
return numlen;
|
||||
}
|
||||
//booleans and null
|
||||
else if(!strncmp(str, "true", 4)){
|
||||
if(!state->curr){
|
||||
*state->target = json_target_none;
|
||||
state->root = state->curr = _rjp__add_value(state->curr, rjp_boolean(1));
|
||||
}else{
|
||||
*state->target = json_target_comma;
|
||||
_rjp__add_value(state->curr, rjp_boolean(1));
|
||||
}
|
||||
state->column += 3;
|
||||
return 4;
|
||||
}else if(!strncmp(str, "false", 5)){
|
||||
if(!state->curr){
|
||||
*state->target = json_target_none;
|
||||
state->root = state->curr = _rjp__add_value(state->curr, rjp_boolean(0));
|
||||
}else{
|
||||
*state->target = json_target_comma;
|
||||
_rjp__add_value(state->curr, rjp_boolean(0));
|
||||
}
|
||||
state->column += 4;
|
||||
return 5;
|
||||
}else if(!strncmp(str, "null", 4)){
|
||||
if(!state->curr){
|
||||
*state->target = json_target_none;
|
||||
state->root = state->curr = _rjp__add_value(state->curr, rjp_null());
|
||||
}else{
|
||||
*state->target = json_target_comma;
|
||||
_rjp__add_value(state->curr, rjp_null());
|
||||
}
|
||||
state->column += 3;
|
||||
return 4;
|
||||
}
|
||||
//unrecognized character
|
||||
else if(!_rjp__is_whitespace(c)){
|
||||
syntax_error("Unexpected character!", state);
|
||||
return -1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
RJP_value* rjp_parse(const char* str){
|
||||
RJP_parse_state state;
|
||||
_rjp__init_parse_state(&state);
|
||||
|
||||
//initially search for the root object
|
||||
*top = json_value;
|
||||
*state.target = json_target_value;
|
||||
|
||||
for(;*str != '\0';++str){
|
||||
int inc = 0;
|
||||
for(;*str != '\0';str += inc){
|
||||
char c = *str;
|
||||
|
||||
//keep track of position in input file
|
||||
if(c == '\n'){
|
||||
++row;
|
||||
column = 0;
|
||||
++state.row;
|
||||
state.column = 0;
|
||||
}else{
|
||||
++column;
|
||||
++state.column;
|
||||
}
|
||||
|
||||
//Handle comments
|
||||
if(in_line_comment){
|
||||
if(c == '\n')
|
||||
in_line_comment = 0;
|
||||
}
|
||||
else if(in_block_comment){
|
||||
if(c == '*' && *(str+1) == '/'){
|
||||
in_block_comment = 0;
|
||||
++str;
|
||||
}
|
||||
}
|
||||
else if(c == '/' && *(str+1) == '/'){
|
||||
in_line_comment = 1;
|
||||
++str;
|
||||
}
|
||||
else if(c == '/' && *(str+1) == '*'){
|
||||
in_block_comment = 1;
|
||||
++str;
|
||||
if((inc = _rjp__handle_comment(str, &state))){
|
||||
continue;
|
||||
}
|
||||
|
||||
else if(*top == json_key){
|
||||
//start of key
|
||||
if(c == '"'){
|
||||
if(curr == NULL)
|
||||
syntax_error("Key found outside of object definition!", row, column);
|
||||
|
||||
int keylen;
|
||||
char* new_string = _rjp__parse_string(root, ++str, &keylen, &row, &column);
|
||||
if(!new_string){
|
||||
if(!keylen)
|
||||
syntax_error("Cannot have empty key name!", row, column);
|
||||
return NULL;
|
||||
}
|
||||
_rjp__add_member_no_alloc(&curr->object, new_string, keylen);
|
||||
str += keylen;
|
||||
*top = json_colon;
|
||||
//end of this object (object is empty)
|
||||
}else if(c == '}'){
|
||||
curr = curr->parent;
|
||||
if(top != state_stack)
|
||||
--top;
|
||||
|
||||
//unrecognized character
|
||||
}else if(!_rjp__is_whitespace(c)){
|
||||
syntax_error("Unexpected character, expected '\"'!", row, column);
|
||||
switch(*state.target){
|
||||
case json_target_key:
|
||||
inc = _rjp__handle_key(str, &state);
|
||||
break;
|
||||
case json_target_colon:
|
||||
inc = _rjp__handle_colon(str, &state);
|
||||
break;
|
||||
case json_target_comma:
|
||||
inc = _rjp__handle_comma(str, &state);
|
||||
break;
|
||||
case json_target_value:
|
||||
inc = _rjp__handle_value(str, &state);
|
||||
break;
|
||||
case json_target_none:
|
||||
if(!_rjp__is_whitespace(*str)){
|
||||
syntax_error("Unexpected character!", &state);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else if(*top == json_colon){
|
||||
//colon after a key
|
||||
if(c == ':'){
|
||||
*top = json_value;
|
||||
//unrecognized character
|
||||
}else if(!_rjp__is_whitespace(c)){
|
||||
syntax_error( "Unexpected character, expected ':'!", row, column);
|
||||
}
|
||||
}
|
||||
else if(*top == json_comma){
|
||||
//comma separating keys in an object or values in an array
|
||||
if(c == ','){
|
||||
*top = (curr->type == json_array ? json_value : json_key);
|
||||
|
||||
//end of object
|
||||
}else if(c == '}'){
|
||||
if(curr->type == json_array){
|
||||
syntax_error("Unexpected end of object within array!", row, column);
|
||||
}
|
||||
curr = curr->parent;
|
||||
if(top != state_stack)
|
||||
--top;
|
||||
//end of array
|
||||
}else if(c == ']' && curr->type == json_array){
|
||||
curr = curr->parent;
|
||||
//unrecognized character
|
||||
}else if(!_rjp__is_whitespace(c)){
|
||||
syntax_error("Unexpected character, expected ','!", row, column);
|
||||
}
|
||||
}
|
||||
else if(*top == json_value){
|
||||
//object
|
||||
if(c == '{'){
|
||||
if(!root){
|
||||
root = _rjp__add_value(NULL, rjp_object());
|
||||
curr = root;
|
||||
*top = json_key;
|
||||
}else{
|
||||
curr = _rjp__add_value(curr, rjp_object());
|
||||
*top = json_comma;
|
||||
++top;
|
||||
*top = json_key;
|
||||
}
|
||||
}
|
||||
else if(c == '['){
|
||||
if(!root){
|
||||
root = _rjp__add_value(NULL, rjp_array());
|
||||
curr = root;
|
||||
|
||||
}else{
|
||||
curr = _rjp__add_value(curr, rjp_array());
|
||||
}
|
||||
}
|
||||
else if(c == ']' && curr->type == json_array){ //empty array
|
||||
*top = json_comma;
|
||||
curr = curr->parent;
|
||||
}
|
||||
//strings
|
||||
else if(c == '"'){
|
||||
int vallen;
|
||||
++str;
|
||||
char* new_string = _rjp__parse_string(root, str, &vallen, &row, &column);
|
||||
if(!new_string){
|
||||
if(vallen == 0){
|
||||
new_string = rjp_calloc(1, 1);
|
||||
}else{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
_rjp__add_value(curr, rjp_string(new_string, vallen));
|
||||
str += vallen;
|
||||
*top = json_comma;
|
||||
}
|
||||
//numbers
|
||||
else if((c >= '0' && c <= '9') || c == '-'){
|
||||
if(!curr)
|
||||
*top = json_none;
|
||||
else
|
||||
*top = json_comma;
|
||||
int numlen;
|
||||
int floating = 0; //is an int or a double
|
||||
for(numlen = 1;*(str+numlen) >= '0' && *(str+numlen) <= '9';++numlen);
|
||||
if(*(str+numlen) == '.'){ //if we have a decimal, make it a double and continue parsing as a number
|
||||
int i = ++numlen;
|
||||
for(;*(str+numlen) >= '0' && *(str+numlen) <= '9';++numlen);
|
||||
if(i == numlen){ //no number after decimal
|
||||
syntax_error("Missing numerals after decimal place!", row, column);
|
||||
}
|
||||
floating = 1;
|
||||
}
|
||||
if(*(str+numlen) == '\0' && curr){ //hit EOF early
|
||||
syntax_error("Unexpected EOF before end of object!", row, column);
|
||||
}
|
||||
if(c == '-' && numlen == 1){ //only have a '-' with no numbers
|
||||
syntax_error("Missing numerals ofter '-' sign!", row, column);
|
||||
}
|
||||
if(floating){
|
||||
if(!root){
|
||||
root = curr = _rjp__add_value(NULL, rjp_dfloat(strtod(str, NULL)));
|
||||
}else{
|
||||
_rjp__add_value(curr, rjp_dfloat(strtod(str, NULL)));
|
||||
}
|
||||
}else{
|
||||
if(!root){
|
||||
root = curr = _rjp__add_value(NULL, rjp_integer(strtol(str, NULL, 10)));
|
||||
}else{
|
||||
_rjp__add_value(curr, rjp_integer(strtol(str, NULL, 10)));
|
||||
}
|
||||
}
|
||||
str += (numlen-1);
|
||||
column += numlen;
|
||||
}
|
||||
//booleans and null
|
||||
else if(!strncmp(str, "true", 4)){
|
||||
if(!curr){
|
||||
*top = json_none;
|
||||
root = curr = _rjp__add_value(curr, rjp_boolean(1));
|
||||
}else{
|
||||
*top = json_comma;
|
||||
_rjp__add_value(curr, rjp_boolean(1));
|
||||
}
|
||||
str += 3;column += 3;
|
||||
}else if(!strncmp(str, "false", 5)){
|
||||
if(!curr){
|
||||
*top = json_none;
|
||||
root = curr = _rjp__add_value(curr, rjp_boolean(0));
|
||||
}else{
|
||||
*top = json_comma;
|
||||
_rjp__add_value(curr, rjp_boolean(0));
|
||||
}
|
||||
str += 4;column += 4;
|
||||
}else if(!strncmp(str, "null", 4)){
|
||||
if(!curr){
|
||||
*top = json_none;
|
||||
root = curr = _rjp__add_value(curr, rjp_null());
|
||||
}else{
|
||||
*top = json_comma;
|
||||
_rjp__add_value(curr, rjp_null());
|
||||
}
|
||||
str += 3;column += 3;
|
||||
}
|
||||
//unrecognized character
|
||||
else if(!_rjp__is_whitespace(c)){
|
||||
syntax_error("Unexpected character!", row, column);
|
||||
}
|
||||
}else if(*top == json_none && !_rjp__is_whitespace(c)){
|
||||
syntax_error("Unexpected character!", row, column);
|
||||
}
|
||||
inc = 1;
|
||||
break;
|
||||
default:
|
||||
inc = 1;
|
||||
break;
|
||||
};
|
||||
}
|
||||
return root;
|
||||
return state.root;
|
||||
}
|
||||
RJP_value* rjp_parse_chunked(const char* str, RJP_value* prev_chunk){
|
||||
if(!prev_chunk){
|
||||
return rjp_parse(str);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#undef syntax_error
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
rjp
|
||||
Copyright (C) 2018 rexy712
|
||||
Copyright (C) 2018-2019 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
23
src/output.c
23
src/output.c
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
rjp
|
||||
Copyright (C) 2018 rexy712
|
||||
Copyright (C) 2018-2019 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -16,6 +16,9 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "rjp.h"
|
||||
#include "rjp_internal.h"
|
||||
#include "memory.h"
|
||||
@@ -33,11 +36,11 @@ RJP_value* rjp_init_json_as(RJP_value value){
|
||||
return ret;
|
||||
}
|
||||
|
||||
static size_t _rjp__write_value(char* dest, RJP_value* val){
|
||||
static size_t _rjp__write_value(char* dest, const RJP_value* val){
|
||||
size_t ret;
|
||||
switch(val->type){
|
||||
case json_integer:
|
||||
ret = sprintf(dest, "%li", val->integer);
|
||||
ret = sprintf(dest, "%" PRId64, val->integer);
|
||||
break;
|
||||
case json_dfloat:
|
||||
ret = sprintf(dest, "%lf", val->dfloat);
|
||||
@@ -64,9 +67,9 @@ static size_t _rjp__write_value(char* dest, RJP_value* val){
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t rjp_dump_array(RJP_value* arr, char* dest){
|
||||
RJP_array* array = &arr->array;
|
||||
RJP_array_element* element_list = array->elements;
|
||||
size_t rjp_dump_array(const RJP_value* arr, char* dest){
|
||||
const RJP_array* array = &arr->array;
|
||||
const RJP_array_element* element_list = array->elements;
|
||||
size_t pos = 1;
|
||||
sprintf(dest, "[");
|
||||
for(;element_list;element_list = element_list->next){
|
||||
@@ -81,9 +84,9 @@ size_t rjp_dump_array(RJP_value* arr, char* dest){
|
||||
return pos;
|
||||
}
|
||||
|
||||
size_t rjp_dump_object(RJP_value* root, char* dest){
|
||||
RJP_object* root_obj = &root->object;
|
||||
RJP_object_member* member_list = root_obj->members;
|
||||
size_t rjp_dump_object(const RJP_value* root, char* dest){
|
||||
const RJP_object* root_obj = &root->object;
|
||||
const RJP_object_member* member_list = root_obj->members;
|
||||
size_t pos = 1;
|
||||
sprintf(dest, "{");
|
||||
|
||||
@@ -100,7 +103,7 @@ size_t rjp_dump_object(RJP_value* root, char* dest){
|
||||
return pos;
|
||||
}
|
||||
|
||||
char* rjp_to_json(RJP_value* root){
|
||||
char* rjp_to_json(const RJP_value* root){
|
||||
if(!root)
|
||||
return NULL;
|
||||
size_t len = _rjp__value_strlen(root);
|
||||
|
||||
102
src/rjp.c
102
src/rjp.c
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
rjp
|
||||
Copyright (C) 2018 rexy712
|
||||
Copyright (C) 2018-2019 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -80,12 +80,38 @@ RJP_value* rjp_add_element(RJP_value* dest, RJP_value value){
|
||||
dest->array.last->value.parent = dest;
|
||||
return &dest->array.last->value;
|
||||
}
|
||||
void rjp_set_key(RJP_value* dest, const char* key, size_t keylen){
|
||||
RJP_object_member* mem = (RJP_object_member*)dest;
|
||||
if(key){
|
||||
if(!keylen){
|
||||
keylen = strlen(key);
|
||||
}
|
||||
}else{
|
||||
keylen = 0;
|
||||
}
|
||||
mem->name.value = rjp_alloc(keylen + 1);
|
||||
strncpy(mem->name.value, key, keylen);
|
||||
mem->name.value[keylen] = 0;
|
||||
mem->name.length = keylen;
|
||||
}
|
||||
void rjp_set_key_no_alloc(RJP_value* dest, char* key, size_t keylen){
|
||||
RJP_object_member* mem = (RJP_object_member*)dest;
|
||||
if(key){
|
||||
if(!keylen){
|
||||
keylen = strlen(key);
|
||||
}
|
||||
}else{
|
||||
keylen = 0;
|
||||
}
|
||||
mem->name.value = key;
|
||||
mem->name.length = keylen;
|
||||
}
|
||||
void rjp_set_value(RJP_value* dest, RJP_value value){
|
||||
struct RJP_value* p = dest->parent;
|
||||
*dest = value;
|
||||
dest->parent = p;
|
||||
}
|
||||
RJP_value rjp_integer(long i){
|
||||
RJP_value rjp_integer(RJP_int i){
|
||||
return (RJP_value){.integer = i, .type = json_integer};
|
||||
}
|
||||
RJP_value rjp_boolean(char b){
|
||||
@@ -97,6 +123,12 @@ RJP_value rjp_dfloat(double d){
|
||||
RJP_value rjp_string(char* c, size_t len){
|
||||
return (RJP_value){.string = {.value = c, .length = len}, .type = json_string};
|
||||
}
|
||||
RJP_value rjp_string_copy(const char* c){
|
||||
size_t esclen = rjp_escape_strlen(c);
|
||||
char* tmp = rjp_alloc(esclen+1);
|
||||
rjp_escape_strcpy(tmp, c);
|
||||
return (RJP_value){.string = {.value = tmp, .length = esclen}, .type = json_string};
|
||||
}
|
||||
RJP_value rjp_null(void){
|
||||
return (RJP_value){.integer = 0, .type = json_null};
|
||||
}
|
||||
@@ -121,67 +153,66 @@ size_t rjp_num_members(const RJP_value* object){
|
||||
char* rjp_member_name(const RJP_value* member){
|
||||
return ((RJP_object_member*)member)->name.value;
|
||||
}
|
||||
size_t rjp_member_name_length(RJP_value* member){
|
||||
size_t rjp_member_name_length(const RJP_value* member){
|
||||
return ((RJP_object_member*)member)->name.length;
|
||||
}
|
||||
RJP_value* rjp_search_member(const RJP_value* object, const char* search, size_t* index, size_t skip){
|
||||
|
||||
RJP_search_res rjp_search_member(const RJP_value* object, const char* search, size_t skip){
|
||||
RJP_value* member = rjp_get_member(object);
|
||||
size_t i = 0;
|
||||
for(;i < skip && member;member = rjp_next_member(member),++i);
|
||||
|
||||
for(;member;member = rjp_next_member(member),++i){
|
||||
if(!strcmp(((RJP_object_member*)member)->name.value, search)){
|
||||
if(index){
|
||||
*index = i;
|
||||
}
|
||||
return member;
|
||||
return (RJP_search_res){member, 0, i};
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
return (RJP_search_res){0};
|
||||
}
|
||||
size_t rjp_search_members(const RJP_value* object, size_t num, const char** searches, RJP_value** dest, size_t skip){
|
||||
const char** tmp = rjp_alloc(num*sizeof(char*));
|
||||
|
||||
size_t rjp_search_members(const RJP_value* object, size_t num, const char* const* searches, RJP_search_res* dest, size_t skip){
|
||||
for(size_t i = 0;i < num;++i){
|
||||
tmp[i] = searches[i];
|
||||
dest[i] = NULL;
|
||||
dest[i] = (RJP_search_res){0};
|
||||
}
|
||||
RJP_value* member = rjp_get_member(object);
|
||||
for(size_t i = 0;i < skip && member;member = rjp_next_member(member));
|
||||
size_t objindex = 0;
|
||||
for(;objindex < skip && member;member = rjp_next_member(member),++objindex);
|
||||
size_t matches = 0;
|
||||
|
||||
for(;member;member = rjp_next_member(member)){
|
||||
for(;member;member = rjp_next_member(member),++objindex){
|
||||
for(size_t i = 0;i < num;++i){
|
||||
if(searches[i] == NULL)
|
||||
if(dest[i].value != NULL)
|
||||
continue;
|
||||
if(!strcmp(((RJP_object_member*)member)->name.value, searches[i])){
|
||||
dest[i] = member;
|
||||
searches[i] = NULL;
|
||||
dest[i].value = member;
|
||||
dest[i].searchindex = i;
|
||||
dest[i].objindex = objindex;
|
||||
++matches;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for(size_t i = 0;i < num;++i){
|
||||
searches[i] = tmp[i];
|
||||
}
|
||||
rjp_free(tmp);
|
||||
return matches;
|
||||
}
|
||||
RJP_value** rjp_search_members_multi(const RJP_value* object, size_t num, const char** searches, size_t* rsize, size_t skip){
|
||||
RJP_value** ret = rjp_alloc(num*sizeof(RJP_value*));
|
||||
RJP_search_res* rjp_search_members_multi(const RJP_value* object, size_t num, const char* const* searches, size_t* rsize, size_t skip){
|
||||
RJP_search_res* ret = rjp_alloc(num*sizeof(RJP_search_res));
|
||||
size_t ret_size = num;
|
||||
size_t j = 0;
|
||||
|
||||
RJP_value* member = rjp_get_member(object);
|
||||
for(size_t i = 0;i < skip && member;member = rjp_next_member(member));
|
||||
size_t objindex = 0;
|
||||
for(;objindex < skip && member;member = rjp_next_member(member),++objindex);
|
||||
|
||||
for(;member;member = rjp_next_member(member)){
|
||||
for(;member;member = rjp_next_member(member),++objindex){
|
||||
for(size_t i = 0;i < num;++i){
|
||||
if(!strcmp(((RJP_object_member*)member)->name.value, searches[i])){
|
||||
ret[j++] = member;
|
||||
ret[j].value = member;
|
||||
ret[j].searchindex = i;
|
||||
ret[j].objindex = objindex;
|
||||
++j;
|
||||
if(j == ret_size){
|
||||
ret_size *= 2;
|
||||
ret = rjp_realloc(ret, ret_size*sizeof(RJP_value*));
|
||||
ret = rjp_realloc(ret, ret_size*sizeof(RJP_search_res));
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -206,25 +237,28 @@ size_t rjp_num_elements(const RJP_value* array){
|
||||
return array->array.num_elements;
|
||||
}
|
||||
|
||||
RJP_value* rjp_value_parent(RJP_value* value){
|
||||
RJP_value* rjp_value_parent(const RJP_value* value){
|
||||
return value->parent;
|
||||
}
|
||||
RJP_type rjp_value_type(RJP_value* value){
|
||||
RJP_type rjp_value_type(const RJP_value* value){
|
||||
return value->type;
|
||||
}
|
||||
double rjp_value_dfloat(RJP_value* value){
|
||||
RJP_float rjp_value_dfloat(const RJP_value* value){
|
||||
return value->dfloat;
|
||||
}
|
||||
int rjp_value_integer(RJP_value* value){
|
||||
RJP_int rjp_value_integer(const RJP_value* value){
|
||||
return value->integer;
|
||||
}
|
||||
char rjp_value_boolean(RJP_value* value){
|
||||
char rjp_value_boolean(const RJP_value* value){
|
||||
return value->boolean;
|
||||
}
|
||||
char* rjp_value_string(RJP_value* value){
|
||||
return value->string.value;
|
||||
}
|
||||
size_t rjp_value_string_length(RJP_value* value){
|
||||
const char* rjp_value_cstring(const RJP_value* value){
|
||||
return value->string.value;
|
||||
}
|
||||
size_t rjp_value_string_length(const RJP_value* value){
|
||||
return value->string.length;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
rjp
|
||||
Copyright (C) 2018 rexy712
|
||||
Copyright (C) 2018-2019 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -16,6 +16,9 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "strings.h"
|
||||
#include "rjp_internal.h"
|
||||
|
||||
@@ -148,7 +151,7 @@ static uint32_t u8_to_codepoint(char* u){
|
||||
}
|
||||
|
||||
//Convert escape sequences in strings
|
||||
char* _rjp__parse_string(RJP_value* root, const char* str, int* len, int* row, int* column){
|
||||
char* _rjp__parse_string(RJP_value* root, const char* str, int* inclen, int* len, int* row, int* column){
|
||||
char* new_string;
|
||||
++(*column); //account for starting quotation mark
|
||||
int oldpos = 0;
|
||||
@@ -173,7 +176,8 @@ char* _rjp__parse_string(RJP_value* root, const char* str, int* len, int* row, i
|
||||
*column = 0;
|
||||
}
|
||||
}
|
||||
*len = oldpos;
|
||||
*inclen = oldpos;
|
||||
*len = newpos;
|
||||
if(newpos == 0){
|
||||
return NULL;
|
||||
}
|
||||
@@ -293,14 +297,14 @@ size_t rjp_escape_strlen(const char* str){
|
||||
}
|
||||
return count;
|
||||
}
|
||||
size_t _rjp__array_strlen(RJP_value* arr){
|
||||
size_t _rjp__array_strlen(const RJP_value* arr){
|
||||
size_t count = 2; //[]
|
||||
RJP_array* array = &arr->array;
|
||||
RJP_array_element* element_list = array->elements;
|
||||
const RJP_array* array = &arr->array;
|
||||
const RJP_array_element* element_list = array->elements;
|
||||
for(;element_list;element_list = element_list->next){
|
||||
switch(element_list->value.type){
|
||||
case json_integer:
|
||||
count += snprintf(NULL, 0, "%li", element_list->value.integer);
|
||||
count += snprintf(NULL, 0, "%" PRId64, element_list->value.integer);
|
||||
break;
|
||||
case json_dfloat:
|
||||
count += snprintf(NULL, 0, "%lf", element_list->value.dfloat);
|
||||
@@ -329,10 +333,10 @@ size_t _rjp__array_strlen(RJP_value* arr){
|
||||
return count;
|
||||
}
|
||||
|
||||
size_t _rjp__object_strlen(RJP_value* root){
|
||||
size_t _rjp__object_strlen(const RJP_value* root){
|
||||
size_t count = 2; //{}
|
||||
RJP_object* root_obj = &root->object;
|
||||
RJP_object_member* member_list = root_obj->members;
|
||||
const RJP_object* root_obj = &root->object;
|
||||
const RJP_object_member* member_list = root_obj->members;
|
||||
for(;member_list;member_list = member_list->next){
|
||||
if(member_list->name.length == 0 || member_list->name.value[0] == 0)
|
||||
return 0;
|
||||
@@ -346,10 +350,10 @@ size_t _rjp__object_strlen(RJP_value* root){
|
||||
return count;
|
||||
}
|
||||
|
||||
size_t _rjp__value_strlen(RJP_value* root){
|
||||
size_t _rjp__value_strlen(const RJP_value* root){
|
||||
switch(root->type){
|
||||
case json_integer:
|
||||
return snprintf(NULL, 0, "%li", root->integer);
|
||||
return snprintf(NULL, 0, "%" PRId64, root->integer);
|
||||
case json_dfloat:
|
||||
return snprintf(NULL, 0, "%lf", root->dfloat);
|
||||
case json_boolean:
|
||||
|
||||
Reference in New Issue
Block a user