19 Commits
v0.2 ... v0.5

Author SHA1 Message Date
rexy712
fa071dd034 Added builtin search functionality 2019-02-08 14:57:39 -08:00
rexy712
25da591780 Add accessor functions for array and object member/element count 2018-12-21 12:50:19 -08:00
rexy712
4b5586ffed Reorganized file structure 2018-12-21 12:44:47 -08:00
Rexy712
6d89e4dc5a Simplified copy logic and fixed string parsing allocation issue 2018-12-19 14:06:43 -08:00
rexy712
adad380228 Added missing operation to copy 2018-12-19 00:44:10 -08:00
Rexy712
e1813d718f Add copy operation, added documentation, broke ABI by adding some const 2018-12-18 15:50:24 -08:00
rexy712
83655214f0 Split rjp_add_member into 2 different functions to maintain const correctness 2018-12-16 14:29:16 -08:00
rexy712
20e46d2af2 Added support for unicode escape sequences 2018-12-15 05:33:44 -08:00
rexy712
383cb5af54 Add revision specifier 2018-12-14 17:50:20 -08:00
rexy712
686541279f Added ability to build shared library 2018-12-14 17:47:26 -08:00
rexy712
edcfee90eb vastly simplified logic and fixed a parsing bug 2018-12-06 16:02:40 -08:00
rexy712
dd3bfb49e9 version bump 2018-12-06 15:35:28 -08:00
rexy712
af9b0b837e Allow root value to be non-object 2018-12-06 15:34:31 -08:00
rexy712
cd5ddd950b changed std allocation to rjp allocations 2018-12-01 10:15:17 -08:00
rexy712
d68bea08f4 added allocation and free functions 2018-12-01 10:11:20 -08:00
rexy712
1445d668a5 fixed missing null terminator 2018-12-01 09:12:28 -08:00
rexy712
6f4a55a766 removed excess 'struct's (typedefs) 2018-12-01 08:33:08 -08:00
rexy712
b27031876b added accessor interface 2018-12-01 08:21:06 -08:00
rexy712
566952de5e Fixed a potential segfault and added diagnostics option to cmake 2018-11-30 14:12:39 -08:00
11 changed files with 919 additions and 424 deletions

View File

@@ -4,7 +4,8 @@ include(GNUInstallDirs)
cmake_minimum_required(VERSION 3.0.2)
project(rjp)
set(rjp_VERSION_MAJOR 0)
set(rjp_VERSION_MINOR 2)
set(rjp_VERSION_MINOR 5)
set(rjp_VERSION_REVISION 0)
set(INCLUDE_PATH ${CMAKE_SOURCE_DIR}/include)
configure_file(
"${INCLUDE_PATH}/config.h.in"
@@ -12,11 +13,26 @@ configure_file(
)
include_directories("${INCLUDE_PATH}")
add_library (rjp STATIC src/input.c src/output.c src/strings.c)
option(ENABLE_DIAGNOSTICS "Print diagnostic messages when parsing json to help identify issues" ON)
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})
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)
endif()
install(TARGETS rjp
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)

View File

@@ -6,7 +6,7 @@ rjp (rexy's json parser) is a very simplistic json parser written in C. There is
Currently only installs 2 files: librjp.a and rjp.h
## Building
Building will produce a static library with no option to create shared because it's such a simple library.
Building will produce a static library by default but can be configured to build a shared library.
##### Run the following commands
```

View File

@@ -21,6 +21,7 @@
#define RJP_VERSION_MAJOR @rjp_VERSION_MAJOR@
#define RJP_VERSION_MINOR @rjp_VERSION_MINOR@
#define RJP_VERSION_REVISION @rjp_VERSION_REVISION@
#endif

View File

@@ -26,106 +26,147 @@ extern "C"{
#endif
//type of data
enum JSON_type{
json_object,
typedef enum RJP_type{
json_object = 0,
json_string,
json_integer,
json_dfloat,
json_boolean,
json_array,
json_null
};
}RJP_type;
enum JSON_flags{
json_flag_string = 1, //in a string
json_flag_comment = 2, //in a comment block
json_flag_search_name = 4, //looking for key/value pair
json_flag_escape = 16 //escaped character in a string
};
//keep track of string length
struct JSON_string{
//Hold a C string and the length excluding NULL terminator
typedef struct RJP_string{
char* value;
size_t length;
};
}RJP_string;
//keep a linked list of all members of a given object
struct JSON_object{
struct JSON_object_member* members;
struct JSON_object_member* last;
//Forward declarations
struct RJP_object_member;
struct RJP_array_element;
//Represents a json object
typedef struct RJP_object{
struct RJP_object_member* members; //linked list of members
struct RJP_object_member* last; //final member of linked list
size_t num_members;
};
}RJP_object;
//keep array plus length
struct JSON_array{
struct JSON_array_element* elements;
struct JSON_array_element* last;
//Represents a json array
typedef struct RJP_array{
struct RJP_array_element* elements; //linked list of elements
struct RJP_array_element* last; //final member of linked list
size_t num_elements;
};
}RJP_array;
//Represents json data
//hold any json data type
struct JSON_value{
typedef struct RJP_value{
union{
long integer;
double dfloat;
char boolean;
struct JSON_object object;
struct JSON_string string;
struct JSON_array array;
struct RJP_object object;
struct RJP_string string;
struct RJP_array array;
};
struct JSON_value* parent;
enum JSON_type type;
};
struct RJP_value* parent; //pointer to parent (either an array or object or NULL)
enum RJP_type type; //flag to determine active member of union
}RJP_value;
struct JSON_array_element{
struct JSON_value value;
struct JSON_array_element* next;
};
//Convert C string consisting of json data into RJP's format
RJP_value* rjp_parse(const char* str);
//A member of an object
struct JSON_object_member{
struct JSON_string name;
struct JSON_value value;
struct JSON_object_member* next;
};
//read in json and convert to easy to work with format
struct JSON_value* rjp_parse(const char* str);
//deallocate a json handle
void rjp_free_json(char* json);
void rjp_free(struct JSON_value* root);
struct JSON_object_member* rjp_get_members(struct JSON_object* j);
struct JSON_object_member* rjp_next_member(struct JSON_object_member* j);
struct JSON_object* rjp_member_parent(struct JSON_object_member* j);
struct JSON_value* rjp_get_member_value(struct JSON_object_member* j);
enum JSON_type rjp_get_member_type(struct JSON_object_member* j);
enum JSON_type rjp_get_value_type(struct JSON_value* j);
//Initialize a root RJP_value to NULL
RJP_value* rjp_init_json(void);
//Initialize a root RJP_value to copy of value
RJP_value* rjp_init_json_as(RJP_value value);
//Free an RJP_value and all members/elements
void rjp_free_value(RJP_value* root);
//Deep copy RJP_value
void rjp_copy_value(RJP_value* dest, const RJP_value* src);
//initialize an empty json handle (outputting to string would result in '{}')
struct JSON_value* rjp_init_json(void);
//Allocate heap space for rjp to use. Use if the memory is to be freed by rjp
void* rjp_alloc(size_t nbytes);
//Allocate heap space for rjp and initialize to 0.
void* rjp_calloc(size_t num, size_t nbytes);
//Resize heap allocated space for rjp
void* rjp_realloc(void* ptr, size_t nbytes);
//Free memory allocated by rjp_alloc or rjp_calloc
void rjp_free(void* dest);
//add a member to a json object
struct JSON_value* rjp_add_member(struct JSON_value* dest, int alloc_key, char* key, size_t keylen, struct JSON_value value);
//add a member to a json object, allocating a copy of the key
RJP_value* rjp_add_member(RJP_value* dest, const char* key, size_t keylen, RJP_value value);
//add a member to a json object without allocation. key must be allocated using rjp_alloc/rjp_calloc
RJP_value* rjp_add_member_no_alloc(RJP_value* dest, char* key, size_t keylen, RJP_value value);
//add an element to a json array
struct JSON_value* rjp_add_element(struct JSON_value* dest, struct JSON_value value);
RJP_value* rjp_add_element(RJP_value* dest, RJP_value value);
struct JSON_value rjp_integer(long i);
struct JSON_value rjp_boolean(char b);
struct JSON_value rjp_dfloat(double d);
struct JSON_value rjp_string(char* c, size_t len);
struct JSON_value rjp_null(void);
struct JSON_value rjp_object(void);
struct JSON_value rjp_array(void);
//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_boolean(char b);
RJP_value rjp_dfloat(double d);
RJP_value rjp_string(char* c, size_t len);
RJP_value rjp_null(void);
RJP_value rjp_object(void);
RJP_value rjp_array(void);
//Access first member of a json object
RJP_value* rjp_get_member(const RJP_value* object);
//Access next member of a json object given the previous member
RJP_value* rjp_next_member(const RJP_value* member);
//Return number of members in the object
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);
//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);
//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);
//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);
//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
RJP_value* rjp_next_element(const RJP_value* element);
//Return number of elements in the array
size_t rjp_num_elements(const RJP_value* array);
//Return handle to parent of given value
RJP_value* rjp_value_parent(RJP_value* element);
//Return type of value
RJP_type rjp_value_type(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);
char* rjp_value_string(RJP_value* value);
size_t rjp_value_string_length(RJP_value* value);
//copy input string and add json escape sequences
size_t rjp_escape_strcpy(char* dest, const char* src);
//length of string including escape sequences
size_t rjp_escape_strlen(const char* str);
size_t rjp_dump_object(struct JSON_value* root, char* dest);
size_t rjp_dump_array(struct JSON_value* arr, char* dest);
char* rjp_to_json(struct JSON_value* root);
//Convert RJP_object to json string
size_t rjp_dump_object(RJP_value* root, char* dest);
//Convert RJP_array to json string
size_t rjp_dump_array(RJP_value* arr, char* dest);
//Convert root rjp value into json string
char* rjp_to_json(RJP_value* root);
#ifdef __cplusplus
}

View File

@@ -1,13 +1,53 @@
/**
rjp
Copyright (C) 2018 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
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef RJP_INTERNAL_H
#define RJP_INTERNAL_H
#include "rjp.h"
char* _rjp__parse_string(struct JSON_value* root, const char* str, int* len, int* row, int* column);
size_t _rjp__object_strlen(struct JSON_value* root);
void _rjp__add_member(struct JSON_object* j, char* str, size_t len);
void _rjp__add_member_no_alloc(struct JSON_object* j, char* str, size_t len);
void _rjp__add_element(struct JSON_array* j);
size_t _rjp__object_strlen(struct JSON_value* root);
#ifdef __GNUC__
#define MAYBE_UNUSED __attribute__((unused))
#else
#define MAYBE_UNUSED
#endif
#ifdef RJP_DIAGNOSTICS
#include <stdio.h>
#define DIAG_PRINT(...) fprintf(__VA_ARGS__)
#else
#define DIAG_PRINT(...)
#endif
//
typedef struct RJP_array_element{
struct RJP_value value;
struct RJP_array_element* next;
}RJP_array_element;
//A member of an object
typedef struct RJP_object_member{
struct RJP_value value;
struct RJP_string name;
struct RJP_object_member* next;
}RJP_object_member;
void _rjp__add_element(RJP_array* j);
void _rjp__add_member(RJP_object* j, const char* str, size_t len);
void _rjp__add_member_no_alloc(RJP_object* j, char* str, size_t len);
#endif

32
include/strings.h Normal file
View File

@@ -0,0 +1,32 @@
/**
rjp
Copyright (C) 2018 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
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef STRINGS_H
#define STRINGS_H
#include "rjp.h"
#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);
void _rjp__strcpy(RJP_string* dest, const RJP_string* src);
#endif

View File

@@ -17,210 +17,54 @@
*/
//TODO: Scientific notation
//TODO: \e escape sequence in strings
#include "rjp.h"
#include "rjp_internal.h"
#include <string.h> //strncpy
#include <stdlib.h> //malloc, calloc, free
#include "strings.h"
#include "memory.h"
#include <stdlib.h> //strtod, strtol
#include <stdio.h> //fprintf, stderr
#ifdef __GNUC__
#define MAYBE_UNUSED __attribute__((unused))
#else
#define MAYBE_UNUSED
#endif
struct JSON_state{
int in_array;
int search_target;
};
//types of searches in the text
enum json_search_target{
typedef enum json_search_target{
json_key,
json_colon,
json_comma,
json_value
};
json_value,
json_none
}json_search_target;
//Determine if the character is valid whitespace
static int _rjp__is_whitespace(char c){
return c == ' ' || c == '\n' || c == '\r' || c == '\t';
}
//add an element to an array
void _rjp__add_element(struct JSON_array* j){
++j->num_elements;
if(!j->elements){
j->elements = calloc(1, sizeof(struct JSON_array_element));
j->last = j->elements;
}else{
j->last->next = calloc(1, sizeof(struct JSON_array_element));
j->last = j->last->next;
}
}
//create member of the object as a linked list member and assign a name with name allocation
void _rjp__add_member(struct JSON_object* j, char* str, size_t len){
++j->num_members;
if(!j->members){
j->members = calloc(1, sizeof(struct JSON_object_member));
j->last = j->members;
}else{
j->last->next = calloc(1, sizeof(struct JSON_object_member));
j->last = j->last->next;
}
j->last->name.value = malloc(len + 1);
strncpy(j->last->name.value, str, len);
j->last->name.value[len] = 0;
j->last->name.length = len;
}
void _rjp__add_member_no_alloc(struct JSON_object* j, char* str, size_t len){
++j->num_members;
if(!j->members){
j->members = calloc(1, sizeof(struct JSON_object_member));
j->last = j->members;
}else{
j->last->next = calloc(1, sizeof(struct JSON_object_member));
j->last = j->last->next;
}
j->last->name.value = str;
j->last->name.length = len;
}
static struct JSON_value* _rjp__add_element_to_array(struct JSON_value* curr, struct JSON_state* state, struct JSON_value* element){
struct JSON_value* tmp = &curr->object.members->value;
for(int i = state->in_array-1;i > 0;--i, tmp = &tmp->array.last->value);
_rjp__add_element(&tmp->array);
tmp->array.last->value = *element;
return &tmp->array.last->value;
}
//Assign object characteristics to previously allocated JSON_value
static struct JSON_value* _rjp__add_object(struct JSON_value* curr, struct JSON_state* state){
static RJP_value* _rjp__add_value(RJP_value* curr, RJP_value new_val){
new_val.parent = curr;
if(!curr){
curr = calloc(1, sizeof(struct JSON_value));
curr->type = json_object;
curr = rjp_calloc(1, sizeof(RJP_value));
*curr = new_val;
return curr;
}
struct JSON_value new_object = {.type = json_object, .integer = 0, .parent = curr};
if(state->in_array)
return _rjp__add_element_to_array(curr, state, &new_object);
curr->object.last->value = new_object;
return &curr->object.last->value;
}
//Assign array characteristics to previously allocated JSON_value
static struct JSON_value* _rjp__add_array(struct JSON_value* curr, struct JSON_state* state){
struct JSON_value new_array = {.type = json_array, .array = {.num_elements = 0, .elements = NULL}, .parent = curr};
if(state->in_array)
return _rjp__add_element_to_array(curr, state, &new_array);
curr->object.last->value = new_array;
return &curr->object.last->value;
}
//Assign string characteristics to previously allocated JSON_value with no string allocation
static struct JSON_value* _rjp__add_string_no_alloc(struct JSON_value* curr, char* str, int len, struct JSON_state* state){
struct JSON_value new_string = {.type = json_string, .string = {.value = str, .length = len}, .parent = curr};
if(state->in_array)
return _rjp__add_element_to_array(curr, state, &new_string);
curr->object.last->value = new_string;
return &curr->object.last->value;
}
//Assign double characteristics to previously allocated JSON_value
static struct JSON_value* _rjp__add_dfloat(struct JSON_value* curr, double value, struct JSON_state* state){
struct JSON_value new_double = {.type = json_dfloat, .dfloat = value, .parent = curr};
if(state->in_array)
return _rjp__add_element_to_array(curr, state, &new_double);
curr->object.last->value = new_double;
return &curr->object.last->value;
}
//Assign integer characteristics to previously allocated JSON_value
static struct JSON_value* _rjp__add_integer(struct JSON_value* curr, long value, struct JSON_state* state){
struct JSON_value new_integer = {.type = json_integer, .integer = value, .parent = curr};
if(state->in_array)
return _rjp__add_element_to_array(curr, state, &new_integer);
curr->object.last->value = new_integer;
return &curr->object.last->value;
}
static struct JSON_value* _rjp__add_boolean(struct JSON_value* curr, int value, struct JSON_state* state){
struct JSON_value new_boolean = {.type = json_boolean, .boolean = value, .parent = curr};
if(state->in_array)
return _rjp__add_element_to_array(curr, state, &new_boolean);
curr->object.last->value = new_boolean;
return &curr->object.last->value;
}
static struct JSON_value* _rjp__add_null(struct JSON_value* curr, struct JSON_state* state){
struct JSON_value new_null = {.type = json_null, .integer = 0, .parent = curr};
if(state->in_array)
return _rjp__add_element_to_array(curr, state, &new_null);
curr->object.last->value = new_null;
return &curr->object.last->value;
}
static void _rjp__free_object_recurse(struct JSON_value* root);
static void _rjp__free_array(struct JSON_value* root){
struct JSON_array_element* arr = root->array.elements;
for(struct JSON_array_element* i = arr;i != NULL;i = arr){
arr = arr->next;
if(i->value.type == json_object){
_rjp__free_object_recurse(&i->value);
}else if(i->value.type == json_array){
_rjp__free_array(&i->value);
}else if(i->value.type == json_string){
free(i->value.string.value);
}
free(i);
if(curr->type == json_array){
_rjp__add_element(&curr->array);
curr->array.last->value = new_val;
return &curr->array.last->value;
}
curr->object.last->value = new_val;
return &curr->object.last->value;
}
//Recursively free JSON objects
static void _rjp__free_object_recurse(struct JSON_value* root){
struct JSON_object_member* next;
for(struct JSON_object_member* m = root->object.members;m;m = next){
next = m->next;
if(m->value.type == json_object)
_rjp__free_object_recurse(&m->value);
else if(m->value.type == json_string)
free(m->value.string.value);
else if(m->value.type == json_array)
_rjp__free_array(&m->value);
if(m->name.value)
free(m->name.value);
free(m);
}
}
void rjp_free_json(char* json){
free(json);
}
//Same as recurse but also frees root node
void rjp_free(struct JSON_value* root){
if(!root)
return;
_rjp__free_object_recurse(root);
free(root);
}
MAYBE_UNUSED static int _rjp__is_array_empty(struct JSON_value* curr){
if(curr->object.members->value.type != json_array)
return 0;
return curr->object.members->value.array.num_elements == 0;
}
#define syntax_error(msg, row, column)\
do{fprintf(stderr, "Syntax error! %s (%i:%i)\n", msg, row, column);rjp_free(root);return NULL;}while(0)
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
struct JSON_value* rjp_parse(const char* str){
struct JSON_value* root = 0;
struct JSON_value* curr = 0;
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
struct JSON_state state_stack[MAX_DEPTH] = {0},*top = state_stack;
int state_stack[MAX_DEPTH] = {0},*top = state_stack;
//initially search for the root object
top->search_target = json_value;
top->in_array = 0;
*top = json_value;
for(;*str != '\0';++str){
char c = *str;
@@ -253,7 +97,7 @@ struct JSON_value* rjp_parse(const char* str){
++str;
}
else if(top->search_target == json_key){
else if(*top == json_key){
//start of key
if(c == '"'){
if(curr == NULL)
@@ -268,7 +112,7 @@ struct JSON_value* rjp_parse(const char* str){
}
_rjp__add_member_no_alloc(&curr->object, new_string, keylen);
str += keylen;
top->search_target = json_colon;
*top = json_colon;
//end of this object (object is empty)
}else if(c == '}'){
curr = curr->parent;
@@ -280,63 +124,62 @@ struct JSON_value* rjp_parse(const char* str){
syntax_error("Unexpected character, expected '\"'!", row, column);
}
}
else if(top->search_target == json_colon){
else if(*top == json_colon){
//colon after a key
if(c == ':'){
top->search_target = json_value;
*top = json_value;
//unrecognized character
}else if(!_rjp__is_whitespace(c)){
syntax_error( "Unexpected character, expected ':'!", row, column);
}
}
else if(top->search_target == json_comma){
else if(*top == json_comma){
//comma separating keys in an object or values in an array
if(c == ','){
top->search_target = top->in_array ? json_value : json_key;
*top = (curr->type == json_array ? json_value : json_key);
//end of object
}else if(c == '}'){
if(top->in_array){
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 == ']' && top->in_array){
--top->in_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->search_target == json_value){
else if(*top == json_value){
//object
if(c == '{'){
if(!root){
root = _rjp__add_object(NULL, top);
root = _rjp__add_value(NULL, rjp_object());
curr = root;
top->search_target = json_key;
*top = json_key;
}else{
curr = _rjp__add_object(curr, top);
top->search_target = json_comma;
curr = _rjp__add_value(curr, rjp_object());
*top = json_comma;
++top;
top->in_array = 0;
top->search_target = json_key;
*top = json_key;
}
}
//something outside of any object
else if(!curr){
syntax_error("Unexpected character outside of object definition!", row, column);
}
//arrays
else if(c == '['){
_rjp__add_array(curr, top);
++top->in_array;
if(!root){
root = _rjp__add_value(NULL, rjp_array());
curr = root;
}else{
curr = _rjp__add_value(curr, rjp_array());
}
}
else if(c == ']' && top->in_array){ //empty array
--top->in_array;
top->search_target = json_comma;
else if(c == ']' && curr->type == json_array){ //empty array
*top = json_comma;
curr = curr->parent;
}
//strings
else if(c == '"'){
@@ -345,17 +188,21 @@ struct JSON_value* rjp_parse(const char* str){
char* new_string = _rjp__parse_string(root, str, &vallen, &row, &column);
if(!new_string){
if(vallen == 0){
new_string = calloc(1, 1);
new_string = rjp_calloc(1, 1);
}else{
return NULL;
}
}
_rjp__add_string_no_alloc(curr, new_string, vallen, top);
_rjp__add_value(curr, rjp_string(new_string, vallen));
str += vallen;
top->search_target = json_comma;
*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);
@@ -367,39 +214,63 @@ struct JSON_value* rjp_parse(const char* str){
}
floating = 1;
}
if(*(str+numlen) == '\0'){ //hit EOF early
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){
_rjp__add_dfloat(curr, strtod(str, NULL), top);
if(!root){
root = curr = _rjp__add_value(NULL, rjp_dfloat(strtod(str, NULL)));
}else{
_rjp__add_value(curr, rjp_dfloat(strtod(str, NULL)));
}
}else{
_rjp__add_integer(curr, strtol(str, NULL, 10), top);
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;
top->search_target = json_comma;
}
//booleans and null
else if(!strncmp(str, "true", 4)){
_rjp__add_boolean(curr, 1, top);
str += 3;
top->search_target = json_comma;
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)){
_rjp__add_boolean(curr, 0, top);
str += 4;
top->search_target = json_comma;
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)){
_rjp__add_null(curr, top);
str += 3;
top->search_target = json_comma;
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);
}
}
return root;
@@ -407,22 +278,4 @@ struct JSON_value* rjp_parse(const char* str){
#undef syntax_error
//TODO
struct JSON_object_member* rjp_get_members(struct JSON_object* j){
return j->members;
}
struct JSON_object_member* rjp_next_member(struct JSON_object_member* j){
return j->next;
}
struct JSON_object* rjp_member_parent(struct JSON_object_member* j){
return &j->value.parent->object;
}
struct JSON_value* rjp_get_member_value(struct JSON_object_member* j){
return &j->value;
}
enum JSON_type rjp_get_member_type(struct JSON_object_member* j){
return j->value.type;
}
enum JSON_type rjp_get_value_type(struct JSON_value* j){
return j->type;
}

124
src/memory.c Normal file
View File

@@ -0,0 +1,124 @@
/**
rjp
Copyright (C) 2018 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
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "rjp_internal.h"
#include <stdlib.h>
#include <string.h>
void* rjp_alloc(size_t nbytes){
return malloc(nbytes);
}
void* rjp_calloc(size_t num, size_t nbytes){
return calloc(num, nbytes);
}
void* rjp_realloc(void* ptr, size_t nbytes){
return realloc(ptr, nbytes);
}
void rjp_free(void* data){
free(data);
}
static void _rjp__copy_object(RJP_value* dest, const RJP_value* src){
dest->object.members = dest->object.last = 0;
for(RJP_value* curr = rjp_get_member(src);curr;curr = rjp_next_member(curr)){
RJP_value copy_mem;
rjp_copy_value(&copy_mem, curr);
rjp_add_member(dest, rjp_member_name(curr), rjp_member_name_length(curr), copy_mem);
}
}
static void _rjp__copy_array(RJP_value* dest, const RJP_value* src){
dest->array.elements = dest->array.last = 0;
for(RJP_value* curr = rjp_get_element(src);curr;curr = rjp_next_element(curr)){
RJP_value copy_mem;
rjp_copy_value(&copy_mem, curr);
rjp_add_element(dest, copy_mem);
}
}
void rjp_copy_value(RJP_value* dest, const RJP_value* src){
dest->type = src->type;
switch(src->type){
case json_object:
_rjp__copy_object(dest, src);
break;
case json_array:
_rjp__copy_array(dest, src);
break;
case json_string:
_rjp__strcpy(&dest->string, &src->string);
break;
case json_integer:
dest->integer = src->integer;
break;
case json_boolean:
dest->boolean = src->boolean;
break;
case json_dfloat:
dest->dfloat = src->dfloat;
break;
default:
break;
};
}
static void _rjp__free_object_recurse(RJP_value* root);
static void _rjp__free_array(RJP_value* root){
RJP_array_element* arr = root->array.elements;
for(RJP_array_element* i = arr;i != NULL;i = arr){
arr = arr->next;
if(i->value.type == json_object){
_rjp__free_object_recurse(&i->value);
}else if(i->value.type == json_array){
_rjp__free_array(&i->value);
}else if(i->value.type == json_string){
free(i->value.string.value);
}
free(i);
}
}
//Recursively free JSON objects
static void _rjp__free_object_recurse(RJP_value* root){
RJP_object_member* next;
for(RJP_object_member* m = root->object.members;m;m = next){
next = m->next;
if(m->value.type == json_object)
_rjp__free_object_recurse(&m->value);
else if(m->value.type == json_string)
free(m->value.string.value);
else if(m->value.type == json_array)
_rjp__free_array(&m->value);
if(m->name.value)
free(m->name.value);
free(m);
}
}
//Same as recurse but also frees root node
void rjp_free_value(RJP_value* root){
if(!root)
return;
if((root->type) == json_object)
_rjp__free_object_recurse(root);
else if((root->type) == json_array)
_rjp__free_array(root);
free(root);
}

View File

@@ -1,56 +1,39 @@
/**
rjp
Copyright (C) 2018 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
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "rjp.h"
#include "rjp_internal.h"
#include "memory.h"
#include <string.h> //strlen
#include <stdlib.h> //malloc, calloc, free
#include <stdio.h> //sprintf
struct JSON_value* rjp_init_json(void){
struct JSON_value* ret = calloc(1, sizeof(struct JSON_value));
ret->type = json_object;
RJP_value* rjp_init_json(void){
RJP_value* ret = rjp_calloc(1, sizeof(RJP_value));
return ret;
}
struct JSON_value* rjp_add_member(struct JSON_value* dest, int alloc_key, char* key, size_t keylen, struct JSON_value value){
if(!keylen && alloc_key)
keylen = strlen(key);
if(alloc_key)
_rjp__add_member(&dest->object, key, keylen);
else
_rjp__add_member_no_alloc(&dest->object, key, keylen);
dest->object.last->value = value;
dest->object.last->value.parent = dest;
return &dest->object.last->value;
}
struct JSON_value* rjp_add_element(struct JSON_value* dest, struct JSON_value value){
_rjp__add_element(&dest->array);
dest->array.last->value = value;
dest->array.last->value.parent = dest;
return &dest->array.last->value;
}
struct JSON_value rjp_integer(long i){
return (struct JSON_value){.integer = i, .type = json_integer};
}
struct JSON_value rjp_boolean(char b){
return (struct JSON_value){.boolean = b, .type = json_boolean};
}
struct JSON_value rjp_dfloat(double d){
return (struct JSON_value){.dfloat = d, .type = json_dfloat};
}
struct JSON_value rjp_string(char* c, size_t len){
return (struct JSON_value){.string = {.value = c, .length = len}, .type = json_string};
}
struct JSON_value rjp_null(void){
return (struct JSON_value){.integer = 0, .type = json_null};
}
struct JSON_value rjp_object(void){
return (struct JSON_value){.object = {0}, .type = json_object};
}
struct JSON_value rjp_array(void){
return (struct JSON_value){.array = {0}, .type = json_array};
RJP_value* rjp_init_json_as(RJP_value value){
RJP_value* ret = rjp_alloc(sizeof(RJP_value));
*ret = value;
return ret;
}
static size_t _rjp__write_value(char* dest, struct JSON_value* val){
static size_t _rjp__write_value(char* dest, RJP_value* val){
size_t ret;
switch(val->type){
case json_integer:
@@ -69,14 +52,10 @@ static size_t _rjp__write_value(char* dest, struct JSON_value* val){
ret = sprintf(dest, "\"%s\"", val->string.value);
break;
case json_array:
ret = sprintf(dest, "[");
ret += rjp_dump_array(val, dest+ret);
ret += sprintf(dest+ret, "]");
ret = rjp_dump_array(val, dest);
break;
case json_object:
ret = sprintf(dest, "{");
ret += rjp_dump_object(val, dest+ret);
ret += sprintf(dest+ret, "}");
ret = rjp_dump_object(val, dest);
break;
default:
ret = 0;
@@ -85,10 +64,11 @@ static size_t _rjp__write_value(char* dest, struct JSON_value* val){
return ret;
}
size_t rjp_dump_array(struct JSON_value* arr, char* dest){
struct JSON_array* array = &arr->array;
struct JSON_array_element* element_list = array->elements;
size_t pos = 0;
size_t rjp_dump_array(RJP_value* arr, char* dest){
RJP_array* array = &arr->array;
RJP_array_element* element_list = array->elements;
size_t pos = 1;
sprintf(dest, "[");
for(;element_list;element_list = element_list->next){
pos += _rjp__write_value(dest+pos, &element_list->value);
@@ -97,13 +77,16 @@ size_t rjp_dump_array(struct JSON_value* arr, char* dest){
else
break;
}
pos += sprintf(dest+pos, "]");
return pos;
}
size_t rjp_dump_object(struct JSON_value* root, char* dest){
struct JSON_object* root_obj = &root->object;
struct JSON_object_member* member_list = root_obj->members;
size_t pos = 0;
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 pos = 1;
sprintf(dest, "{");
for(;member_list;member_list = member_list->next){
pos += sprintf(dest+pos, "\"%s\":", member_list->name.value);
pos += _rjp__write_value(dest+pos, &member_list->value);
@@ -113,18 +96,20 @@ size_t rjp_dump_object(struct JSON_value* root, char* dest){
else
break;
}
pos += sprintf(dest+pos, "}");
return pos;
}
char* rjp_to_json(struct JSON_value* root){
size_t len = _rjp__object_strlen(root);
char* tmp = malloc(len + 1);
char* rjp_to_json(RJP_value* root){
if(!root)
return NULL;
size_t len = _rjp__value_strlen(root);
if(!len)
return NULL;
char* tmp = rjp_alloc(len + 1);
tmp[len] = 0;
size_t pos = 1;
sprintf(tmp, "{");
pos += rjp_dump_object(root, tmp+pos);
sprintf(tmp+pos, "}");
_rjp__write_value(tmp, root);
return tmp;
}

231
src/rjp.c Normal file
View File

@@ -0,0 +1,231 @@
/**
rjp
Copyright (C) 2018 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
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "rjp_internal.h"
#include <string.h> //strncpy, strlen
void _rjp__add_element(RJP_array* j){
++j->num_elements;
if(!j->elements){
j->elements = rjp_calloc(1, sizeof(RJP_array_element));
j->last = j->elements;
}else{
j->last->next = rjp_calloc(1, sizeof(RJP_array_element));
j->last = j->last->next;
}
}
//create member of the object as a linked list member and assign a name with name allocation
void _rjp__add_member(RJP_object* j, const char* str, size_t len){
++j->num_members;
if(!j->members){
j->members = rjp_calloc(1, sizeof(RJP_object_member));
j->last = j->members;
}else{
j->last->next = rjp_calloc(1, sizeof(RJP_object_member));
j->last = j->last->next;
}
j->last->name.value = rjp_alloc(len + 1);
strncpy(j->last->name.value, str, len);
j->last->name.value[len] = 0;
j->last->name.length = len;
}
void _rjp__add_member_no_alloc(RJP_object* j, char* str, size_t len){
++j->num_members;
if(!j->members){
j->members = rjp_calloc(1, sizeof(RJP_object_member));
j->last = j->members;
}else{
j->last->next = rjp_calloc(1, sizeof(RJP_object_member));
j->last = j->last->next;
}
j->last->name.value = str;
j->last->name.length = len;
}
RJP_value* rjp_add_member(RJP_value* dest, const char* key, size_t keylen, RJP_value value){
if(!keylen)
keylen = strlen(key);
_rjp__add_member(&dest->object, key, keylen);
dest->object.last->value = value;
dest->object.last->value.parent = dest;
return &dest->object.last->value;
}
RJP_value* rjp_add_member_no_alloc(RJP_value* dest, char* key, size_t keylen, RJP_value value){
if(!keylen)
keylen = strlen(key);
_rjp__add_member_no_alloc(&dest->object, key, keylen);
dest->object.last->value = value;
dest->object.last->value.parent = dest;
return &dest->object.last->value;
}
RJP_value* rjp_add_element(RJP_value* dest, RJP_value value){
_rjp__add_element(&dest->array);
dest->array.last->value = value;
dest->array.last->value.parent = dest;
return &dest->array.last->value;
}
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){
return (RJP_value){.integer = i, .type = json_integer};
}
RJP_value rjp_boolean(char b){
return (RJP_value){.boolean = b, .type = json_boolean};
}
RJP_value rjp_dfloat(double d){
return (RJP_value){.dfloat = d, .type = json_dfloat};
}
RJP_value rjp_string(char* c, size_t len){
return (RJP_value){.string = {.value = c, .length = len}, .type = json_string};
}
RJP_value rjp_null(void){
return (RJP_value){.integer = 0, .type = json_null};
}
RJP_value rjp_object(void){
return (RJP_value){.object = {0}, .type = json_object};
}
RJP_value rjp_array(void){
return (RJP_value){.array = {0}, .type = json_array};
}
RJP_value* rjp_get_member(const RJP_value* object){
return &object->object.members->value;
}
RJP_value* rjp_next_member(const RJP_value* member){
//polymorphism
return (RJP_value*)(((RJP_object_member*)member)->next);
}
size_t rjp_num_members(const RJP_value* object){
return object->object.num_members;
}
char* rjp_member_name(const RJP_value* member){
return ((RJP_object_member*)member)->name.value;
}
size_t rjp_member_name_length(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_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 NULL;
}
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*));
for(size_t i = 0;i < num;++i){
tmp[i] = searches[i];
dest[i] = NULL;
}
RJP_value* member = rjp_get_member(object);
for(size_t i = 0;i < skip && member;member = rjp_next_member(member));
size_t matches = 0;
for(;member;member = rjp_next_member(member)){
for(size_t i = 0;i < num;++i){
if(searches[i] == NULL)
continue;
if(!strcmp(((RJP_object_member*)member)->name.value, searches[i])){
dest[i] = member;
searches[i] = NULL;
++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*));
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));
for(;member;member = rjp_next_member(member)){
for(size_t i = 0;i < num;++i){
if(!strcmp(((RJP_object_member*)member)->name.value, searches[i])){
ret[j++] = member;
if(j == ret_size){
ret_size *= 2;
ret = rjp_realloc(ret, ret_size*sizeof(RJP_value*));
}
break;
}
}
}
if(rsize)
*rsize = j;
if(j == 0){
rjp_free(ret);
return NULL;
}
return ret;
}
RJP_value* rjp_get_element(const RJP_value* array){
return &array->array.elements->value;
}
RJP_value* rjp_next_element(const RJP_value* element){
return (RJP_value*)(((RJP_array_element*)element)->next);
}
size_t rjp_num_elements(const RJP_value* array){
return array->array.num_elements;
}
RJP_value* rjp_value_parent(RJP_value* value){
return value->parent;
}
RJP_type rjp_value_type(RJP_value* value){
return value->type;
}
double rjp_value_dfloat(RJP_value* value){
return value->dfloat;
}
int rjp_value_integer(RJP_value* value){
return value->integer;
}
char rjp_value_boolean(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){
return value->string.length;
}

View File

@@ -1,32 +1,184 @@
#include "rjp.h"
/**
rjp
Copyright (C) 2018 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
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "strings.h"
#include "rjp_internal.h"
#include <stdio.h> //fprintf
#include <stdlib.h> //malloc, free
#include <stdint.h> //uintN_t
#include <string.h> //strcpy
//Determine if the character is valid whitespace
int _rjp__is_whitespace(char c){
return c == ' ' || c == '\n' || c == '\r' || c == '\t';
}
static uint32_t utf_strtol_4(const char* c){
uint32_t ret = 0;
for(size_t i = 0;i < 4;++i){
if(c[i] >= '0' && c[i] <= '9'){
ret |= ((c[i] ^ 0x30) << (4*(3-i)));
}else if(c[i] >= 'A' && c[i] <= 'F'){
ret |= ((c[i] - 0x37) << (4*(3-i)));
}else if(c[i] >= 'a' && c[i] <= 'f'){
ret |= ((c[i] - 0x57) << (4*(3-i)));
}else{
return 0;
}
}
return ret;
}
static int decode_unicode_escape(const char* str, uint32_t* high, uint32_t* low){
if(*str != '\\' || *(str+1) != 'u'){ //invalid
return *low = *high = 0;
}
*high = utf_strtol_4(str+2);
if(!*high)
return *low = *high = 0;
if((*high & 0xF800) == 0xD800){ //utf-16
if(*(str+6) != '\\' || *(str+7) != 'u'){
return *low = *high = 0;
}
*low = utf_strtol_4(str+8);
return 12;
}else{
*low = 0;
}
return 6;
}
static uint32_t u16_surrogate_pair_to_codepoint(uint32_t high, uint32_t low){
uint32_t codepoint;
codepoint = ((high & 0x07FF) << 10) | (1 << 16);
codepoint = codepoint | (low & 0x03FF);
return codepoint;
}
static uint32_t utf_to_codepoint(uint32_t high, uint32_t low){
if(!low) //utf8
return high;
return u16_surrogate_pair_to_codepoint(high, low);
}
static int codepoint_strlen(uint32_t codepoint){
if(codepoint <= 0x007F){
return 1;
}else if(codepoint <= 0x07FF){
return 2;
}else if(codepoint <= 0xFFFF){
return 3;
}else if(codepoint <= 0x10FFFF){
return 4;
}else{
return 0;
}
}
static int codepoint_to_u8(char* dest, uint32_t codepoint){
if(codepoint <= 0x007F){
dest[0] = codepoint;
return 1;
}else if(codepoint <= 0x07FF){
dest[0] = (codepoint >> 6) | 0xC0;
dest[1] = (codepoint & 0x3F) | 0x80;
return 2;
}else if(codepoint <= 0xFFFF){
dest[0] = (codepoint >> 12) | 0xE0;
dest[1] = ((codepoint >> 6) & 0x3F) | 0x80;
dest[2] = (codepoint & 0x3F) | 0x80;
return 3;
}else if(codepoint <= 0x10FFFF){
dest[0] = (codepoint >> 18) | 0xF0;
dest[1] = ((codepoint >> 12) & 0x3F) | 0x80;
dest[2] = ((codepoint >> 6) & 0x3F) | 0x80;
dest[3] = (codepoint & 0x3F) | 0x80;
return 4;
}else{
return 0;
}
}
static uint32_t u8_to_codepoint(char* u){
if((u[0] & 0x80) == 0){
//one byte
return u[0];
}else if((u[0] & 0xE0) == 0xC0){
//two byte
uint32_t codepoint;
codepoint = (u[0] & 0x1F) << 6;
codepoint |= (u[1] & 0x3F);
return codepoint;
}else if((u[0] & 0xF0) == 0xE0){
//three byte
uint32_t codepoint;
codepoint = (u[0] & 0x0F) << 12;
codepoint |= (u[1] & 0x3F) << 6;
codepoint |= (u[2] & 0x3F);
return codepoint;
}else if((u[0] & 0xF8) == 0xF0){
//four byte
uint32_t codepoint;
codepoint = (u[0] & 0x07) << 18;
codepoint |= (u[1] & 0x3F) << 12;
codepoint |= (u[2] & 0x3F) << 6;
codepoint |= (u[3] & 0x3F);
return codepoint;
}else{
//invalid
return 0;
}
}
//Convert escape sequences in strings
char* _rjp__parse_string(struct JSON_value* root, const char* str, int* len, int* row, int* column){
char* _rjp__parse_string(RJP_value* root, const char* str, int* len, int* row, int* column){
char* new_string;
++(*column); //account for starting quotation mark
for(*len = 0;*(str+*len) != '"';++(*len), ++(*column)){
if(*(str+*len) == '\\'){
++(*len);
++(*column);
}else if(*(str+*len) == '\0'){
*len = 1;
int oldpos = 0;
int newpos = 0;
for(;*(str+oldpos) != '"';++oldpos, ++(newpos), ++(*column)){
if(*(str+oldpos) == '\\'){
if(*(str+oldpos+1) == 'u'){
uint32_t high, low;
oldpos += (decode_unicode_escape(str+oldpos, &high, &low)-1);
newpos += (codepoint_strlen(utf_to_codepoint(high, low))-1);
}else{
++oldpos;
++(*column);
}
}else if(*(str+oldpos) == '\0'){
newpos = 1;
fprintf(stderr, "Syntax error! %s (%i:%i)\n", "Unexpected EOF in string!", *row, *column);
rjp_free(root);
rjp_free_value(root);
return NULL;
}else if(*(str+*len) == '\n'){
}else if(*(str+oldpos) == '\n'){
++(*row);
*column = 0;
}
}
if(*len == 0){
*len = oldpos;
if(newpos == 0){
return NULL;
}
new_string = malloc(*len + 1);
new_string[*len] = 0;
new_string = rjp_alloc(newpos + 1);
new_string[newpos] = 0;
for(int i = 0;*str != '"';++i,++str){
if(*str == '\\'){
++str;
@@ -52,6 +204,18 @@ char* _rjp__parse_string(struct JSON_value* root, const char* str, int* len, int
case 'f':
new_string[i] = '\f';
break;
case 'u':;
uint32_t high, low;
uint32_t codepoint;
--str;
str += (decode_unicode_escape(str, &high, &low) - 1);
if(!high){
rjp_free(new_string);
return NULL;
}
codepoint = utf_to_codepoint(high, low);
i += (codepoint_to_u8(new_string+i, codepoint)-1);
break;
default:
new_string[i] = *str;
break;
@@ -100,8 +264,16 @@ size_t rjp_escape_strcpy(char* dest, const char* src){
break;
};
}
dest[j] = 0;
return j;
}
void _rjp__strcpy(RJP_string* dest, const RJP_string* src){
dest->value = rjp_alloc(src->length + 1);
strcpy(dest->value, src->value);
dest->value[src->length] = 0;
dest->length = src->length;
}
size_t rjp_escape_strlen(const char* str){
size_t count = 0;
for(size_t i = 0;str[i];++i){
@@ -121,10 +293,10 @@ size_t rjp_escape_strlen(const char* str){
}
return count;
}
size_t _rjp__array_strlen(struct JSON_value* arr){
size_t _rjp__array_strlen(RJP_value* arr){
size_t count = 2; //[]
struct JSON_array* array = &arr->array;
struct JSON_array_element* element_list = array->elements;
RJP_array* array = &arr->array;
RJP_array_element* element_list = array->elements;
for(;element_list;element_list = element_list->next){
switch(element_list->value.type){
case json_integer:
@@ -157,35 +329,15 @@ size_t _rjp__array_strlen(struct JSON_value* arr){
return count;
}
size_t _rjp__object_strlen(struct JSON_value* root){
size_t _rjp__object_strlen(RJP_value* root){
size_t count = 2; //{}
struct JSON_object* root_obj = &root->object;
struct JSON_object_member* member_list = root_obj->members;
RJP_object* root_obj = &root->object;
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;
count += member_list->name.length + 3; //"":
switch(member_list->value.type){
case json_integer:
count += snprintf(NULL, 0, "%li", member_list->value.integer);
break;
case json_dfloat:
count += snprintf(NULL, 0, "%lf", member_list->value.dfloat);
break;
case json_boolean:
count += member_list->value.boolean ? 4 : 5; //true, false
break;
case json_null:
count += 4; //null
break;
case json_string:
count += member_list->value.string.length;
break;
case json_array:
count += _rjp__array_strlen(&member_list->value);
break;
case json_object:
count += _rjp__object_strlen(&member_list->value);
break;
};
count += _rjp__value_strlen(&member_list->value);
if(member_list->next)
++count; //,
else
@@ -194,3 +346,23 @@ size_t _rjp__object_strlen(struct JSON_value* root){
return count;
}
size_t _rjp__value_strlen(RJP_value* root){
switch(root->type){
case json_integer:
return snprintf(NULL, 0, "%li", root->integer);
case json_dfloat:
return snprintf(NULL, 0, "%lf", root->dfloat);
case json_boolean:
return root->boolean ? 4 : 5; //true, false
case json_null:
return 4;
case json_string:
return rjp_escape_strlen(root->string.value) + 2; //"";
case json_array:
return _rjp__array_strlen(root);
case json_object:
return _rjp__object_strlen(root);
default:
return 0;
};
}