42 Commits

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
22 changed files with 1996 additions and 808 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,7 @@ include(GNUInstallDirs)
cmake_minimum_required(VERSION 3.0.2)
project(rjp)
set(rjp_VERSION_MAJOR 0)
set(rjp_VERSION_MINOR 6)
set(rjp_VERSION_MINOR 8)
set(rjp_VERSION_REVISION 2)
set(INCLUDE_PATH ${CMAKE_SOURCE_DIR}/include)
configure_file(
@@ -16,15 +16,15 @@ 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/strings.c" "src/memory.c" "src/rjp.c")
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()
set_target_properties(rjp PROPERTIES PUBLIC_HEADER ${INCLUDE_PATH}/rjp.h)
set_target_properties(rjp PROPERTIES SOVERSION "${rjp_VERSION_MAJOR}.${rjp_VERSION_MINOR}.${rjp_VERSION_REVISION}")
target_compile_options(rjp PRIVATE -Wall -Wextra -pedantic)
if(ENABLE_DIAGNOSTICS)
target_compile_definitions(rjp PRIVATE RJP_DIAGNOSTICS)

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-2019 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,8 +16,8 @@
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@

View File

@@ -1,6 +1,6 @@
/**
rjp
Copyright (C) 2018-2019 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,164 +19,182 @@
#ifndef RJP_H
#define RJP_H
#include <stddef.h> //size_t
#ifdef __cplusplus
extern "C"{
#endif
#if defined(__GNUC__) || defined(__clang__)
#define DEPRECATED(str) __attribute__((deprecated(str)))
#else
#define DEPRECATED(str)
#endif
#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_type{
json_object = 0,
json_string,
json_integer,
json_dfloat,
json_boolean,
json_array,
json_null
}RJP_type;
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;
//Forward declarations
struct RJP_object_member;
struct RJP_array_element;
struct RJP_object_iterator_impl;
typedef struct RJP_object_iterator{
struct RJP_object_iterator_impl* it;
}RJP_object_iterator;
//Represents a json object
typedef struct RJP_object{
struct RJP_object_member* members; //linked list of members
struct RJP_object_member* last; //final member of linked list
size_t num_members;
}RJP_object;
//Represents a json array
typedef struct RJP_array{
struct RJP_array_element* elements; //linked list of elements
struct RJP_array_element* last; //final member of linked list
size_t num_elements;
}RJP_array;
//Represents json data
//hold any json data type
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; //pointer to parent (either an array or object or NULL)
enum RJP_type type; //flag to determine active member of union
}RJP_value;
//Result of an rjp search operation
typedef struct RJP_search_res{
RJP_value* value; //pointer to match
size_t searchindex; //index in the search list
size_t objindex; //index in the searched object
}RJP_search_res;
//Convert C string consisting of json data into RJP's format
RJP_value* rjp_parse(const char* str);
//Initialize a root RJP_value to NULL
RJP_value* rjp_init_json(void);
//Initialize a root RJP_value to copy of value
RJP_value* rjp_init_json_as(RJP_value value);
//Free an RJP_value and all members/elements
void rjp_free_value(RJP_value* root);
//Deep copy RJP_value
void rjp_copy_value(RJP_value* dest, const RJP_value* src);
typedef struct RJP_array_iterator{
RJP_value* current;
}RJP_array_iterator;
/***************** NON OBJECT OPERATIONS *******************/
//Allocate heap space for rjp to use. Use if the memory is to be freed by rjp
void* rjp_alloc(size_t nbytes);
void* rjp_alloc(RJP_index nbytes);
//Allocate heap space for rjp and initialize to 0.
void* rjp_calloc(size_t num, size_t nbytes);
void* rjp_calloc(RJP_index num, RJP_index nbytes);
//Resize heap allocated space for rjp
void* rjp_realloc(void* ptr, size_t nbytes);
void* rjp_realloc(void* ptr, RJP_index nbytes);
//Free memory allocated by rjp_alloc or rjp_calloc
void rjp_free(void* dest);
//add a member to a json object, allocating a copy of the key
RJP_value* rjp_add_member(RJP_value* dest, const char* key, size_t keylen, RJP_value value);
//add a member to a json object without allocation. key must be allocated using rjp_alloc/rjp_calloc
RJP_value* rjp_add_member_no_alloc(RJP_value* dest, char* key, size_t keylen, RJP_value value);
//add an element to a json array
RJP_value* rjp_add_element(RJP_value* dest, RJP_value value);
//copy input string and add json escape sequences
RJP_index rjp_escape_strcpy(char* dest, const char* src);
//length of string including escape sequences
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
void rjp_set_value(RJP_value* dest, RJP_value value);
//initialize a RJP_value to the requested type and value
RJP_value rjp_integer(long i);
RJP_value rjp_boolean(char b);
RJP_value rjp_dfloat(double d);
RJP_value rjp_string(char* c, size_t len);
RJP_value rjp_null(void);
RJP_value rjp_object(void);
RJP_value rjp_array(void);
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);
//Access first member of a json object
RJP_value* rjp_get_member(const RJP_value* object);
//Access next member of a json object given the previous member
RJP_value* rjp_next_member(const RJP_value* member);
//Return number of members in the object
size_t rjp_num_members(const RJP_value* object);
//Return the object member's key name
char* rjp_member_name(const RJP_value* member);
//Return the object member's key name length excluding null terminator
size_t rjp_member_name_length(RJP_value* member);
//Search for an object member with given key
RJP_search_res rjp_search_member(const RJP_value* object, const char* search, size_t skip);
//Search for first occurance of multiple keys. Returns first occurance of each.
//Assumes dest is large enough to hold maximum num items.
size_t rjp_search_members(const RJP_value* object, size_t num, const char* const* searches, RJP_search_res* dest, size_t skip);
//Search for multiple occurances of multiple keys. Returns pointer to list of matches.
//Returned pointer must be freed using rjp_free
RJP_search_res* rjp_search_members_multi(const RJP_value* object, size_t num, const char* const* searches, size_t* rsize, size_t skip);
//Access first element of a json array
RJP_value* rjp_get_element(const RJP_value* array);
//Access next element of a json array given the previous element
RJP_value* rjp_next_element(const RJP_value* element);
//Return number of elements in the array
size_t rjp_num_elements(const RJP_value* array);
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);
//Return handle to parent of given value
RJP_value* rjp_value_parent(RJP_value* element);
/***************** OBJECT/ARRAY SHARED OPERATIONS *******************/
RJP_value* rjp_value_parent(const RJP_value* element);
//Return type of value
RJP_type rjp_value_type(RJP_value* value);
RJP_data_type rjp_value_type(const RJP_value* value);
//value accessors
double rjp_value_dfloat(RJP_value* value);
int rjp_value_integer(RJP_value* value);
char rjp_value_boolean(RJP_value* value);
char* rjp_value_string(RJP_value* value);
size_t rjp_value_string_length(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);
//copy input string and add json escape sequences
size_t rjp_escape_strcpy(char* dest, const char* src);
//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);
//length of string including escape sequences
size_t rjp_escape_strlen(const char* str);
//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);
//Convert RJP_object to json string
size_t rjp_dump_object(RJP_value* root, char* dest);
//Convert RJP_array to json string
size_t rjp_dump_array(RJP_value* arr, char* dest);
//Convert root rjp value into json string
char* rjp_to_json(RJP_value* root);
#ifdef __cplusplus
}
#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

@@ -1,6 +1,6 @@
/**
rjp
Copyright (C) 2018-2019 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,17 +16,16 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef STRINGS_H
#define STRINGS_H
#ifndef RJP_ARRAY_ELEMENT_H
#define RJP_ARRAY_ELEMENT_H
#include "rjp.h"
#include <stddef.h> //size_t
#include "rjp_internal.h"
#include "rjp_value.h"
int _rjp__is_whitespace(char c);
char* _rjp__parse_string(RJP_value* root, const char* str, int* inclen, int* len, int* row, int* column);
size_t _rjp__array_strlen(RJP_value* arr);
size_t _rjp__object_strlen(RJP_value* root);
size_t _rjp__value_strlen(RJP_value* root);
void _rjp__strcpy(RJP_string* dest, const RJP_string* src);
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,6 +1,6 @@
/**
rjp
Copyright (C) 2018-2019 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
@@ -20,6 +20,7 @@
#define RJP_INTERNAL_H
#include "rjp.h"
#include <stdio.h>
#ifdef __GNUC__
#define MAYBE_UNUSED __attribute__((unused))
@@ -27,27 +28,28 @@
#define MAYBE_UNUSED
#endif
#define UNUSED_VARIABLE(thing) (void)(thing)
#ifdef RJP_DIAGNOSTICS
#include <stdio.h>
#define DIAG_PRINT(...) fprintf(__VA_ARGS__)
#else
#define DIAG_PRINT(...)
#define DIAG_PRINT(...) irjp_ignore_unused(__VA_ARGS__)
static inline void irjp_ignore_unused(FILE* fp, ...){
UNUSED_VARIABLE(fp);
}
#endif
//
typedef struct RJP_array_element{
struct RJP_value value;
struct RJP_array_element* next;
}RJP_array_element;
//A member of an object
typedef struct RJP_object_member{
struct RJP_value value;
struct RJP_string name;
struct RJP_object_member* next;
}RJP_object_member;
void _rjp__add_element(RJP_array* j);
void _rjp__add_member(RJP_object* j, const char* str, size_t len);
void _rjp__add_member_no_alloc(RJP_object* j, char* str, size_t len);
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);
#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-2019 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
@@ -20,263 +20,359 @@
#include "rjp.h"
#include "rjp_internal.h"
#include "strings.h"
#include "rjp_value.h"
#include "rjp_string.h"
#include "memory.h"
#include <stdlib.h> //strtod, strtol
#include <stdio.h> //fprintf, stderr
#include <string.h> //memset
//types of searches in the text
typedef enum json_search_target{
json_key,
json_colon,
json_comma,
json_value,
json_none
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;
static RJP_value* _rjp__add_value(RJP_value* curr, RJP_value new_val){
static RJP_value* irjp_add_value(RJP_value* curr, RJP_value* lastadded, RJP_value new_val){
new_val.parent = curr;
if(!curr){
curr = rjp_calloc(1, sizeof(RJP_value));
*curr = new_val;
return curr;
}
if(curr->type == json_array){
_rjp__add_element(&curr->array);
curr->array.last->value = new_val;
return &curr->array.last->value;
if(curr->type == rjp_json_array){
RJP_value* last = rjp_add_element(curr);
rjp_move_value(last, &new_val);
return last;
}
curr->object.last->value = new_val;
return &curr->object.last->value;
*lastadded = new_val;
return lastadded;
}
#define syntax_error(msg, row, column)\
do{DIAG_PRINT(stderr, "Syntax error! %s (%i:%i)\n", msg, row, column);rjp_free_value(root);return NULL;}while(0)
#define MAX_DEPTH 16
RJP_value* rjp_parse(const char* str){
RJP_value* root = 0;
RJP_value* curr = 0;
int row = 1, column = 0;
int in_line_comment = 0;
int in_block_comment = 0;
//keep track of where we are in a given subobject
int state_stack[MAX_DEPTH] = {0},*top = state_stack;
typedef struct RJP_string_state{
int escaped;
int in_utf_sequence;
char* buffer; //store partial string here only when chunked reading and chunk ends mid string
}RJP_string_state;
typedef struct RJP_numeral_state{
int numlen;
char* buffer; //store partial number string here only when chunked reading and chunk ends mid number
}RJP_numeral_state;
typedef struct RJP_parse_state{
RJP_value* root;
RJP_value* curr;
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 = json_value;
*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 == json_key){
//start of key
if(c == '"'){
if(curr == NULL)
syntax_error("Key found outside of object definition!", row, column);
int keylen;
int inclen;
char* new_string = _rjp__parse_string(root, ++str, &inclen, &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 += inclen;
*top = json_colon;
//end of this object (object is empty)
}else if(c == '}'){
curr = curr->parent;
if(top != state_stack)
--top;
//unrecognized character
}else if(!_rjp__is_whitespace(c)){
syntax_error("Unexpected character, expected '\"'!", row, column);
switch(*state.target){
case 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 == json_colon){
//colon after a key
if(c == ':'){
*top = json_value;
//unrecognized character
}else if(!_rjp__is_whitespace(c)){
syntax_error( "Unexpected character, expected ':'!", row, column);
}
}
else if(*top == json_comma){
//comma separating keys in an object or values in an array
if(c == ','){
*top = (curr->type == json_array ? json_value : json_key);
//end of object
}else if(c == '}'){
if(curr->type == json_array){
syntax_error("Unexpected end of object within array!", row, column);
}
curr = curr->parent;
if(top != state_stack)
--top;
//end of array
}else if(c == ']' && curr->type == json_array){
curr = curr->parent;
//unrecognized character
}else if(!_rjp__is_whitespace(c)){
syntax_error("Unexpected character, expected ','!", row, column);
}
}
else if(*top == json_value){
//object
if(c == '{'){
if(!root){
root = _rjp__add_value(NULL, rjp_object());
curr = root;
*top = json_key;
}else{
curr = _rjp__add_value(curr, rjp_object());
*top = json_comma;
++top;
*top = json_key;
}
}
else if(c == '['){
if(!root){
root = _rjp__add_value(NULL, rjp_array());
curr = root;
}else{
curr = _rjp__add_value(curr, rjp_array());
}
}
else if(c == ']' && curr->type == json_array){ //empty array
*top = json_comma;
curr = curr->parent;
}
//strings
else if(c == '"'){
int vallen, inclen;
++str;
char* new_string = _rjp__parse_string(root, str, &inclen, &vallen, &row, &column);
if(!new_string){
if(vallen == 0){
new_string = rjp_calloc(1, 1);
}else{
return NULL;
}
}
_rjp__add_value(curr, rjp_string(new_string, vallen));
str += inclen;
*top = json_comma;
}
//numbers
else if((c >= '0' && c <= '9') || c == '-'){
if(!curr)
*top = json_none;
else
*top = json_comma;
int numlen;
int floating = 0; //is an int or a double
for(numlen = 1;*(str+numlen) >= '0' && *(str+numlen) <= '9';++numlen);
if(*(str+numlen) == '.'){ //if we have a decimal, make it a double and continue parsing as a number
int i = ++numlen;
for(;*(str+numlen) >= '0' && *(str+numlen) <= '9';++numlen);
if(i == numlen){ //no number after decimal
syntax_error("Missing numerals after decimal place!", row, column);
}
floating = 1;
}
if(*(str+numlen) == '\0' && curr){ //hit EOF early
syntax_error("Unexpected EOF before end of object!", row, column);
}
if(c == '-' && numlen == 1){ //only have a '-' with no numbers
syntax_error("Missing numerals ofter '-' sign!", row, column);
}
if(floating){
if(!root){
root = curr = _rjp__add_value(NULL, rjp_dfloat(strtod(str, NULL)));
}else{
_rjp__add_value(curr, rjp_dfloat(strtod(str, NULL)));
}
}else{
if(!root){
root = curr = _rjp__add_value(NULL, rjp_integer(strtol(str, NULL, 10)));
}else{
_rjp__add_value(curr, rjp_integer(strtol(str, NULL, 10)));
}
}
str += (numlen-1);
column += numlen;
}
//booleans and null
else if(!strncmp(str, "true", 4)){
if(!curr){
*top = json_none;
root = curr = _rjp__add_value(curr, rjp_boolean(1));
}else{
*top = json_comma;
_rjp__add_value(curr, rjp_boolean(1));
}
str += 3;column += 3;
}else if(!strncmp(str, "false", 5)){
if(!curr){
*top = json_none;
root = curr = _rjp__add_value(curr, rjp_boolean(0));
}else{
*top = json_comma;
_rjp__add_value(curr, rjp_boolean(0));
}
str += 4;column += 4;
}else if(!strncmp(str, "null", 4)){
if(!curr){
*top = json_none;
root = curr = _rjp__add_value(curr, rjp_null());
}else{
*top = json_comma;
_rjp__add_value(curr, rjp_null());
}
str += 3;column += 3;
}
//unrecognized character
else if(!_rjp__is_whitespace(c)){
syntax_error("Unexpected character!", row, column);
}
}else if(*top == json_none && !_rjp__is_whitespace(c)){
syntax_error("Unexpected character!", row, column);
}
inc = 1;
break;
default:
inc = 1;
break;
};
}
return root;
return state.root;
}
RJP_value* rjp_parse_chunked(const char* str, RJP_value* prev_chunk){
if(!prev_chunk){
return rjp_parse(str);
}
return NULL;
}
#undef syntax_error

View File

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

View File

@@ -1,6 +1,6 @@
/**
rjp
Copyright (C) 2018-2019 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,45 +16,151 @@
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 <stdio.h> //sprintf
RJP_value* rjp_init_json(void){
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_init_json_as(RJP_value value){
RJP_value* ret = rjp_alloc(sizeof(RJP_value));
*ret = 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_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_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_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_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_new_object(void){
RJP_value* ret = rjp_calloc(1, sizeof(RJP_value));
ret->type = rjp_json_object;
return ret;
}
RJP_value* rjp_new_array(void){
RJP_value* ret = rjp_calloc(1, sizeof(RJP_value));
ret->type = rjp_json_array;
return ret;
}
static size_t _rjp__write_value(char* dest, RJP_value* val){
size_t ret;
RJP_value* rjp_set_null(RJP_value* v){
irjp_delete_value(v);
v->type = rjp_json_null;
return v;
}
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_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_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;
}
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:
case rjp_json_array:
ret = rjp_dump_array(val, dest);
break;
case json_object:
case rjp_json_object:
ret = rjp_dump_object(val, dest);
break;
default:
@@ -63,16 +169,88 @@ static size_t _rjp__write_value(char* dest, RJP_value* val){
};
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);
size_t rjp_dump_array(RJP_value* arr, char* dest){
RJP_array* array = &arr->array;
RJP_array_element* element_list = array->elements;
size_t pos = 1;
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;
break;
};
return ret;
}
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(;element_list;element_list = element_list->next){
pos += _rjp__write_value(dest+pos, &element_list->value);
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;
@@ -81,35 +259,39 @@ size_t rjp_dump_array(RJP_value* arr, char* dest){
return pos;
}
size_t rjp_dump_object(RJP_value* root, char* dest){
RJP_object* root_obj = &root->object;
RJP_object_member* member_list = root_obj->members;
size_t pos = 1;
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, "{");
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);
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(member_list->next)
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__value_strlen(root);
RJP_index len = pretty ? irjp_value_strlen_pretty(root, 0) : irjp_value_strlen(root);
if(!len)
return NULL;
char* tmp = rjp_alloc(len + 1);
tmp[len] = 0;
_rjp__write_value(tmp, root);
if(pretty)
irjp_write_value_pretty(tmp, root, 0);
else
irjp_write_value(tmp, root);
return tmp;
}

320
src/rjp.c
View File

@@ -1,6 +1,6 @@
/**
rjp
Copyright (C) 2018-2019 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,218 +17,152 @@
*/
#include "rjp_internal.h"
#include "rjp_string.h"
#include "rjp_object.h"
#include "rjp_value.h"
#include <string.h> //strncpy, strlen
#include <stdlib.h> //free, malloc
void _rjp__add_element(RJP_array* j){
++j->num_elements;
if(!j->elements){
j->elements = rjp_calloc(1, sizeof(RJP_array_element));
j->last = j->elements;
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{
j->last->next = rjp_calloc(1, sizeof(RJP_array_element));
j->last = j->last->next;
ret = rjp_new_null();
}
}
//create member of the object as a linked list member and assign a name with name allocation
void _rjp__add_member(RJP_object* j, const char* str, size_t len){
++j->num_members;
if(!j->members){
j->members = rjp_calloc(1, sizeof(RJP_object_member));
j->last = j->members;
}else{
j->last->next = rjp_calloc(1, sizeof(RJP_object_member));
j->last = j->last->next;
}
j->last->name.value = rjp_alloc(len + 1);
strncpy(j->last->name.value, str, len);
j->last->name.value[len] = 0;
j->last->name.length = len;
}
void _rjp__add_member_no_alloc(RJP_object* j, char* str, size_t len){
++j->num_members;
if(!j->members){
j->members = rjp_calloc(1, sizeof(RJP_object_member));
j->last = j->members;
}else{
j->last->next = rjp_calloc(1, sizeof(RJP_object_member));
j->last = j->last->next;
}
j->last->name.value = str;
j->last->name.length = len;
}
ret->type = src->type;
RJP_value* rjp_add_member(RJP_value* dest, const char* key, size_t keylen, RJP_value value){
if(!keylen)
keylen = strlen(key);
_rjp__add_member(&dest->object, key, keylen);
dest->object.last->value = value;
dest->object.last->value.parent = dest;
return &dest->object.last->value;
}
RJP_value* rjp_add_member_no_alloc(RJP_value* dest, char* key, size_t keylen, RJP_value value){
if(!keylen)
keylen = strlen(key);
_rjp__add_member_no_alloc(&dest->object, key, keylen);
dest->object.last->value = value;
dest->object.last->value.parent = dest;
return &dest->object.last->value;
}
RJP_value* rjp_add_element(RJP_value* dest, RJP_value value){
_rjp__add_element(&dest->array);
dest->array.last->value = value;
dest->array.last->value.parent = dest;
return &dest->array.last->value;
}
void rjp_set_value(RJP_value* dest, RJP_value value){
struct RJP_value* p = dest->parent;
*dest = value;
dest->parent = p;
}
RJP_value rjp_integer(long i){
return (RJP_value){.integer = i, .type = json_integer};
}
RJP_value rjp_boolean(char b){
return (RJP_value){.boolean = b, .type = json_boolean};
}
RJP_value rjp_dfloat(double d){
return (RJP_value){.dfloat = d, .type = json_dfloat};
}
RJP_value rjp_string(char* c, size_t len){
return (RJP_value){.string = {.value = c, .length = len}, .type = json_string};
}
RJP_value rjp_null(void){
return (RJP_value){.integer = 0, .type = json_null};
}
RJP_value rjp_object(void){
return (RJP_value){.object = {0}, .type = json_object};
}
RJP_value rjp_array(void){
return (RJP_value){.array = {0}, .type = json_array};
}
RJP_value* rjp_get_member(const RJP_value* object){
return &object->object.members->value;
}
RJP_value* rjp_next_member(const RJP_value* member){
//polymorphism
return (RJP_value*)(((RJP_object_member*)member)->next);
}
size_t rjp_num_members(const RJP_value* object){
return object->object.num_members;
}
char* rjp_member_name(const RJP_value* member){
return ((RJP_object_member*)member)->name.value;
}
size_t rjp_member_name_length(RJP_value* member){
return ((RJP_object_member*)member)->name.length;
}
RJP_search_res rjp_search_member(const RJP_value* object, const char* search, size_t skip){
RJP_value* member = rjp_get_member(object);
size_t i = 0;
for(;i < skip && member;member = rjp_next_member(member),++i);
for(;member;member = rjp_next_member(member),++i){
if(!strcmp(((RJP_object_member*)member)->name.value, search)){
return (RJP_search_res){member, 0, i};
}
}
return (RJP_search_res){0};
}
size_t rjp_search_members(const RJP_value* object, size_t num, const char* const* searches, RJP_search_res* dest, size_t skip){
const char** tmp = rjp_alloc(num*sizeof(char*));
for(size_t i = 0;i < num;++i){
tmp[i] = searches[i];
dest[i] = (RJP_search_res){0};
}
RJP_value* member = rjp_get_member(object);
size_t objindex = 0;
for(;objindex < skip && member;member = rjp_next_member(member),++objindex);
size_t matches = 0;
for(;member;member = rjp_next_member(member),++objindex){
for(size_t i = 0;i < num;++i){
if(tmp[i] == NULL)
continue;
if(!strcmp(((RJP_object_member*)member)->name.value, tmp[i])){
dest[i].value = member;
dest[i].searchindex = i;
dest[i].objindex = objindex;
tmp[i] = NULL;
++matches;
break;
}
}
}
rjp_free(tmp);
return matches;
}
RJP_search_res* rjp_search_members_multi(const RJP_value* object, size_t num, const char* const* searches, size_t* rsize, size_t skip){
RJP_search_res* ret = rjp_alloc(num*sizeof(RJP_search_res));
size_t ret_size = num;
size_t j = 0;
RJP_value* member = rjp_get_member(object);
size_t objindex = 0;
for(;objindex < skip && member;member = rjp_next_member(member),++objindex);
for(;member;member = rjp_next_member(member),++objindex){
for(size_t i = 0;i < num;++i){
if(!strcmp(((RJP_object_member*)member)->name.value, searches[i])){
ret[j].value = member;
ret[j].searchindex = i;
ret[j].objindex = objindex;
++j;
if(j == ret_size){
ret_size *= 2;
ret = rjp_realloc(ret, ret_size*sizeof(RJP_search_res));
}
break;
}
}
}
if(rsize)
*rsize = j;
if(j == 0){
rjp_free(ret);
return NULL;
}
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_get_element(const RJP_value* array){
return &array->array.elements->value;
}
RJP_value* rjp_next_element(const RJP_value* element){
return (RJP_value*)(((RJP_array_element*)element)->next);
}
size_t rjp_num_elements(const RJP_value* array){
return array->array.num_elements;
RJP_value* rjp_move_value(RJP_value* dest, RJP_value* src){
*dest = *src;
return dest;
}
RJP_value* rjp_value_parent(RJP_value* value){
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_type rjp_value_type(RJP_value* value){
RJP_data_type rjp_value_type(const RJP_value* value){
return value->type;
}
double rjp_value_dfloat(RJP_value* value){
RJP_float rjp_get_float(const RJP_value* value){
return value->dfloat;
}
int rjp_value_integer(RJP_value* value){
RJP_int rjp_get_int(const RJP_value* value){
return value->integer;
}
char rjp_value_boolean(RJP_value* value){
RJP_bool rjp_get_bool(const RJP_value* value){
return value->boolean;
}
char* rjp_value_string(RJP_value* value){
return value->string.value;
RJP_string* rjp_get_string(RJP_value* value){
return &(value->string);
}
size_t rjp_value_string_length(RJP_value* value){
return value->string.length;
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;
}

View File

@@ -1,6 +1,6 @@
/**
rjp
Copyright (C) 2018-2019 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,8 +16,13 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "strings.h"
#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
@@ -25,13 +30,13 @@
#include <string.h> //strcpy
//Determine if the character is valid whitespace
int _rjp__is_whitespace(char c){
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(size_t i = 0;i < 4;++i){
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'){
@@ -116,7 +121,7 @@ static int codepoint_to_u8(char* dest, uint32_t codepoint){
return 0;
}
}
static uint32_t u8_to_codepoint(char* u){
/*static uint32_t u8_to_codepoint(char* u){
if((u[0] & 0x80) == 0){
//one byte
return u[0];
@@ -145,10 +150,10 @@ static uint32_t u8_to_codepoint(char* u){
//invalid
return 0;
}
}
}*/
//Convert escape sequences in strings
char* _rjp__parse_string(RJP_value* root, const char* str, int* inclen, int* len, int* row, int* column){
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;
@@ -228,9 +233,9 @@ char* _rjp__parse_string(RJP_value* root, const char* str, int* inclen, int* len
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){
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++] = '\\';
@@ -268,16 +273,16 @@ size_t rjp_escape_strcpy(char* dest, const char* src){
dest[j] = 0;
return j;
}
void _rjp__strcpy(RJP_string* dest, const RJP_string* src){
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;
}
size_t rjp_escape_strlen(const char* str){
size_t count = 0;
for(size_t i = 0;str[i];++i){
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':
@@ -294,75 +299,113 @@ size_t rjp_escape_strlen(const char* str){
}
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;
}
RJP_index irjp_array_strlen(const RJP_value* arr){
RJP_index count = 2; //[]
RJP_array_iterator it;
rjp_init_array_iterator(&it, arr);
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; //"":
count += _rjp__value_strlen(&member_list->value);
if(member_list->next)
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; //,
else
break;
}
return count;
}
size_t _rjp__value_strlen(RJP_value* root){
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 json_integer:
return snprintf(NULL, 0, "%li", root->integer);
case json_dfloat:
case rjp_json_integer:
return snprintf(NULL, 0, "%" PRId64, root->integer);
case rjp_json_dfloat:
return snprintf(NULL, 0, "%lf", root->dfloat);
case json_boolean:
case rjp_json_boolean:
return root->boolean ? 4 : 5; //true, false
case json_null:
case rjp_json_null:
return 4;
case json_string:
case rjp_json_string:
return rjp_escape_strlen(root->string.value) + 2; //"";
case json_array:
return _rjp__array_strlen(root);
case json_object:
return _rjp__object_strlen(root);
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;
};

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;
}