64 Commits
v0.3 ... v0.8.2

Author SHA1 Message Date
rexy712
e15c54b7cc Revision bump 2020-02-24 18:38:35 -08:00
rexy712
092a6497f8 Add memory zeroing during object/array mutation 2020-02-24 18:31:09 -08:00
rexy712
88cdc61357 Actually implement key mutability 2020-02-24 18:30:38 -08:00
rexy712
4be1d9b2d0 Update version 2020-02-24 14:49:25 -08:00
rexy712
250c6b1e59 Fix c++ compatability and const correctness 2020-02-24 14:46:47 -08:00
rexy712
08b05e8162 Update rjp version. Remove debug source file 2020-02-23 10:49:16 -08:00
rexy712
dac026802f Update CmakeLists.txt 2020-02-23 10:15:16 -08:00
rexy712
656992233e Updated copyright year 2020-02-23 10:03:59 -08:00
rexy712
6a9597c274 Reorganized file structure 2020-02-23 09:59:37 -08:00
rexy712
debd8cc31d Move unused utility function into debug only section 2020-02-19 06:16:09 -08:00
rexy712
67208e38e0 Removed vinit, was a really dumb idea. Redid a lot of the API to match. Fixed some tree related memory leaks. 2020-02-18 17:36:54 -08:00
rexy712
0dfbbb5a5c Fix object member removal 2020-01-28 16:53:02 -08:00
rexy712
ff241e2a6f Successfully hid RJP_value implementation from API 2020-01-19 09:19:20 -08:00
rexy712
30cc8008af Separate object operations from tree operations 2020-01-19 08:12:34 -08:00
rexy712
34c918615f Update todo 2020-01-17 23:41:41 -08:00
rexy712
b447a745a3 Fix missing parent assignment in rjp_add_member 2020-01-17 23:41:26 -08:00
rexy712
45ed17d6ca Tree setup now working on all compilation units 2020-01-17 14:22:15 -08:00
rexy712
0b4562a95a Update rjp.h types 2020-01-17 13:02:53 -08:00
rexy712
b5fdb94fd3 Fix RJP_object_iterator to actually usable state 2020-01-17 12:48:26 -08:00
rexy712
ed5179c677 Fix tree related segfaults. Add member removal to API 2020-01-13 18:38:19 -08:00
rexy712
a646d1c4e8 Moved rjp.c, memory.c, and strings.c over to new tree-based objects. Unfortunately, the API did need modified because C lacks the 'mutable' keyword. 2020-01-13 15:19:27 -08:00
rexy712
6e53fe29e2 Fix memory management of trees 2020-01-13 13:22:49 -08:00
rexy712
05250e6af9 Merge branch 'master' into rbtree 2020-01-12 18:06:46 -08:00
rexy712
faccd2f31a Add naming convention documentation 2020-01-12 18:06:17 -08:00
rexy712
bc755098b3 Add tree iterator (untested) and tree copying 2020-01-12 18:05:24 -08:00
rexy712
ce1877f52a Basic working red black tree 2020-01-12 00:18:45 -08:00
rexy712
12388ac8f6 Fix handling line comments. Still need to convert to system that actually reads one character at a time... 2019-11-09 21:15:33 -08:00
rexy712
4cfd07df38 Add generic deprecated macro 2019-10-30 02:56:36 -07:00
rexy712
be71b9edb4 Merge branch 'master' of ssh://rexy712-server:1995/var/git/repos/rexy712/rjp 2019-10-29 13:52:10 -07:00
rexy712
0b50b3b21a Fix naming issue with RJP_type 2019-10-29 13:51:58 -07:00
rexy712
688dd22c73 Add pretty printing 2019-10-29 13:34:42 -07:00
rexy712
c781550b09 Fixed implicit functions 2019-09-14 06:54:52 -07:00
rexy712
0df7efabb7 Fixed standard noncompliant function naming convention 2019-08-25 07:49:56 -07:00
rexy712
3349468e07 Fix const correctness in API. Version Bump to 0.7.0. 2019-08-20 11:58:11 -07:00
rexy712
339c3af4f4 Fix issue with setting null key (used for stealing data) 2019-07-29 15:09:56 -07:00
rexy712
25c3c018f6 Const correction 2019-07-29 14:58:00 -07:00
rexy712
f367993ad4 Finally add key modifier functions. 2019-07-29 14:48:13 -07:00
rexy712
2d94783ee7 Fix remaining integer inconsistencies 2019-07-27 11:38:59 -07:00
rexy712
f9a36a5c37 Fix inconsistent integer types for rjp_value_int 2019-07-27 11:31:29 -07:00
Rexy712
d3c99152fc Added string copy constructor 2019-06-26 18:07:19 -07:00
Rexy712
dc0c003785 Working on enabling chunked reading 2019-06-10 17:51:45 -07:00
rexy712
4577836f8e much better rjp_search_members 2019-04-06 07:08:46 -07:00
rexy712
50c7a055ec Updated version 2019-03-30 05:22:11 -07:00
rexy712
6771d00c6e Updated copyright notice 2019-03-30 05:20:53 -07:00
rexy712
71661cd9ad Made rjp_search_members take a 'const char* const*' for search terms 2019-03-30 04:37:06 -07:00
rexy712
46b7b56852 Fix rjp_string containing incorrect length 2019-02-21 10:49:21 -08:00
rexy712
fd7d9988b8 Fixed search return value being in an inconsistent state 2019-02-13 15:12:14 -08:00
rexy712
280e24a8e7 Rethought searching after trying to use it. Should be more useful now with the added return information 2019-02-08 15:51:11 -08:00
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
23 changed files with 2434 additions and 798 deletions

3
.gitignore vendored
View File

@@ -5,3 +5,6 @@ tester.exe
*.swp
build
include/config.h
makefile
Makefile
src/tester.c

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 8)
set(rjp_VERSION_REVISION 2)
set(INCLUDE_PATH ${CMAKE_SOURCE_DIR}/include)
configure_file(
"${INCLUDE_PATH}/config.h.in"
@@ -13,15 +14,25 @@ configure_file(
include_directories("${INCLUDE_PATH}")
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/rjp_array.c" "src/rjp.c" "src/rjp_object.c" "src/rjp_string.c" "src/tree.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()
add_library (rjp STATIC src/input.c src/output.c src/strings.c)
set_target_properties(rjp PROPERTIES PUBLIC_HEADER ${INCLUDE_PATH}/rjp.h)
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
```

6
TODO Normal file
View File

@@ -0,0 +1,6 @@
Change string handling to work with chunked reading
Change numeral handling to work with chunked reading
handle scientific notation
modularize internal object management. eg tree.h should only be needed in object.c and tree.c

View File

@@ -0,0 +1,9 @@
rjp_init_* = initialize passed pointer
rjp_delete_* = cleanup internal structures
rjp_new_* = rjp_malloc new value and initialize it
rjp_free_* = cleanup internal structures and rjp_free passed pointer
irjp_* = internal function
rjp_* = api function
RJP_* = data type name/typedef

View File

@@ -1,6 +1,6 @@
/**
rjp
Copyright (C) 2018 rexy712
Copyright (C) 2018-2020 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,11 +16,12 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CONFIG_H
#define CONFIG_H
#ifndef RJP_CONFIG_H
#define RJP_CONFIG_H
#define RJP_VERSION_MAJOR @rjp_VERSION_MAJOR@
#define RJP_VERSION_MINOR @rjp_VERSION_MINOR@
#define RJP_VERSION_REVISION @rjp_VERSION_REVISION@
#endif

View File

@@ -1,6 +1,6 @@
/**
rjp
Copyright (C) 2018 rexy712
Copyright (C) 2018-2020 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
@@ -19,128 +19,182 @@
#ifndef RJP_H
#define RJP_H
#include <stddef.h> //size_t
#ifdef __cplusplus
extern "C"{
#endif
//type of data
typedef enum RJP_type{
json_object,
json_string,
json_integer,
json_dfloat,
json_boolean,
json_array,
json_null
}RJP_type;
#if defined(__GNUC__) || defined(__clang__)
#define DEPRECATED(str) __attribute__((deprecated(str)))
#else
#define DEPRECATED(str)
#endif
//keep track of string length
#ifndef RJP_int
#include <stdint.h>
#define RJP_int int_fast64_t
#endif
#ifndef RJP_index
#include <stddef.h>
#define RJP_index size_t
#endif
#ifndef RJP_float
#include <stdint.h>
#define RJP_float double
#endif
#ifndef RJP_bool
#define RJP_bool char
#endif
//used with rjp_to_json
#define RJP_FORMAT_NONE 0
#define RJP_FORMAT_PRETTY 1
//type of data
typedef enum RJP_data_type{
rjp_json_object = 0,
rjp_json_string,
rjp_json_integer,
rjp_json_dfloat,
rjp_json_boolean,
rjp_json_array,
rjp_json_null
}RJP_data_type;
typedef struct RJP_value RJP_value;
//Hold a C string and the length excluding NULL terminator
typedef struct RJP_string{
char* value;
size_t length;
RJP_index length;
}RJP_string;
//keep a linked list of all members of a given object
struct RJP_object_member;
typedef struct RJP_object{
struct RJP_object_member* members;
struct RJP_object_member* last;
size_t num_members;
}RJP_object;
//Forward declarations
struct RJP_object_iterator_impl;
typedef struct RJP_object_iterator{
struct RJP_object_iterator_impl* it;
}RJP_object_iterator;
//keep array plus length
struct RJP_array_element;
typedef struct RJP_array{
struct RJP_array_element* elements;
struct RJP_array_element* last;
size_t num_elements;
}RJP_array;
typedef struct RJP_array_iterator{
RJP_value* current;
}RJP_array_iterator;
//hold any json data type
typedef struct RJP_value{
union{
long integer;
double dfloat;
char boolean;
struct RJP_object object;
struct RJP_string string;
struct RJP_array array;
};
struct RJP_value* parent;
enum RJP_type type;
}RJP_value;
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;
//read in json and convert to easy to work with format
RJP_value* rjp_parse(const char* str);
//deallocate a json handle
void rjp_free_json(char* json);
void rjp_free(RJP_value* root);
//initialize an empty json handle (outputting to string would result in '{}')
RJP_value* rjp_init_json(void);
//add a member to a json object
RJP_value* rjp_add_member(RJP_value* dest, int alloc_key, char* key, size_t keylen, RJP_value value);
//add an element to a json array
RJP_value* rjp_add_element(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 members/elements
RJP_value* rjp_get_member(RJP_value* object);
RJP_value* rjp_next_member(RJP_value* member);
RJP_value* rjp_get_element(RJP_value* array);
RJP_value* rjp_next_element(RJP_value* element);
//member metadata
char* rjp_member_name(RJP_value* member);
size_t rjp_member_name_length(RJP_value* member);
//value metadata
RJP_value* rjp_value_parent(RJP_value* element);
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);
/***************** NON OBJECT OPERATIONS *******************/
//Allocate heap space for rjp to use. Use if the memory is to be freed by rjp
void* rjp_alloc(RJP_index nbytes);
//Allocate heap space for rjp and initialize to 0.
void* rjp_calloc(RJP_index num, RJP_index nbytes);
//Resize heap allocated space for rjp
void* rjp_realloc(void* ptr, RJP_index nbytes);
//Free memory allocated by rjp_alloc or rjp_calloc
void rjp_free(void* dest);
//copy input string and add json escape sequences
size_t rjp_escape_strcpy(char* dest, const char* src);
RJP_index rjp_escape_strcpy(char* dest, const char* src);
//length of string including escape sequences
size_t rjp_escape_strlen(const char* str);
RJP_index rjp_escape_strlen(const char* str);
/***************** GENERIC OPERATIONS *******************/
//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);
char* rjp_to_json(const RJP_value* root, int pretty);
//Initialize a root RJP_value to copy of value
RJP_value* rjp_new_null(void);
RJP_value* rjp_new_int(RJP_int val);
RJP_value* rjp_new_float(RJP_float val);
RJP_value* rjp_new_bool(RJP_bool val);
RJP_value* rjp_new_string_copy(const char* val, RJP_index length);
RJP_value* rjp_new_string(char* val, RJP_index length);
RJP_value* rjp_new_object(void);
RJP_value* rjp_new_array(void);
//Deallocate RJP_value
void rjp_free_value(RJP_value* root);
//Deep copy RJP_value
//When dest is not null, cleanup and then fill the object pointed to by dest. Otherwise allocate a new value.
RJP_value* rjp_copy_value(RJP_value* dest, const RJP_value* src);
RJP_value* rjp_move_value(RJP_value* dest, RJP_value* src);
//set existing value
//initialize a RJP_value to the requested type and value
RJP_value* rjp_set_null(RJP_value* v);
RJP_value* rjp_set_int(RJP_value* v, RJP_int val);
RJP_value* rjp_set_float(RJP_value* v, RJP_float val);
RJP_value* rjp_set_bool(RJP_value* v, RJP_bool val);
RJP_value* rjp_set_string_copy(RJP_value* v, const char* val, RJP_index length);
RJP_value* rjp_set_string(RJP_value* v, char* val, RJP_index length);
RJP_value* rjp_set_object(RJP_value* v);
RJP_value* rjp_set_array(RJP_value* v);
RJP_float rjp_get_float(const RJP_value* value);
RJP_int rjp_get_int(const RJP_value* value);
RJP_bool rjp_get_bool(const RJP_value* value);
RJP_string* rjp_get_string(RJP_value* value);
const RJP_string* rjp_get_cstring(const RJP_value* value);
/***************** OBJECT/ARRAY SHARED OPERATIONS *******************/
RJP_value* rjp_value_parent(const RJP_value* element);
//Return type of value
RJP_data_type rjp_value_type(const RJP_value* value);
/***************** OBJECT OPERATIONS *******************/
//add a member to a json object, allocating a copy of the key
RJP_value* rjp_add_member_key_copy(RJP_value* dest, const char* key, RJP_index keylen);
//add a member to a json object without allocation. key must be allocated using rjp_alloc/rjp_calloc
RJP_value* rjp_add_member(RJP_value* dest, char* key, RJP_index keylen);
//Remove member without freeing
RJP_value* rjp_remove_member_by_key(RJP_value* obj, const char* key);
RJP_value* rjp_remove_member(RJP_value* obj, RJP_value* member);
//Remove and free member
void rjp_free_member_by_key(RJP_value* obj, const char* key);
void rjp_free_member(RJP_value* obj, RJP_value* member);
//set existing object member's key without allocation. key must be allocated using rjp_alloc/rjp_calloc
void rjp_set_key(RJP_value* dest, char* key, RJP_index keylen);
//set existing object member's key
void rjp_set_key_copy(RJP_value* dest, const char* key, RJP_index keylen);
//Return number of members in the object
RJP_index rjp_num_members(const RJP_value* object);
//Return the object member's key
const RJP_string* rjp_member_key(const RJP_value* member);
//Search for an object member with given key
RJP_value* rjp_search_member(const RJP_value* object, const char* search);
/***************** OBJECT ITERATOR OPERATIONS *******************/
//Access first member of a json object
void rjp_init_object_iterator(RJP_object_iterator* iter, const RJP_value* object);
void rjp_delete_object_iterator(RJP_object_iterator* it);
RJP_value* rjp_object_iterator_current(const RJP_object_iterator* it);
RJP_value* rjp_object_iterator_next(RJP_object_iterator* it);
RJP_value* rjp_object_iterator_peek(const RJP_object_iterator* it);
/***************** ARRAY OPERATIONS *******************/
//add an element to a json array
RJP_value* rjp_add_element(RJP_value* dest);
RJP_value* rjp_remove_element(RJP_value* arr, RJP_value* elem);
void rjp_free_element(RJP_value* arr, RJP_value* elem);
RJP_index rjp_num_elements(const RJP_value* arr);
/***************** ARRAY ITERATOR OPERATIONS *******************/
void rjp_init_array_iterator(RJP_array_iterator* iter, const RJP_value* array);
void rjp_delete_array_iterator(RJP_array_iterator* iter);
RJP_value* rjp_array_iterator_current(const RJP_array_iterator* it);
RJP_value* rjp_array_iterator_next(RJP_array_iterator* it);
RJP_value* rjp_array_iterator_peek(const RJP_array_iterator* it);
size_t rjp_dump_object(RJP_value* root, char* dest);
size_t rjp_dump_array(RJP_value* arr, char* dest);
char* rjp_to_json(RJP_value* root);
#ifdef __cplusplus
}
#endif
#undef DEPRECATED
#endif

39
include/rjp_array.h Normal file
View File

@@ -0,0 +1,39 @@
/**
rjp
Copyright (C) 2018-2020 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_ARRAY_H
#define RJP_ARRAY_H
#include "rjp.h"
struct RJP_array_element;
//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
RJP_index num_elements;
}RJP_array;
void irjp_add_element(RJP_array* j);
void irjp_delete_value(RJP_value* root);
void irjp_copy_array(RJP_value* dest, const RJP_value* src);
RJP_index rjp_dump_array(const RJP_value* arr, char* dest);
#endif

View File

@@ -0,0 +1,31 @@
/**
rjp
Copyright (C) 2018-2020 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_ARRAY_ELEMENT_H
#define RJP_ARRAY_ELEMENT_H
#include "rjp_internal.h"
#include "rjp_value.h"
typedef struct RJP_array_element{
struct RJP_value value;
struct RJP_array_element* next;
struct RJP_array_element* prev;
}RJP_array_element;
#endif

View File

@@ -1,13 +1,55 @@
/**
rjp
Copyright (C) 2018-2020 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"
#include <stdio.h>
#ifdef __GNUC__
#define MAYBE_UNUSED __attribute__((unused))
#else
#define MAYBE_UNUSED
#endif
#define UNUSED_VARIABLE(thing) (void)(thing)
#ifdef RJP_DIAGNOSTICS
#define DIAG_PRINT(...) fprintf(__VA_ARGS__)
#else
#define DIAG_PRINT(...) irjp_ignore_unused(__VA_ARGS__)
static inline void irjp_ignore_unused(FILE* fp, ...){
UNUSED_VARIABLE(fp);
}
#endif
RJP_value irjp_integer(RJP_int i);
RJP_value irjp_boolean(RJP_bool b);
RJP_value irjp_dfloat(RJP_float d);
RJP_value irjp_string(char* c, RJP_index len);
RJP_value irjp_string_copy(const char* c);
RJP_value irjp_null(void);
RJP_value irjp_object(void);
RJP_value irjp_array(void);
char* _rjp__parse_string(RJP_value* root, const char* str, int* len, int* row, int* column);
size_t _rjp__object_strlen(RJP_value* root);
void _rjp__add_member(RJP_object* j, char* str, size_t len);
void _rjp__add_member_no_alloc(RJP_object* j, char* str, size_t len);
void _rjp__add_element(RJP_array* j);
size_t _rjp__object_strlen(RJP_value* root);
#endif

36
include/rjp_object.h Normal file
View File

@@ -0,0 +1,36 @@
/**
rjp
Copyright (C) 2018-2020 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_OBJECT_H
#define RJP_OBJECT_H
#include "rjp_internal.h"
struct RJP_tree_node;
typedef struct RJP_object{
struct RJP_tree_node* root;
RJP_index num_members;
}RJP_object;
void irjp_copy_object(RJP_value* dest, const RJP_value* src);
void irjp_delete_object(RJP_value* obj);
RJP_index rjp_dump_object(const RJP_value* root, char* dest);
#endif

View File

@@ -0,0 +1,30 @@
/**
rjp
Copyright (C) 2018-2020 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_OBJECT_MEMBER_H
#define RJP_OBJECT_MEMBER_H
#include "rjp_internal.h"
#include "rjp_value.h"
typedef struct RJP_object_member{
RJP_value value;
RJP_string name;
}RJP_object_member;
#endif

32
include/rjp_string.h Normal file
View File

@@ -0,0 +1,32 @@
/**
rjp
Copyright (C) 2018-2020 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_STRINGS_H
#define RJP_STRINGS_H
#include "rjp.h"
int irjp_is_whitespace(char c);
char* irjp_parse_string(RJP_value* root, const char* str, int* inclen, int* len, int* row, int* column);
RJP_index irjp_array_strlen(const RJP_value* arr);
RJP_index irjp_object_strlen(const RJP_value* root);
RJP_index irjp_value_strlen(const RJP_value* root);
RJP_index irjp_value_strlen_pretty(const RJP_value* root, int depth);
void irjp_strcpy(RJP_string* dest, const RJP_string* src);
#endif

42
include/rjp_value.h Normal file
View File

@@ -0,0 +1,42 @@
/**
rjp
Copyright (C) 2018-2020 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_VALUE_H
#define RJP_VALUE_H
#include "rjp_internal.h"
#include "rjp_object.h"
#include "rjp_array.h"
//Represents json data
//hold any json data typetypetypetype
typedef struct RJP_value{
union{
RJP_int integer;
RJP_float dfloat;
RJP_bool boolean;
struct RJP_object object;
struct RJP_string string;
struct RJP_array array;
};
struct RJP_value* parent; //pointer to parent (either an array or object or NULL)
enum RJP_data_type type; //flag to determine active member of union
}RJP_value;
#endif

51
include/tree.h Normal file
View File

@@ -0,0 +1,51 @@
/**
rjp
Copyright (C) 2018-2020 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_TREE_H
#define RJP_TREE_H
#include "rjp.h"
#include "rjp_object.h"
#include "rjp_object_member.h"
#define RJP_TREE_SUCCESS 0
#define RJP_TREE_ERR_NULL_ROOT 1
#define RJP_TREE_ERR_NOT_FOUND 2
typedef struct RJP_object_iterator RJP_object_iterator;
typedef struct RJP_tree_node RJP_tree_node;
struct RJP_tree_node{
RJP_object_member data;
RJP_tree_node* parent;
RJP_tree_node* left;
RJP_tree_node* right;
unsigned color:1;
};
RJP_tree_node* irjp_new_node(char* key, RJP_index keylen);
RJP_tree_node* irjp_tree_insert_value(RJP_tree_node* root, char* key, RJP_index keylen, RJP_value** added, int* status);
RJP_tree_node* irjp_tree_insert_node(RJP_tree_node *restrict root, RJP_tree_node *restrict newnode);
RJP_tree_node* irjp_tree_remove_value(RJP_tree_node* restrict root, RJP_tree_node* restrict member, RJP_tree_node** removed_node);
RJP_tree_node* irjp_tree_search_value(RJP_tree_node* root, const char* key);
RJP_tree_node* irjp_copy_tree(const RJP_tree_node* root);
void irjp_free_tree(RJP_tree_node* root);
void irjp_dbg_print_tree(RJP_tree_node* root);
void irjp_dbg_print_tree_bfs(RJP_tree_node* root);
#endif

View File

@@ -1,6 +1,6 @@
/**
rjp
Copyright (C) 2018 rexy712
Copyright (C) 2018-2020 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
@@ -17,400 +17,362 @@
*/
//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 "rjp_value.h"
#include "rjp_string.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
#ifdef RJP_DIAGNOSTICS
#define DIAG_PRINT(...) fprintf(__VA_ARGS__)
#else
#define DIAG_PRINT(...)
#endif
typedef struct RJP_state{
int in_array;
int search_target;
}RJP_state;
#include <string.h> //memset
//types of searches in the text
typedef enum json_search_target{
json_key,
json_colon,
json_comma,
json_value
typedef enum rjp_json_search_target{
rjp_json_target_key,
rjp_json_target_colon,
rjp_json_target_comma,
rjp_json_target_value,
rjp_json_target_string,
rjp_json_target_numeral,
rjp_json_target_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(RJP_array* j){
++j->num_elements;
if(!j->elements){
j->elements = calloc(1, sizeof(RJP_array_element));
j->last = j->elements;
}else{
j->last->next = 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, char* str, size_t len){
++j->num_members;
if(!j->members){
j->members = calloc(1, sizeof(RJP_object_member));
j->last = j->members;
}else{
j->last->next = calloc(1, sizeof(RJP_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(RJP_object* j, char* str, size_t len){
++j->num_members;
if(!j->members){
j->members = calloc(1, sizeof(RJP_object_member));
j->last = j->members;
}else{
j->last->next = calloc(1, sizeof(RJP_object_member));
j->last = j->last->next;
}
j->last->name.value = str;
j->last->name.length = len;
}
static RJP_value* _rjp__add_element_to_array(RJP_value* curr, RJP_state* state, RJP_value* element){
RJP_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 RJP_value
static RJP_value* _rjp__add_object(RJP_value* curr, RJP_state* state){
static RJP_value* irjp_add_value(RJP_value* curr, RJP_value* lastadded, RJP_value new_val){
new_val.parent = curr;
if(!curr){
curr = calloc(1, sizeof(RJP_value));
curr->type = json_object;
curr = rjp_calloc(1, sizeof(RJP_value));
*curr = new_val;
return curr;
}
RJP_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 RJP_value
static RJP_value* _rjp__add_array(RJP_value* curr, RJP_state* state){
RJP_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 RJP_value with no string allocation
static RJP_value* _rjp__add_string_no_alloc(RJP_value* curr, char* str, int len, RJP_state* state){
RJP_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 RJP_value
static RJP_value* _rjp__add_dfloat(RJP_value* curr, double value, RJP_state* state){
RJP_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 RJP_value
static RJP_value* _rjp__add_integer(RJP_value* curr, long value, RJP_state* state){
RJP_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 RJP_value* _rjp__add_boolean(RJP_value* curr, int value, RJP_state* state){
RJP_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 RJP_value* _rjp__add_null(RJP_value* curr, RJP_state* state){
RJP_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(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);
if(curr->type == rjp_json_array){
RJP_value* last = rjp_add_element(curr);
rjp_move_value(last, &new_val);
return last;
}
*lastadded = new_val;
return lastadded;
}
//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);
}
}
void rjp_free_json(char* json){
free(json);
}
//Same as recurse but also frees root node
void rjp_free(RJP_value* root){
if(!root)
return;
_rjp__free_object_recurse(root);
free(root);
}
MAYBE_UNUSED static int _rjp__is_array_empty(RJP_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{DIAG_PRINT(stderr, "Syntax error! %s (%i:%i)\n", msg, row, column);rjp_free(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
RJP_state 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;
RJP_value* lastadded;
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 irjp_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 irjp_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 irjp_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 = irjp_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;
}
state->lastadded = rjp_add_member(state->curr, new_string, keylen);
rjp_set_null(state->lastadded);
*state->target = rjp_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(!irjp_is_whitespace(c)){
syntax_error("Unexpected character, expected '\"'!", state);
return -1;
}
return 1;
}
int irjp_handle_colon(const char* str, RJP_parse_state* state){
char c = *str;
//colon after a key
if(c == ':'){
*state->target = rjp_json_target_value;
//unrecognized character
}else if(!irjp_is_whitespace(c)){
syntax_error( "Unexpected character, expected ':'!", state);
return -1;
}
return 1;
}
int irjp_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 == rjp_json_array ? rjp_json_target_value : rjp_json_target_key);
//end of object
}else if(c == '}'){
if(state->curr->type == rjp_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 == rjp_json_array){
state->curr = state->curr->parent;
//unrecognized character
}else if(!irjp_is_whitespace(c)){
syntax_error("Unexpected character, expected ','!", state);
return -1;
}
return 1;
}
int irjp_handle_value(const char* str, RJP_parse_state* state){
//object
char c = *str;
if(c == '{'){
if(!state->root){
state->root = irjp_add_value(NULL, NULL, irjp_object());
state->curr = state->root;
*state->target = rjp_json_target_key;
}else{
state->curr = irjp_add_value(state->curr, state->lastadded, irjp_object());
*state->target = rjp_json_target_comma;
++state->target;
*state->target = rjp_json_target_key;
}
return 1;
}
else if(c == '['){
if(!state->root){
state->root = irjp_add_value(NULL, NULL, irjp_array());
state->curr = state->root;
}else{
state->curr = irjp_add_value(state->curr, state->lastadded, irjp_array());
}
return 1;
}
else if(c == ']' && state->curr->type == rjp_json_array){ //empty array
*state->target = rjp_json_target_comma;
state->curr = state->curr->parent;
return 1;
}
//strings
else if(c == '"'){
int vallen, inclen;
char* new_string = irjp_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;
}
}
irjp_add_value(state->curr, state->lastadded, irjp_string(new_string, vallen));
*state->target = rjp_json_target_comma;
return inclen+2;
}
//numbers
else if((c >= '0' && c <= '9') || c == '-'){
if(!state->curr)
*state->target = rjp_json_target_none;
else
*state->target = rjp_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 = irjp_add_value(NULL, NULL, irjp_dfloat(strtod(str, NULL)));
}else{
irjp_add_value(state->curr, state->lastadded, irjp_dfloat(strtod(str, NULL)));
}
}else{
if(!state->root){
state->root = state->curr = irjp_add_value(NULL, NULL, irjp_integer(strtoll(str, NULL, 10)));
}else{
irjp_add_value(state->curr, state->lastadded, irjp_integer(strtoll(str, NULL, 10)));
}
}
state->column += numlen;
return numlen;
}
//booleans and null
else if(!strncmp(str, "true", 4)){
if(!state->curr){
*state->target = rjp_json_target_none;
state->root = state->curr = irjp_add_value(state->curr, NULL, irjp_boolean(1));
}else{
*state->target = rjp_json_target_comma;
irjp_add_value(state->curr, state->lastadded, irjp_boolean(1));
}
state->column += 3;
return 4;
}else if(!strncmp(str, "false", 5)){
if(!state->curr){
*state->target = rjp_json_target_none;
state->root = state->curr = irjp_add_value(state->curr, NULL, irjp_boolean(0));
}else{
*state->target = rjp_json_target_comma;
irjp_add_value(state->curr, state->lastadded, irjp_boolean(0));
}
state->column += 4;
return 5;
}else if(!strncmp(str, "null", 4)){
if(!state->curr){
*state->target = rjp_json_target_none;
state->root = state->curr = irjp_add_value(state->curr, NULL, irjp_null());
}else{
*state->target = rjp_json_target_comma;
irjp_add_value(state->curr, state->lastadded, irjp_null());
}
state->column += 3;
return 4;
}
//unrecognized character
else if(!irjp_is_whitespace(c)){
syntax_error("Unexpected character!", state);
return -1;
}
return 1;
}
RJP_value* rjp_parse(const char* str){
RJP_parse_state state;
irjp_init_parse_state(&state);
//initially search for the root object
top->search_target = json_value;
top->in_array = 0;
*state.target = rjp_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 = irjp_handle_comment(str, &state))){
continue;
}
else if(top->search_target == 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->search_target = 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 rjp_json_target_key:
inc = irjp_handle_key(str, &state);
break;
case rjp_json_target_colon:
inc = irjp_handle_colon(str, &state);
break;
case rjp_json_target_comma:
inc = irjp_handle_comma(str, &state);
break;
case rjp_json_target_value:
inc = irjp_handle_value(str, &state);
break;
case rjp_json_target_none:
if(!irjp_is_whitespace(*str)){
syntax_error("Unexpected character!", &state);
return NULL;
}
}
else if(top->search_target == json_colon){
//colon after a key
if(c == ':'){
top->search_target = json_value;
//unrecognized character
}else if(!_rjp__is_whitespace(c)){
syntax_error( "Unexpected character, expected ':'!", row, column);
}
}
else if(top->search_target == 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;
//end of object
}else if(c == '}'){
if(top->in_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;
//unrecognized character
}else if(!_rjp__is_whitespace(c)){
syntax_error("Unexpected character, expected ','!", row, column);
}
}
else if(top->search_target == json_value){
//object
if(c == '{'){
if(!root){
root = _rjp__add_object(NULL, top);
curr = root;
top->search_target = json_key;
}else{
curr = _rjp__add_object(curr, top);
top->search_target = json_comma;
++top;
top->in_array = 0;
top->search_target = 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;
}
else if(c == ']' && top->in_array){ //empty array
--top->in_array;
top->search_target = json_comma;
}
//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 = calloc(1, 1);
}else{
return NULL;
}
}
_rjp__add_string_no_alloc(curr, new_string, vallen, top);
str += vallen;
top->search_target = json_comma;
}
//numbers
else if((c >= '0' && c <= '9') || c == '-'){
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'){ //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);
}else{
_rjp__add_integer(curr, strtol(str, NULL, 10), top);
}
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;
}else if(!strncmp(str, "false", 5)){
_rjp__add_boolean(curr, 0, top);
str += 4;
top->search_target = json_comma;
}else if(!strncmp(str, "null", 4)){
_rjp__add_null(curr, top);
str += 3;
top->search_target = json_comma;
}
//unrecognized character
else if(!_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

View File

@@ -1,127 +1,239 @@
/**
rjp
Copyright (C) 2018-2020 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/>.
*/
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
#include "rjp.h"
#include "rjp_internal.h"
#include "memory.h"
#include "rjp_string.h"
#include "rjp_object.h"
#include "rjp_value.h"
#include <string.h> //strlen
#include <stdlib.h> //malloc, calloc, free
#include <stdio.h> //sprintf
RJP_value* rjp_init_json(void){
RJP_value* ret = calloc(1, sizeof(RJP_value));
ret->type = json_object;
RJP_value* rjp_new_null(void){
RJP_value* ret = rjp_calloc(1, sizeof(RJP_value));
ret->type = rjp_json_null;
return ret;
}
RJP_value* rjp_add_member(RJP_value* dest, int alloc_key, char* key, size_t keylen, RJP_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;
RJP_value* rjp_new_int(RJP_int val){
RJP_value* ret = rjp_calloc(1, sizeof(RJP_value));
ret->type = rjp_json_integer;
ret->integer = val;
return ret;
}
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;
RJP_value* rjp_new_float(RJP_float val){
RJP_value* ret = rjp_calloc(1, sizeof(RJP_value));
ret->type = rjp_json_dfloat;
ret->dfloat = val;
return ret;
}
RJP_value rjp_integer(long i){
return (RJP_value){.integer = i, .type = json_integer};
RJP_value* rjp_new_bool(RJP_bool val){
RJP_value* ret = rjp_calloc(1, sizeof(RJP_value));
ret->type = rjp_json_boolean;
ret->boolean = val;
return ret;
}
RJP_value rjp_boolean(char b){
return (RJP_value){.boolean = b, .type = json_boolean};
RJP_value* rjp_new_string(char* val, RJP_index length){
RJP_value* ret = rjp_calloc(1, sizeof(RJP_value));
ret->type = rjp_json_string;
ret->string.value = val;
ret->string.length = length;
return ret;
}
RJP_value rjp_dfloat(double d){
return (RJP_value){.dfloat = d, .type = json_dfloat};
RJP_value* rjp_new_string_copy(const char* value, RJP_index length){
UNUSED_VARIABLE(length);
RJP_value* ret = rjp_calloc(1, sizeof(RJP_value));
ret->type = rjp_json_string;
RJP_index esclen = rjp_escape_strlen(value);
ret->string.value = rjp_alloc(esclen+1);
rjp_escape_strcpy(ret->string.value, value);
ret->string.value[esclen] = 0;
ret->string.length = esclen;
return ret;
}
RJP_value rjp_string(char* c, size_t len){
return (RJP_value){.string = {.value = c, .length = len}, .type = json_string};
RJP_value* rjp_new_object(void){
RJP_value* ret = rjp_calloc(1, sizeof(RJP_value));
ret->type = rjp_json_object;
return ret;
}
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_new_array(void){
RJP_value* ret = rjp_calloc(1, sizeof(RJP_value));
ret->type = rjp_json_array;
return ret;
}
RJP_value* rjp_get_member(RJP_value* object){
return &object->object.members->value;
RJP_value* rjp_set_null(RJP_value* v){
irjp_delete_value(v);
v->type = rjp_json_null;
return v;
}
RJP_value* rjp_next_member(RJP_value* member){
//polymorphism
return (RJP_value*)(((RJP_object_member*)member)->next);
RJP_value* rjp_set_int(RJP_value* v, RJP_int val){
irjp_delete_value(v);
v->type = rjp_json_integer;
v->integer = val;
return v;
}
RJP_value* rjp_get_element(RJP_value* array){
return &array->array.elements->value;
RJP_value* rjp_set_float(RJP_value* v, RJP_float val){
irjp_delete_value(v);
v->type = rjp_json_dfloat;
v->dfloat = val;
return v;
}
RJP_value* rjp_next_element(RJP_value* element){
return (RJP_value*)(((RJP_array_element*)element)->next);
RJP_value* rjp_set_bool(RJP_value* v, RJP_bool val){
irjp_delete_value(v);
v->type = rjp_json_boolean;
v->boolean = val;
return v;
}
RJP_value* rjp_set_string(RJP_value* v, char* val, RJP_index len){
irjp_delete_value(v);
v->type = rjp_json_string;
v->string.value = val;
v->string.length = len;
return v;
}
RJP_value* rjp_set_string_copy(RJP_value* v, const char* val, RJP_index length){
UNUSED_VARIABLE(length);
irjp_delete_value(v);
v->type = rjp_json_string;
RJP_index esclen = rjp_escape_strlen(val);
v->string.value = rjp_alloc(esclen+1);
rjp_escape_strcpy(v->string.value, val);
v->string.value[esclen] = 0;
v->string.length = esclen;
return v;
}
RJP_value* rjp_set_object(RJP_value* v){
if(v->type == rjp_json_object)
return v;
irjp_delete_value(v);
memset(&v->object, 0, sizeof(RJP_object));
v->type = rjp_json_object;
return v;
}
RJP_value* rjp_set_array(RJP_value* v){
if(v->type == rjp_json_array)
return v;
irjp_delete_value(v);
memset(&v->object, 0, sizeof(RJP_array));
v->type = rjp_json_array;
return v;
}
char* rjp_member_name(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_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;
}
static size_t _rjp__write_value(char* dest, RJP_value* val){
size_t ret;
static RJP_index irjp_write_value(char* dest, const RJP_value* val){
RJP_index ret;
switch(val->type){
case json_integer:
ret = sprintf(dest, "%li", val->integer);
case rjp_json_integer:
ret = sprintf(dest, "%" PRId64, val->integer);
break;
case json_dfloat:
case rjp_json_dfloat:
ret = sprintf(dest, "%lf", val->dfloat);
break;
case json_boolean:
case rjp_json_boolean:
ret = sprintf(dest, val->boolean ? "true" : "false");
break;
case json_null:
case rjp_json_null:
ret = sprintf(dest, "null");
break;
case json_string:;
case rjp_json_string:;
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, "]");
case rjp_json_array:
ret = rjp_dump_array(val, dest);
break;
case json_object:
ret = sprintf(dest, "{");
ret += rjp_dump_object(val, dest+ret);
ret += sprintf(dest+ret, "}");
case rjp_json_object:
ret = rjp_dump_object(val, dest);
break;
default:
ret = 0;
break;
};
return ret;
}
static RJP_index irjp_write_value_pretty(char* dest, const RJP_value* val, int depth);
static RJP_index irjp_dump_array_pretty(const RJP_value* arr, char* dest, int depth){
RJP_array_iterator it;
rjp_init_array_iterator(&it, arr);
RJP_index pos = 2;
sprintf(dest, "[\n");
for(RJP_value* current = rjp_array_iterator_current(&it);current;current = rjp_array_iterator_next(&it)){
for(int i = 0;i < (depth+1);++i)
pos += sprintf(dest+pos, "\t");
pos += irjp_write_value_pretty(dest+pos, current, depth+1);
if(rjp_array_iterator_peek(&it))
pos += sprintf(dest+pos, ",");
pos += sprintf(dest+pos, "\n");
}
for(int i = 0;i < depth;++i)
pos += sprintf(dest+pos, "\t");
pos += sprintf(dest+pos, "]");
return pos;
}
static RJP_index irjp_dump_object_pretty(const RJP_value* root, char* dest, int depth){
RJP_object_iterator it;
rjp_init_object_iterator(&it, root);
RJP_index pos = 2;
sprintf(dest, "{\n");
for(RJP_value* current = rjp_object_iterator_current(&it);current;current = rjp_object_iterator_next(&it)){
for(int i = 0;i < (depth+1);++i)
pos += sprintf(dest+pos, "\t");
pos += sprintf(dest+pos, "\"%s\": ", rjp_member_key(current)->value);
pos += irjp_write_value_pretty(dest+pos, current, depth+1);
if(rjp_object_iterator_peek(&it))
pos += sprintf(dest+pos, ",");
pos += sprintf(dest+pos, "\n");
}
for(int i = 0;i < depth;++i)
pos += sprintf(dest+pos, "\t");
pos += sprintf(dest+pos, "}");
rjp_delete_object_iterator(&it);
return pos;
}
static RJP_index irjp_write_value_pretty(char* dest, const RJP_value* val, int depth){
RJP_index ret;
switch(val->type){
case rjp_json_integer:
ret = sprintf(dest, "%" PRId64, val->integer);
break;
case rjp_json_dfloat:
ret = sprintf(dest, "%lf", val->dfloat);
break;
case rjp_json_boolean:
ret = sprintf(dest, val->boolean ? "true" : "false");
break;
case rjp_json_null:
ret = sprintf(dest, "null");
break;
case rjp_json_string:;
ret = sprintf(dest, "\"%s\"", val->string.value);
break;
case rjp_json_array:
ret = irjp_dump_array_pretty(val, dest, depth);
break;
case rjp_json_object:
ret = irjp_dump_object_pretty(val, dest, depth);
break;
default:
ret = 0;
@@ -130,50 +242,56 @@ 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 pos = 0;
for(;element_list;element_list = element_list->next){
pos += _rjp__write_value(dest+pos, &element_list->value);
RJP_index rjp_dump_array(const RJP_value* arr, char* dest){
RJP_array_iterator it;
rjp_init_array_iterator(&it, arr);
RJP_index pos = 1;
sprintf(dest, "[");
for(RJP_value* current = rjp_array_iterator_current(&it);current;current = rjp_array_iterator_next(&it)){
pos += irjp_write_value(dest+pos, current);
if(element_list->next)
if(rjp_array_iterator_peek(&it))
pos += sprintf(dest+pos, ",");
else
break;
}
pos += sprintf(dest+pos, "]");
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 pos = 0;
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);
RJP_index rjp_dump_object(const RJP_value* root, char* dest){
RJP_object_iterator it;
rjp_init_object_iterator(&it, root);
RJP_index pos = 1;
sprintf(dest, "{");
if(member_list->next)
for(RJP_value* current = rjp_object_iterator_current(&it);current;current = rjp_object_iterator_next(&it)){
pos += sprintf(dest+pos, "\"%s\":", rjp_member_key(current)->value);
pos += irjp_write_value(dest+pos, current);
if(rjp_object_iterator_peek(&it))
pos += sprintf(dest+pos, ",");
else
break;
}
pos += sprintf(dest+pos, "}");
rjp_delete_object_iterator(&it);
return pos;
}
char* rjp_to_json(RJP_value* root){
char* rjp_to_json(const RJP_value* root, int pretty){
if(!root)
return NULL;
size_t len = _rjp__object_strlen(root);
RJP_index len = pretty ? irjp_value_strlen_pretty(root, 0) : irjp_value_strlen(root);
if(!len)
return NULL;
char* tmp = malloc(len + 1);
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, "}");
if(pretty)
irjp_write_value_pretty(tmp, root, 0);
else
irjp_write_value(tmp, root);
return tmp;
}

168
src/rjp.c Normal file
View File

@@ -0,0 +1,168 @@
/**
rjp
Copyright (C) 2018-2020 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 "rjp_string.h"
#include "rjp_object.h"
#include "rjp_value.h"
#include <stdlib.h> //free, malloc
void* rjp_alloc(RJP_index nbytes){
return malloc(nbytes);
}
void* rjp_calloc(RJP_index num, RJP_index nbytes){
return calloc(num, nbytes);
}
void* rjp_realloc(void* ptr, RJP_index nbytes){
return realloc(ptr, nbytes);
}
void rjp_free(void* data){
free(data);
}
RJP_value* rjp_copy_value(RJP_value* dest, const RJP_value* src){
RJP_value* ret;
if(dest){
irjp_delete_value(dest);
ret = dest;
}else{
ret = rjp_new_null();
}
ret->type = src->type;
switch(src->type){
case rjp_json_object:
irjp_copy_object(ret, src);
break;
case rjp_json_array:
irjp_copy_array(ret, src);
break;
case rjp_json_string:
irjp_strcpy(&ret->string, &src->string);
break;
case rjp_json_integer:
ret->integer = src->integer;
break;
case rjp_json_boolean:
ret->boolean = src->boolean;
break;
case rjp_json_dfloat:
ret->dfloat = src->dfloat;
break;
default:
break;
};
return ret;
}
RJP_value* rjp_move_value(RJP_value* dest, RJP_value* src){
*dest = *src;
return dest;
}
static void irjp_delete_array(RJP_value* root){
RJP_array_iterator it;
rjp_init_array_iterator(&it, root);
RJP_value* next = rjp_array_iterator_current(&it);
for(RJP_value* current = next;current;current = next){
next = rjp_array_iterator_next(&it);
switch(current->type){
case rjp_json_object:
irjp_delete_object(current);
break;
case rjp_json_array:
irjp_delete_array(current);
break;
case rjp_json_string:
free(current->string.value);
break;
default:
break;
};
free(current);
}
}
void irjp_delete_value(RJP_value* root){
if((root->type) == rjp_json_object)
irjp_delete_object(root);
else if((root->type) == rjp_json_array)
irjp_delete_array(root);
else if((root->type) == rjp_json_string)
rjp_free(root->string.value);
}
void rjp_free_value(RJP_value* root){
if(!root)
return;
irjp_delete_value(root);
free(root);
}
/* VALUE SETTING */
/* GETTERS */
RJP_value* rjp_value_parent(const RJP_value* value){
return value->parent;
}
RJP_data_type rjp_value_type(const RJP_value* value){
return value->type;
}
RJP_float rjp_get_float(const RJP_value* value){
return value->dfloat;
}
RJP_int rjp_get_int(const RJP_value* value){
return value->integer;
}
RJP_bool rjp_get_bool(const RJP_value* value){
return value->boolean;
}
RJP_string* rjp_get_string(RJP_value* value){
return &(value->string);
}
const RJP_string* rjp_get_cstring(const RJP_value* value){
return &(value->string);
}
RJP_value irjp_integer(RJP_int i){
return (RJP_value){.integer = i, .type = rjp_json_integer};
}
RJP_value irjp_boolean(RJP_bool b){
return (RJP_value){.boolean = b, .type = rjp_json_boolean};
}
RJP_value irjp_dfloat(RJP_float d){
return (RJP_value){.dfloat = d, .type = rjp_json_dfloat};
}
RJP_value irjp_string(char* c, RJP_index len){
return (RJP_value){.string = {.value = c, .length = len}, .type = rjp_json_string};
}
RJP_value irjp_string_copy(const char* c){
RJP_index 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 = rjp_json_string};
}
RJP_value irjp_null(void){
return (RJP_value){.integer = 0, .type = rjp_json_null};
}
RJP_value irjp_object(void){
return (RJP_value){.object = {0}, .type = rjp_json_object};
}
RJP_value irjp_array(void){
return (RJP_value){.array = {0}, .type = rjp_json_array};
}

94
src/rjp_array.c Normal file
View File

@@ -0,0 +1,94 @@
/**
rjp
Copyright (C) 2018-2020 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_array.h"
#include "rjp_internal.h"
#include "rjp_value.h"
#include "rjp_array_element.h"
void irjp_copy_array(RJP_value* dest, const RJP_value* src){
dest->array.elements = dest->array.last = NULL;
for(RJP_array_element* curr = src->array.elements;curr;curr = curr->next){
RJP_value* copy_mem;
copy_mem = rjp_add_element(dest);
rjp_copy_value(copy_mem, &curr->value);
}
}
void irjp_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;
j->last->prev = j->last;
}
}
RJP_value* rjp_add_element(RJP_value* dest){
irjp_add_element(&dest->array);
dest->array.last->value.parent = dest;
return &dest->array.last->value;
}
RJP_value* rjp_remove_element(RJP_value* dest, RJP_value* value){
RJP_array* j = &dest->array;
if(!j->num_elements)
return NULL;
--j->num_elements;
RJP_array_element* elem = (RJP_array_element*)value;
RJP_array_element* prev = elem->prev;
RJP_array_element* next = elem->next;
if(prev)
prev->next = elem->next;
if(next)
next->prev = prev;
if(elem == j->elements)
j->elements = next;
if(elem == j->last)
j->last = prev;
return value;
}
void rjp_free_element(RJP_value* dest, RJP_value* value){
rjp_remove_element(dest, value);
rjp_free_value(value);
}
RJP_index rjp_num_elements(const RJP_value* array){
return array->array.num_elements;
}
void rjp_init_array_iterator(RJP_array_iterator* iter, const RJP_value* array){
iter->current = &(array->array.elements->value);
}
void rjp_delete_array_iterator(RJP_array_iterator* it){
if(!it)
return;
it->current = NULL;
}
RJP_value* rjp_array_iterator_current(const RJP_array_iterator* it){
return it->current;
}
RJP_value* rjp_array_iterator_next(RJP_array_iterator* it){
RJP_array_element* curr = (RJP_array_element*)it->current;
it->current = curr ? &(curr->next->value) : NULL;
return it->current;
}
RJP_value* rjp_array_iterator_peek(const RJP_array_iterator* it){
RJP_array_element* curr = (RJP_array_element*)it->current;
return curr ? &(curr->next->value) : NULL;
}

242
src/rjp_object.c Normal file
View File

@@ -0,0 +1,242 @@
/**
rjp
Copyright (C) 2018-2020 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 "tree.h"
#include "rjp_object.h"
#include "rjp_object_member.h"
#include <string.h> //strlen, strncpy
#define RJP_TREE_ITERATOR_STACK_START_SIZE 32
typedef struct RJP_tree_stack{
RJP_tree_node** data;
int size;
int pos;
}RJP_tree_stack;
typedef struct RJP_object_iterator_impl{
RJP_tree_stack stack;
}RJP_object_iterator_impl;
static int irjp_init_tree_stack(RJP_tree_stack* stack);
static void irjp_delete_tree_stack(RJP_tree_stack* stack);
static void irjp_resize_tree_stack(RJP_tree_stack* stack);
static void irjp_push_tree_stack(RJP_tree_stack* stack, RJP_tree_node* value);
static RJP_tree_node* irjp_pop_tree_stack(RJP_tree_stack* stack);
static RJP_tree_node* irjp_peek_tree_stack(RJP_tree_stack* stack);
static int irjp_init_object_iterator(RJP_object_iterator_impl* it, const RJP_tree_node* root);
static void irjp_delete_object_iterator(RJP_object_iterator_impl* it);
static RJP_tree_node* irjp_object_iterator_next(RJP_object_iterator_impl* it);
static RJP_tree_node* irjp_object_iterator_peek(RJP_object_iterator_impl* it);
static RJP_tree_node* irjp_object_iterator_current(RJP_object_iterator_impl* it);
/* TREE STACK */
//Stack construction / destruction
static int irjp_init_tree_stack(RJP_tree_stack* stack){
stack->data = rjp_alloc(sizeof(RJP_tree_node*)*RJP_TREE_ITERATOR_STACK_START_SIZE);
stack->size = RJP_TREE_ITERATOR_STACK_START_SIZE;
stack->pos = 0;
return 0;
}
static void irjp_delete_tree_stack(RJP_tree_stack* stack){
rjp_free(stack->data);
}
static void irjp_resize_tree_stack(RJP_tree_stack* stack){
int newsize = stack->size*2;
RJP_tree_node** newdata = rjp_alloc(sizeof(RJP_tree_node*)*newsize);
for(int i = 0;i < stack->size;++i){
newdata[i] = stack->data[i];
}
rjp_free(stack->data);
stack->data = newdata;
stack->size = newsize;
}
//Stack operations
static void irjp_push_tree_stack(RJP_tree_stack* stack, RJP_tree_node* value){
stack->data[stack->pos++] = value;
if(stack->pos == stack->size)
irjp_resize_tree_stack(stack);
}
static RJP_tree_node* irjp_pop_tree_stack(RJP_tree_stack* stack){
return stack->data[--stack->pos];
}
static RJP_tree_node* irjp_peek_tree_stack(RJP_tree_stack* stack){
return (stack->pos > 0) ? (stack->data[stack->pos-1]) : NULL;
}
static RJP_tree_node* irjp_double_peek_tree_stack(RJP_tree_stack* stack){
return (stack->pos > 1) ? (stack->data[stack->pos-2]) : NULL;
}
/* TREE ITERATOR */
//Iterator construction / destruction
static int irjp_init_object_iterator(RJP_object_iterator_impl* it, const RJP_tree_node* root){
irjp_init_tree_stack(&it->stack);
RJP_tree_node* current = (RJP_tree_node*)root;
while(current){
irjp_push_tree_stack(&it->stack, current);
current = current->left;
}
return 0;
}
static void irjp_delete_object_iterator(RJP_object_iterator_impl* it){
irjp_delete_tree_stack(&it->stack);
}
//Iterator operations
static RJP_tree_node* irjp_object_iterator_next(RJP_object_iterator_impl* it){
RJP_tree_node* last = irjp_pop_tree_stack(&it->stack);
if(last->right){
irjp_push_tree_stack(&it->stack, last->right);
}
return irjp_object_iterator_current(it);
}
static RJP_tree_node* irjp_object_iterator_peek(RJP_object_iterator_impl* it){
RJP_tree_node* n = irjp_peek_tree_stack(&it->stack)->right;
if(n)
return n;
return irjp_double_peek_tree_stack(&it->stack);
}
static RJP_tree_node* irjp_object_iterator_current(RJP_object_iterator_impl* it){
return irjp_peek_tree_stack(&it->stack);
}
/* RJP_OBJECT */
void irjp_copy_object(RJP_value* dest, const RJP_value* src){
irjp_free_tree(dest->object.root);
dest->object.root = irjp_copy_tree(src->object.root);
}
void irjp_delete_object(RJP_value* obj){
irjp_free_tree(obj->object.root);
}
static RJP_value* irjp_add_member_impl(RJP_value* dest, char* key, RJP_index keylen){
++dest->object.num_members;
int status;
RJP_value* retval;
dest->object.root = irjp_tree_insert_value(dest->object.root, key, keylen, &retval, &status);
retval->parent = dest;
return retval;
}
RJP_value* rjp_add_member(RJP_value* dest, char* key, RJP_index keylen){
if(!keylen)
keylen = strlen(key);
return irjp_add_member_impl(dest, key, keylen);
}
RJP_value* rjp_add_member_key_copy(RJP_value* dest, const char* key, RJP_index keylen){
if(!keylen)
keylen = rjp_escape_strlen(key);
char* newkey = rjp_alloc(keylen+1);
rjp_escape_strcpy(newkey, key);
newkey[keylen] = 0;
return irjp_add_member_impl(dest, newkey, keylen);
}
RJP_value* rjp_remove_member_by_key(RJP_value* obj, const char* key){
RJP_value* member = rjp_search_member(obj, key);
if(!member)
return NULL;
return rjp_remove_member(obj, member);
}
RJP_value* rjp_remove_member(RJP_value* obj, RJP_value* member){
RJP_tree_node* removed_node = NULL;
obj->object.root = irjp_tree_remove_value(obj->object.root, (RJP_tree_node*)member, &removed_node);
rjp_free(((RJP_object_member*)removed_node)->name.value);
return member;
}
void rjp_free_member_by_key(RJP_value* obj, const char* key){
RJP_value* removed = rjp_remove_member_by_key(obj, key);
rjp_free_value(removed);
}
void rjp_free_member(RJP_value* obj, RJP_value* member){
rjp_free_member_by_key(obj, ((RJP_object_member*)member)->name.value);
}
void rjp_set_key_copy(RJP_value* dest, const char* key, RJP_index keylen){
if(key){
if(!keylen){
keylen = strlen(key);
}
}else{
keylen = 0;
}
char* newkey = rjp_alloc(keylen + 1);
strncpy(newkey, key, keylen);
newkey[keylen] = 0;
rjp_set_key(dest, newkey, keylen);
}
void rjp_set_key(RJP_value* dest, char* key, RJP_index keylen){
if(key){
if(!keylen){
keylen = strlen(key);
}
}else{
keylen = 0;
}
RJP_value* parent = dest->parent;
RJP_tree_node* removed_node = NULL;
parent->object.root = irjp_tree_remove_value(parent->object.root, (RJP_tree_node*)dest, &removed_node);
rjp_free(((RJP_object_member*)removed_node)->name.value);
((RJP_object_member*)removed_node)->name.value = key;
((RJP_object_member*)removed_node)->name.length = keylen;
parent->object.root = irjp_tree_insert_node(parent->object.root, removed_node);
}
RJP_index rjp_num_members(const RJP_value* object){
return object->object.num_members;
}
const RJP_string* rjp_member_key(const RJP_value* member){
return &(((RJP_object_member*)member)->name);
}
RJP_value* rjp_search_member(const RJP_value* object, const char* search){
RJP_tree_node* n = irjp_tree_search_value(object->object.root, search);
if(!n)
return NULL;
return &n->data.value;
}
void rjp_init_object_iterator(RJP_object_iterator* it, const RJP_value* object){
it->it = rjp_alloc(sizeof(RJP_object_iterator_impl));
irjp_init_object_iterator(it->it, object->object.root);
}
void rjp_delete_object_iterator(RJP_object_iterator* it){
irjp_delete_object_iterator(it->it);
rjp_free(it->it);
it->it = NULL;
}
RJP_value* rjp_object_iterator_current(const RJP_object_iterator* it){
RJP_tree_node* n = irjp_object_iterator_current(it->it);
if(!n)
return NULL;
return &n->data.value;
}
RJP_value* rjp_object_iterator_next(RJP_object_iterator* it){
RJP_tree_node* n = irjp_object_iterator_next(it->it);
if(!n)
return NULL;
return &n->data.value;
}
RJP_value* rjp_object_iterator_peek(const RJP_object_iterator* it){
RJP_tree_node* n = irjp_object_iterator_peek(it->it);
if(!n)
return NULL;
return &n->data.value;
}

412
src/rjp_string.c Normal file
View File

@@ -0,0 +1,412 @@
/**
rjp
Copyright (C) 2018-2020 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/>.
*/
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
#include "rjp_string.h"
#include "rjp_internal.h"
#include "rjp_object.h"
#include "rjp_value.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 irjp_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(RJP_index 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* irjp_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;
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_value(root);
return NULL;
}else if(*(str+oldpos) == '\n'){
++(*row);
*column = 0;
}
}
*inclen = oldpos;
*len = newpos;
if(newpos == 0){
return NULL;
}
new_string = rjp_alloc(newpos + 1);
new_string[newpos] = 0;
for(int i = 0;*str != '"';++i,++str){
if(*str == '\\'){
++str;
switch(*str){
case '"':
new_string[i] = '"';
break;
case 'n':
new_string[i] = '\n';
break;
case 'r':
new_string[i] = '\r';
break;
case 'b':
new_string[i] = '\b';
break;
case '\\':
new_string[i] = '\\';
break;
case 't':
new_string[i] = '\t';
break;
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;
}
}else{
new_string[i] = *str;
}
}
return new_string;
}
RJP_index rjp_escape_strcpy(char* dest, const char* src){
RJP_index j = 0;
for(RJP_index i = 0;src[i];++i){
switch(src[i]){
case '"':
dest[j++] = '\\';
dest[j++] = '"';
break;
case '\n':
dest[j++] = '\\';
dest[j++] = 'n';
break;
case '\r':
dest[j++] = '\\';
dest[j++] = 'r';
break;
case '\b':
dest[j++] = '\\';
dest[j++] = 'b';
break;
case '\\':
dest[j++] = '\\';
dest[j++] = '\\';
break;
case '\t':
dest[j++] = '\\';
dest[j++] = 't';
break;
case '\f':
dest[j++] = '\\';
dest[j++] = 'f';
break;
default:
dest[j++] = src[i];
break;
};
}
dest[j] = 0;
return j;
}
void irjp_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;
}
RJP_index rjp_escape_strlen(const char* str){
RJP_index count = 0;
for(RJP_index i = 0;str[i];++i){
switch(str[i]){
case '"':
case '\n':
case '\r':
case '\b':
case '\\':
case '\t':
case '\f':
++count;
//fallthrough
default:
++count;
};
}
return count;
}
RJP_index irjp_array_strlen(const RJP_value* arr){
RJP_index count = 2; //[]
RJP_array_iterator it;
rjp_init_array_iterator(&it, arr);
for(RJP_value* current = rjp_array_iterator_current(&it);current;current = rjp_array_iterator_next(&it)){
count += irjp_value_strlen(current);
if(rjp_array_iterator_peek(&it))
++count; //,
}
return count;
}
RJP_index irjp_array_strlen_pretty(const RJP_value* arr, int depth){
RJP_index count = 3 + depth; //[\n\t]
++depth;
RJP_array_iterator it;
rjp_init_array_iterator(&it, arr);
for(RJP_value* current = rjp_array_iterator_current(&it);current;current = rjp_array_iterator_next(&it)){
count += irjp_value_strlen_pretty(current, depth);
count += depth; //tabs
++count; //newline
if(rjp_array_iterator_peek(&it))
++count; //,
}
return count;
}
RJP_index irjp_object_strlen(const RJP_value* root){
RJP_index count = 2; //{}
RJP_object_iterator it;
rjp_init_object_iterator(&it, root);
RJP_value* current = rjp_object_iterator_current(&it);
while(current){
RJP_index namelen = rjp_member_key(current)->length;
const char* name = rjp_member_key(current)->value;
if(namelen == 0 || name[0] == 0){
rjp_delete_object_iterator(&it);
return 0;
}
count += namelen + 3; //"":
count += irjp_value_strlen(current);
if((current = rjp_object_iterator_next(&it)))
++count; //,
}
rjp_delete_object_iterator(&it);
return count;
}
RJP_index irjp_object_strlen_pretty(const RJP_value* root, int depth){
RJP_index count = 3 + depth; //{\n\t}
++depth;
RJP_object_iterator it;
rjp_init_object_iterator(&it, root);
RJP_value* current = rjp_object_iterator_current(&it);
while(current){
RJP_index namelen = rjp_member_key(current)->length;
const char* name = rjp_member_key(current)->value;
if(namelen == 0 || name[0] == 0){
rjp_delete_object_iterator(&it);
return 0;
}
count += namelen + 4; //"":space
count += irjp_value_strlen_pretty(current, depth);
count += depth; //tabs
++count; //newline
if((current = rjp_object_iterator_next(&it)))
++count; //,
}
rjp_delete_object_iterator(&it);
return count;
}
RJP_index irjp_value_strlen(const RJP_value* root){
switch(root->type){
case rjp_json_integer:
return snprintf(NULL, 0, "%" PRId64, root->integer);
case rjp_json_dfloat:
return snprintf(NULL, 0, "%lf", root->dfloat);
case rjp_json_boolean:
return root->boolean ? 4 : 5; //true, false
case rjp_json_null:
return 4;
case rjp_json_string:
return rjp_escape_strlen(root->string.value) + 2; //"";
case rjp_json_array:
return irjp_array_strlen(root);
case rjp_json_object:
return irjp_object_strlen(root);
default:
return 0;
};
}
RJP_index irjp_value_strlen_pretty(const RJP_value* root, int depth){
switch(root->type){
case rjp_json_integer:
return snprintf(NULL, 0, "%" PRId64, root->integer);
case rjp_json_dfloat:
return snprintf(NULL, 0, "%lf", root->dfloat);
case rjp_json_boolean:
return root->boolean ? 4 : 5; //true, false
case rjp_json_null:
return 4;
case rjp_json_string:
return rjp_escape_strlen(root->string.value) + 2; //"";
case rjp_json_array:
return irjp_array_strlen_pretty(root, depth);
case rjp_json_object:
return irjp_object_strlen_pretty(root, depth);
default:
return 0;
};
}

View File

@@ -1,201 +0,0 @@
#include "rjp.h"
#include "rjp_internal.h"
#include <stdio.h> //fprintf
#include <stdlib.h> //malloc, free
//Convert escape sequences in strings
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;
fprintf(stderr, "Syntax error! %s (%i:%i)\n", "Unexpected EOF in string!", *row, *column);
rjp_free(root);
return NULL;
}else if(*(str+*len) == '\n'){
++(*row);
*column = 0;
}
}
if(*len == 0){
return NULL;
}
new_string = malloc(*len + 1);
new_string[*len] = 0;
for(int i = 0;*str != '"';++i,++str){
if(*str == '\\'){
++str;
switch(*str){
case '"':
new_string[i] = '"';
break;
case 'n':
new_string[i] = '\n';
break;
case 'r':
new_string[i] = '\r';
break;
case 'b':
new_string[i] = '\b';
break;
case '\\':
new_string[i] = '\\';
break;
case 't':
new_string[i] = '\t';
break;
case 'f':
new_string[i] = '\f';
break;
default:
new_string[i] = *str;
break;
}
}else{
new_string[i] = *str;
}
}
return new_string;
}
size_t rjp_escape_strcpy(char* dest, const char* src){
size_t j = 0;
for(size_t i = 0;src[i];++i){
switch(src[i]){
case '"':
dest[j++] = '\\';
dest[j++] = '"';
break;
case '\n':
dest[j++] = '\\';
dest[j++] = 'n';
break;
case '\r':
dest[j++] = '\\';
dest[j++] = 'r';
break;
case '\b':
dest[j++] = '\\';
dest[j++] = 'b';
break;
case '\\':
dest[j++] = '\\';
dest[j++] = '\\';
break;
case '\t':
dest[j++] = '\\';
dest[j++] = 't';
break;
case '\f':
dest[j++] = '\\';
dest[j++] = 'f';
break;
default:
dest[j++] = src[i];
break;
};
}
return j;
}
size_t rjp_escape_strlen(const char* str){
size_t count = 0;
for(size_t i = 0;str[i];++i){
switch(str[i]){
case '"':
case '\n':
case '\r':
case '\b':
case '\\':
case '\t':
case '\f':
++count;
//fallthrough
default:
++count;
};
}
return count;
}
size_t _rjp__array_strlen(RJP_value* arr){
size_t count = 2; //[]
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:
count += snprintf(NULL, 0, "%li", element_list->value.integer);
break;
case json_dfloat:
count += snprintf(NULL, 0, "%lf", element_list->value.dfloat);
break;
case json_boolean:
count += element_list->value.boolean ? 4 : 5;
break;
case json_null:
count += 4;
break;
case json_string:
count += element_list->value.string.length;
break;
case json_array:
count += _rjp__array_strlen(&element_list->value);
break;
case json_object:
count += _rjp__object_strlen(&element_list->value);
break;
};
if(element_list->next)
++count;
else
break;
}
return count;
}
size_t _rjp__object_strlen(RJP_value* root){
size_t count = 2; //{}
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:;
size_t new_count = _rjp__object_strlen(&member_list->value);
if(!new_count)
return 0;
count += new_count;
break;
};
if(member_list->next)
++count; //,
else
break;
}
return count;
}

454
src/tree.c Normal file
View File

@@ -0,0 +1,454 @@
/**
rjp
Copyright (C) 2018-2020 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 "tree.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "rjp_string.h"
#include "rjp_internal.h"
#include "rjp_object.h"
#define BLACK 0
#define RED 1
//Forward declare static functions
static inline RJP_tree_node* irjp_grandparent(RJP_tree_node* n);
static inline RJP_tree_node* irjp_sibling(RJP_tree_node* n);
static inline RJP_tree_node* irjp_uncle(RJP_tree_node* n);
static inline RJP_tree_node* irjp_rotate_left(RJP_tree_node* pivot);
static inline RJP_tree_node* irjp_rotate_right(RJP_tree_node* pivot);
static inline int irjp_is_inside_left(RJP_tree_node* n);
static inline int irjp_is_inside_right(RJP_tree_node* n);
static void irjp_replace_node(RJP_tree_node *restrict node, RJP_tree_node *restrict child);
static void irjp_copy_node_data(RJP_tree_node *restrict dest, RJP_tree_node *restrict src);
static RJP_tree_node* irjp_clone_node(const RJP_tree_node* src, RJP_tree_node* parent);
static RJP_tree_node* irjp_copy_node(const RJP_tree_node* root, RJP_tree_node* parent);
static void irjp_free_node(RJP_tree_node* node);
static void irjp_delete_node(RJP_tree_node* node);
static RJP_tree_node* irjp_tree_insert_impl(RJP_tree_node *restrict root, RJP_tree_node *restrict newnode);
static RJP_tree_node* irjp_tree_repair(RJP_tree_node* node);
static RJP_tree_node* irjp_tree_remove_node(RJP_tree_node* target, RJP_tree_node** removed_node);
//Tree helpers
static inline RJP_tree_node* irjp_grandparent(RJP_tree_node* n){
if(!n->parent)
return NULL;
return n->parent->parent;
}
static inline RJP_tree_node* irjp_sibling(RJP_tree_node* n){
RJP_tree_node* p = n->parent;
if(!p)
return NULL;
if(p->left == n)
return p->right;
return p->left;
}
static inline RJP_tree_node* irjp_uncle(RJP_tree_node* n){
if(!n->parent)
return NULL;
return irjp_sibling(n->parent);
}
static inline RJP_tree_node* irjp_rotate_right(RJP_tree_node* pivot){
RJP_tree_node* newroot = pivot->left;
pivot->left = newroot->right;
newroot->right = pivot;
newroot->parent = pivot->parent;
pivot->parent = newroot;
if(newroot->parent){
if(newroot->parent->left == pivot)
newroot->parent->left = newroot;
else
newroot->parent->right = newroot;
}
if(pivot->left)
pivot->left->parent = pivot;
return newroot;
}
static inline RJP_tree_node* irjp_rotate_left(RJP_tree_node* pivot){
RJP_tree_node* newroot = pivot->right;
pivot->right = newroot->left;
newroot->left = pivot;
newroot->parent = pivot->parent;
pivot->parent = newroot;
if(newroot->parent){
if(newroot->parent->left == pivot)
newroot->parent->left = newroot;
else
newroot->parent->right = newroot;
}
if(pivot->right)
pivot->right->parent = pivot;
return newroot;
}
static inline int irjp_is_inside_left(RJP_tree_node* n){
RJP_tree_node* p = n->parent;
RJP_tree_node* g = irjp_grandparent(n);
return n == p->right && p == g->left;
}
static inline int irjp_is_inside_right(RJP_tree_node* n){
RJP_tree_node* p = n->parent;
RJP_tree_node* g = irjp_grandparent(n);
return n == p->left && p == g->right;
}
static void irjp_replace_node(RJP_tree_node *restrict node, RJP_tree_node *restrict child){
if(child)
child->parent = node->parent;
if(!node->parent)
return;
if(node == node->parent->left)
node->parent->left = child;
else
node->parent->right = child;
}
//Node construction / destruction
RJP_tree_node* irjp_new_node(char* key, RJP_index keylen){
RJP_tree_node* node = rjp_calloc(sizeof(RJP_tree_node), 1);
node->data.name.value = key;
node->data.name.length = keylen;
node->parent = node->left = node->right = NULL;
node->color = BLACK;
return node;
}
static void irjp_copy_node_data(RJP_tree_node *restrict dest, RJP_tree_node *restrict src){
dest->data = src->data;
}
static RJP_tree_node* irjp_clone_node(const RJP_tree_node* src, RJP_tree_node* parent){
RJP_tree_node* dest = rjp_alloc(sizeof(RJP_tree_node));
rjp_copy_value(&dest->data.value, &src->data.value);
irjp_strcpy(&dest->data.name, &src->data.name);
dest->parent = parent;
return dest;
}
static RJP_tree_node* irjp_copy_node(const RJP_tree_node* root, RJP_tree_node* parent){
if(!root){
return NULL;
}
RJP_tree_node* newnode = irjp_clone_node(root, parent);
newnode->left = irjp_copy_node(root->left, newnode);
newnode->right = irjp_copy_node(root->right, newnode);
return newnode;
}
static void irjp_free_node(RJP_tree_node* node){
irjp_delete_node(node);
rjp_free(node);
}
static void irjp_delete_node(RJP_tree_node* node){
rjp_free(node->data.name.value);
irjp_delete_value(&node->data.value);
}
/* TREE */
//Tree construction / destruction
RJP_tree_node* irjp_copy_tree(const RJP_tree_node* root){
return irjp_copy_node(root, NULL);
}
void irjp_free_tree(RJP_tree_node* root){
if(!root)
return;
irjp_free_tree(root->left);
irjp_free_tree(root->right);
irjp_free_node(root);
}
//Tree operations
RJP_tree_node* irjp_tree_insert_value(RJP_tree_node* root, char* key, RJP_index keylen, RJP_value** added, int* status){
*status = RJP_TREE_SUCCESS;
if(!root){
root = irjp_new_node(key, keylen);
if(added)
*added = &root->data.value;
return root;
}
RJP_tree_node* newnode = irjp_new_node(key, keylen);
if(added)
*added = &newnode->data.value;
return irjp_tree_insert_node(root, newnode);
}
RJP_tree_node* irjp_tree_insert_node(RJP_tree_node *restrict root, RJP_tree_node *restrict newnode){
irjp_tree_insert_impl(root, newnode);
irjp_tree_repair(newnode);
while(root->parent)
root = root->parent;
return root;
}
static RJP_tree_node* irjp_tree_insert_impl(RJP_tree_node *restrict root, RJP_tree_node *restrict newnode){
if(!root){
return newnode;
}
RJP_tree_node* n = root;
while(1){
//compare json key strings
int cmpval = strcmp(newnode->data.name.value, n->data.name.value);
if(cmpval < 0){
if(n->left){
n = n->left;
}else{
n->left = newnode;
newnode->parent = n;
break;
}
}else{
if(n->right){
n = n->right;
}else{
n->right = newnode;
newnode->parent = n;
break;
}
}
}
return newnode;
}
static RJP_tree_node* irjp_tree_repair(RJP_tree_node* node){
node->color = RED;
while(1){
if(!node->parent){
node->color = BLACK;
return node;
}else if(node->parent->color == BLACK){
return node;
}else if(irjp_uncle(node) && irjp_uncle(node)->color == RED){
node->parent->color = BLACK;
irjp_uncle(node)->color = BLACK;
node = irjp_grandparent(node);
}else{
if(irjp_is_inside_left(node)){
irjp_rotate_left(node->parent);
node = node->left;
}else if(irjp_is_inside_right(node)){
irjp_rotate_right(node->parent);
node = node->right;
}
RJP_tree_node* pa = node->parent;
RJP_tree_node* gp = irjp_grandparent(node);
if(node == pa->left){
irjp_rotate_right(gp);
}else{
irjp_rotate_left(gp);
}
pa->color = BLACK;
gp->color = RED;
return node;
}
}
}
RJP_tree_node* irjp_tree_search_value(RJP_tree_node* root, const char* key){
while(root != NULL){
int cmpval = strcmp(key, root->data.name.value);
if(cmpval < 0){
root = root->left;
}else if(cmpval > 0){
root = root->right;
}else{
return root;
}
}
return NULL;
}
//Debug printouts
void irjp_dbg_print_tree(RJP_tree_node* root){
if(!root){
return;
}
RJP_tree_node* current = root, *pre;
while(current){
if(!current->left){
printf("%s\n", current->data.name.value);
current = current->right;
}else{
pre = current->left;
while(pre->right && pre->right != current){
pre = pre->right;
}
if(!pre->right){
pre->right = current;
current = current->left;
}else{
pre->right = NULL;
printf("%s\n", current->data.name.value);
current = current->right;
}
}
}
}
#define pop() front = (front+1)%queuelen
#define push(val, dp) do{queue[rear].n = (val);queue[rear].depth = (dp);rear = (rear+1)%queuelen;}while(0)
struct tmpthing{
RJP_tree_node* n;
int depth;
};
void irjp_dbg_print_tree_bfs(RJP_tree_node* root){
int queuelen = 64;
struct tmpthing queue[64];
int front = 0, rear = 0;
int lastdepth = 0;
push(root, lastdepth);
while(front != rear){
if(lastdepth != queue[front].depth){
lastdepth = queue[front].depth;
printf("\n");
}
if(!queue[front].n){
printf("*");
}else{
printf("%s ", queue[front].n->data.name.value);
if(queue[front].n->left){
push(queue[front].n->left, queue[front].depth+1);
}else{
push(NULL,queue[front].depth+1);
}
if(queue[front].n->right){
push(queue[front].n->right, queue[front].depth+1);
}else{
push(NULL,queue[front].depth+1);
}
}
pop();
}
printf("\n");
}
#undef pop
#undef push
RJP_tree_node* irjp_tree_remove_value(RJP_tree_node* restrict root, RJP_tree_node* restrict member, RJP_tree_node** removed_node){
if(!root)
return root;
if(!member)
return root;
return irjp_tree_remove_node(member, removed_node);
}
static RJP_tree_node* irjp_tree_remove_node(RJP_tree_node* target, RJP_tree_node** removed_node){
while(target->right && target->left){
irjp_copy_node_data(target, target->right);
target = target->right;
}
*removed_node = target;
RJP_tree_node* retval = target->parent;
do{
RJP_tree_node* child = (target->left) ? target->left : target->right;
//handle trivial cases
if(target->color == RED){
irjp_replace_node(target, child);
target->parent = NULL;
break;
}else{
if(child && child->color == RED){
child->color = BLACK;
}
irjp_replace_node(target, child);
if(!retval)
retval = child;
break;
}
if(!target->parent){
retval = child;
break;
//handle harder cases
}else{
//parent and sibling guaranteed to exist at this point
RJP_tree_node* sibling = irjp_sibling(target);
//Deal with red sibling
if(sibling->color == RED){
sibling->color = BLACK;
target->parent->color = RED;
if(target == target->parent->left){
irjp_rotate_left(target->parent);
}else{
irjp_rotate_right(target->parent);
}
sibling = irjp_sibling(target);
}
//sibling guaranteed to be black here
if(target->parent->color == BLACK &&
((!sibling->left) || sibling->left->color == BLACK) &&
((!sibling->right) || sibling->right->color == BLACK))
{
sibling->color = RED;
target = target->parent;
continue;
}
//sibling guaranteed to be black here
if(target->parent->color == RED &&
((!sibling->left) || sibling->left->color == BLACK) &&
((!sibling->right) || sibling->right->color == BLACK))
{
sibling->color = RED;
target->parent->color = BLACK;
break;
}
//Orient the subtree for the following section
if((target == target->parent->left) && (!sibling->right || sibling->right->color == BLACK) &&
(sibling->left && sibling->left->color == RED))
{
sibling->color = RED;
if(sibling->left)
sibling->left->color = BLACK;
irjp_rotate_right(sibling);
sibling = irjp_sibling(target);
}else if((target == target->parent->right) && (sibling->right && sibling->right->color == RED) &&
(!sibling->left || sibling->left->color == BLACK))
{
sibling->color = RED;
if(sibling->right)
sibling->right->color = BLACK;
irjp_rotate_left(sibling);
sibling = irjp_sibling(target);
}
sibling->color = target->parent->color;
target->parent->color = BLACK;
if(target == target->parent->left){
//guaranteed to not be NULL
sibling->right->color = BLACK;
irjp_rotate_left(target->parent);
}else{
//guaranteed to not be NULL
sibling->left->color = BLACK;
irjp_rotate_right(target->parent);
}
break;
}
}while(1);
(*removed_node)->left = NULL;
(*removed_node)->right = NULL;
//return new root
if(!retval)
return NULL;
while(retval->parent)
retval = retval->parent;
return retval;
}