Compare commits
67 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e15c54b7cc | ||
|
|
092a6497f8 | ||
|
|
88cdc61357 | ||
|
|
4be1d9b2d0 | ||
|
|
250c6b1e59 | ||
|
|
08b05e8162 | ||
|
|
dac026802f | ||
|
|
656992233e | ||
|
|
6a9597c274 | ||
|
|
debd8cc31d | ||
|
|
67208e38e0 | ||
|
|
0dfbbb5a5c | ||
|
|
ff241e2a6f | ||
|
|
30cc8008af | ||
|
|
34c918615f | ||
|
|
b447a745a3 | ||
|
|
45ed17d6ca | ||
|
|
0b4562a95a | ||
|
|
b5fdb94fd3 | ||
|
|
ed5179c677 | ||
|
|
a646d1c4e8 | ||
|
|
6e53fe29e2 | ||
|
|
05250e6af9 | ||
|
|
faccd2f31a | ||
|
|
bc755098b3 | ||
|
|
ce1877f52a | ||
|
|
12388ac8f6 | ||
|
|
4cfd07df38 | ||
|
|
be71b9edb4 | ||
|
|
0b50b3b21a | ||
|
|
688dd22c73 | ||
|
|
c781550b09 | ||
|
|
0df7efabb7 | ||
|
|
3349468e07 | ||
|
|
339c3af4f4 | ||
|
|
25c3c018f6 | ||
|
|
f367993ad4 | ||
|
|
2d94783ee7 | ||
|
|
f9a36a5c37 | ||
|
|
d3c99152fc | ||
|
|
dc0c003785 | ||
|
|
4577836f8e | ||
|
|
50c7a055ec | ||
|
|
6771d00c6e | ||
|
|
71661cd9ad | ||
|
|
46b7b56852 | ||
|
|
fd7d9988b8 | ||
|
|
280e24a8e7 | ||
|
|
fa071dd034 | ||
|
|
25da591780 | ||
|
|
4b5586ffed | ||
|
|
6d89e4dc5a | ||
|
|
adad380228 | ||
|
|
e1813d718f | ||
|
|
83655214f0 | ||
|
|
20e46d2af2 | ||
|
|
383cb5af54 | ||
|
|
686541279f | ||
|
|
edcfee90eb | ||
|
|
dd3bfb49e9 | ||
|
|
af9b0b837e | ||
|
|
cd5ddd950b | ||
|
|
d68bea08f4 | ||
|
|
1445d668a5 | ||
|
|
6f4a55a766 | ||
|
|
b27031876b | ||
|
|
566952de5e |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -5,3 +5,6 @@ tester.exe
|
||||
*.swp
|
||||
build
|
||||
include/config.h
|
||||
makefile
|
||||
Makefile
|
||||
src/tester.c
|
||||
|
||||
@@ -4,7 +4,8 @@ include(GNUInstallDirs)
|
||||
cmake_minimum_required(VERSION 3.0.2)
|
||||
project(rjp)
|
||||
set(rjp_VERSION_MAJOR 0)
|
||||
set(rjp_VERSION_MINOR 2)
|
||||
set(rjp_VERSION_MINOR 8)
|
||||
set(rjp_VERSION_REVISION 2)
|
||||
set(INCLUDE_PATH ${CMAKE_SOURCE_DIR}/include)
|
||||
configure_file(
|
||||
"${INCLUDE_PATH}/config.h.in"
|
||||
@@ -12,11 +13,26 @@ configure_file(
|
||||
)
|
||||
include_directories("${INCLUDE_PATH}")
|
||||
|
||||
add_library (rjp STATIC src/input.c src/output.c src/strings.c)
|
||||
option(ENABLE_DIAGNOSTICS "Print diagnostic messages when parsing json to help identify issues" ON)
|
||||
option(ENABLE_SHARED "Build shared library" OFF)
|
||||
|
||||
set(SOURCE_LIST "src/input.c" "src/output.c" "src/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)
|
||||
target_compile_options(rjp PRIVATE -Wall -Wextra -pedantic)
|
||||
if(ENABLE_DIAGNOSTICS)
|
||||
target_compile_definitions(rjp PRIVATE RJP_DIAGNOSTICS)
|
||||
endif()
|
||||
|
||||
install(TARGETS rjp
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
||||
)
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ rjp (rexy's json parser) is a very simplistic json parser written in C. There is
|
||||
Currently only installs 2 files: librjp.a and rjp.h
|
||||
|
||||
## Building
|
||||
Building will produce a static library with no option to create shared because it's such a simple library.
|
||||
Building will produce a static library by default but can be configured to build a shared library.
|
||||
|
||||
##### Run the following commands
|
||||
```
|
||||
|
||||
6
TODO
Normal file
6
TODO
Normal 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
|
||||
9
doc/naming_convention.txt
Normal file
9
doc/naming_convention.txt
Normal 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
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
rjp
|
||||
Copyright (C) 2018 rexy712
|
||||
Copyright (C) 2018-2020 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -16,11 +16,12 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef CONFIG_H
|
||||
#define CONFIG_H
|
||||
#ifndef RJP_CONFIG_H
|
||||
#define RJP_CONFIG_H
|
||||
|
||||
#define RJP_VERSION_MAJOR @rjp_VERSION_MAJOR@
|
||||
#define RJP_VERSION_MINOR @rjp_VERSION_MINOR@
|
||||
#define RJP_VERSION_REVISION @rjp_VERSION_REVISION@
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
248
include/rjp.h
248
include/rjp.h
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
rjp
|
||||
Copyright (C) 2018 rexy712
|
||||
Copyright (C) 2018-2020 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -19,116 +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
|
||||
enum JSON_type{
|
||||
json_object,
|
||||
json_string,
|
||||
json_integer,
|
||||
json_dfloat,
|
||||
json_boolean,
|
||||
json_array,
|
||||
json_null
|
||||
};
|
||||
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;
|
||||
|
||||
enum JSON_flags{
|
||||
json_flag_string = 1, //in a string
|
||||
json_flag_comment = 2, //in a comment block
|
||||
json_flag_search_name = 4, //looking for key/value pair
|
||||
json_flag_escape = 16 //escaped character in a string
|
||||
};
|
||||
|
||||
//keep track of string length
|
||||
struct JSON_string{
|
||||
typedef struct RJP_value RJP_value;
|
||||
|
||||
//Hold a C string and the length excluding NULL terminator
|
||||
typedef struct RJP_string{
|
||||
char* value;
|
||||
size_t length;
|
||||
};
|
||||
RJP_index length;
|
||||
}RJP_string;
|
||||
|
||||
//keep a linked list of all members of a given object
|
||||
struct JSON_object{
|
||||
struct JSON_object_member* members;
|
||||
struct JSON_object_member* last;
|
||||
size_t num_members;
|
||||
};
|
||||
//Forward declarations
|
||||
struct RJP_object_iterator_impl;
|
||||
typedef struct RJP_object_iterator{
|
||||
struct RJP_object_iterator_impl* it;
|
||||
}RJP_object_iterator;
|
||||
|
||||
//keep array plus length
|
||||
struct JSON_array{
|
||||
struct JSON_array_element* elements;
|
||||
struct JSON_array_element* last;
|
||||
size_t num_elements;
|
||||
};
|
||||
|
||||
//hold any json data type
|
||||
struct JSON_value{
|
||||
union{
|
||||
long integer;
|
||||
double dfloat;
|
||||
char boolean;
|
||||
struct JSON_object object;
|
||||
struct JSON_string string;
|
||||
struct JSON_array array;
|
||||
};
|
||||
struct JSON_value* parent;
|
||||
enum JSON_type type;
|
||||
};
|
||||
|
||||
struct JSON_array_element{
|
||||
struct JSON_value value;
|
||||
struct JSON_array_element* next;
|
||||
};
|
||||
|
||||
//A member of an object
|
||||
struct JSON_object_member{
|
||||
struct JSON_string name;
|
||||
struct JSON_value value;
|
||||
struct JSON_object_member* next;
|
||||
};
|
||||
|
||||
//read in json and convert to easy to work with format
|
||||
struct JSON_value* rjp_parse(const char* str);
|
||||
|
||||
//deallocate a json handle
|
||||
void rjp_free_json(char* json);
|
||||
void rjp_free(struct JSON_value* root);
|
||||
|
||||
struct JSON_object_member* rjp_get_members(struct JSON_object* j);
|
||||
struct JSON_object_member* rjp_next_member(struct JSON_object_member* j);
|
||||
struct JSON_object* rjp_member_parent(struct JSON_object_member* j);
|
||||
struct JSON_value* rjp_get_member_value(struct JSON_object_member* j);
|
||||
enum JSON_type rjp_get_member_type(struct JSON_object_member* j);
|
||||
enum JSON_type rjp_get_value_type(struct JSON_value* j);
|
||||
typedef struct RJP_array_iterator{
|
||||
RJP_value* current;
|
||||
}RJP_array_iterator;
|
||||
|
||||
|
||||
//initialize an empty json handle (outputting to string would result in '{}')
|
||||
struct JSON_value* rjp_init_json(void);
|
||||
|
||||
//add a member to a json object
|
||||
struct JSON_value* rjp_add_member(struct JSON_value* dest, int alloc_key, char* key, size_t keylen, struct JSON_value value);
|
||||
/***************** NON OBJECT OPERATIONS *******************/
|
||||
//Allocate heap space for rjp to use. Use if the memory is to be freed by rjp
|
||||
void* rjp_alloc(RJP_index nbytes);
|
||||
//Allocate heap space for rjp and initialize to 0.
|
||||
void* rjp_calloc(RJP_index num, RJP_index nbytes);
|
||||
//Resize heap allocated space for rjp
|
||||
void* rjp_realloc(void* ptr, RJP_index nbytes);
|
||||
//Free memory allocated by rjp_alloc or rjp_calloc
|
||||
void rjp_free(void* dest);
|
||||
|
||||
//copy input string and add json escape sequences
|
||||
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
|
||||
//initialize a RJP_value to the requested type and value
|
||||
RJP_value* rjp_set_null(RJP_value* v);
|
||||
RJP_value* rjp_set_int(RJP_value* v, RJP_int val);
|
||||
RJP_value* rjp_set_float(RJP_value* v, RJP_float val);
|
||||
RJP_value* rjp_set_bool(RJP_value* v, RJP_bool val);
|
||||
RJP_value* rjp_set_string_copy(RJP_value* v, const char* val, RJP_index length);
|
||||
RJP_value* rjp_set_string(RJP_value* v, char* val, RJP_index length);
|
||||
RJP_value* rjp_set_object(RJP_value* v);
|
||||
RJP_value* rjp_set_array(RJP_value* v);
|
||||
|
||||
RJP_float rjp_get_float(const RJP_value* value);
|
||||
RJP_int rjp_get_int(const RJP_value* value);
|
||||
RJP_bool rjp_get_bool(const RJP_value* value);
|
||||
RJP_string* rjp_get_string(RJP_value* value);
|
||||
const RJP_string* rjp_get_cstring(const RJP_value* value);
|
||||
|
||||
|
||||
/***************** OBJECT/ARRAY SHARED OPERATIONS *******************/
|
||||
RJP_value* rjp_value_parent(const RJP_value* element);
|
||||
//Return type of value
|
||||
RJP_data_type rjp_value_type(const RJP_value* value);
|
||||
|
||||
/***************** OBJECT OPERATIONS *******************/
|
||||
//add a member to a json object, allocating a copy of the key
|
||||
RJP_value* rjp_add_member_key_copy(RJP_value* dest, const char* key, RJP_index keylen);
|
||||
//add a member to a json object without allocation. key must be allocated using rjp_alloc/rjp_calloc
|
||||
RJP_value* rjp_add_member(RJP_value* dest, char* key, RJP_index keylen);
|
||||
|
||||
//Remove member without freeing
|
||||
RJP_value* rjp_remove_member_by_key(RJP_value* obj, const char* key);
|
||||
RJP_value* rjp_remove_member(RJP_value* obj, RJP_value* member);
|
||||
//Remove and free member
|
||||
void rjp_free_member_by_key(RJP_value* obj, const char* key);
|
||||
void rjp_free_member(RJP_value* obj, RJP_value* member);
|
||||
|
||||
//set existing object member's key without allocation. key must be allocated using rjp_alloc/rjp_calloc
|
||||
void rjp_set_key(RJP_value* dest, char* key, RJP_index keylen);
|
||||
//set existing object member's key
|
||||
void rjp_set_key_copy(RJP_value* dest, const char* key, RJP_index keylen);
|
||||
|
||||
//Return number of members in the object
|
||||
RJP_index rjp_num_members(const RJP_value* object);
|
||||
//Return the object member's key
|
||||
const RJP_string* rjp_member_key(const RJP_value* member);
|
||||
//Search for an object member with given key
|
||||
RJP_value* rjp_search_member(const RJP_value* object, const char* search);
|
||||
|
||||
/***************** OBJECT ITERATOR OPERATIONS *******************/
|
||||
//Access first member of a json object
|
||||
void rjp_init_object_iterator(RJP_object_iterator* iter, const RJP_value* object);
|
||||
void rjp_delete_object_iterator(RJP_object_iterator* it);
|
||||
RJP_value* rjp_object_iterator_current(const RJP_object_iterator* it);
|
||||
RJP_value* rjp_object_iterator_next(RJP_object_iterator* it);
|
||||
RJP_value* rjp_object_iterator_peek(const RJP_object_iterator* it);
|
||||
|
||||
/***************** ARRAY OPERATIONS *******************/
|
||||
//add an element to a json array
|
||||
struct JSON_value* rjp_add_element(struct JSON_value* dest, struct JSON_value value);
|
||||
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);
|
||||
|
||||
struct JSON_value rjp_integer(long i);
|
||||
struct JSON_value rjp_boolean(char b);
|
||||
struct JSON_value rjp_dfloat(double d);
|
||||
struct JSON_value rjp_string(char* c, size_t len);
|
||||
struct JSON_value rjp_null(void);
|
||||
struct JSON_value rjp_object(void);
|
||||
struct JSON_value rjp_array(void);
|
||||
RJP_index rjp_num_elements(const RJP_value* arr);
|
||||
|
||||
size_t rjp_escape_strcpy(char* dest, const char* src);
|
||||
size_t rjp_escape_strlen(const char* str);
|
||||
/***************** ARRAY ITERATOR OPERATIONS *******************/
|
||||
void rjp_init_array_iterator(RJP_array_iterator* iter, const RJP_value* array);
|
||||
void rjp_delete_array_iterator(RJP_array_iterator* iter);
|
||||
RJP_value* rjp_array_iterator_current(const RJP_array_iterator* it);
|
||||
RJP_value* rjp_array_iterator_next(RJP_array_iterator* it);
|
||||
RJP_value* rjp_array_iterator_peek(const RJP_array_iterator* it);
|
||||
|
||||
size_t rjp_dump_object(struct JSON_value* root, char* dest);
|
||||
size_t rjp_dump_array(struct JSON_value* arr, char* dest);
|
||||
char* rjp_to_json(struct JSON_value* root);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#undef DEPRECATED
|
||||
|
||||
#endif
|
||||
|
||||
39
include/rjp_array.h
Normal file
39
include/rjp_array.h
Normal 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
|
||||
31
include/rjp_array_element.h
Normal file
31
include/rjp_array_element.h
Normal file
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
rjp
|
||||
Copyright (C) 2018-2020 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef RJP_ARRAY_ELEMENT_H
|
||||
#define RJP_ARRAY_ELEMENT_H
|
||||
|
||||
#include "rjp_internal.h"
|
||||
#include "rjp_value.h"
|
||||
|
||||
typedef struct RJP_array_element{
|
||||
struct RJP_value value;
|
||||
struct RJP_array_element* next;
|
||||
struct RJP_array_element* prev;
|
||||
}RJP_array_element;
|
||||
|
||||
#endif
|
||||
@@ -1,13 +1,55 @@
|
||||
/**
|
||||
rjp
|
||||
Copyright (C) 2018-2020 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef RJP_INTERNAL_H
|
||||
#define RJP_INTERNAL_H
|
||||
|
||||
#include "rjp.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define MAYBE_UNUSED __attribute__((unused))
|
||||
#else
|
||||
#define MAYBE_UNUSED
|
||||
#endif
|
||||
|
||||
#define UNUSED_VARIABLE(thing) (void)(thing)
|
||||
|
||||
#ifdef RJP_DIAGNOSTICS
|
||||
#define DIAG_PRINT(...) fprintf(__VA_ARGS__)
|
||||
#else
|
||||
#define DIAG_PRINT(...) irjp_ignore_unused(__VA_ARGS__)
|
||||
|
||||
static inline void irjp_ignore_unused(FILE* fp, ...){
|
||||
UNUSED_VARIABLE(fp);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
RJP_value irjp_integer(RJP_int i);
|
||||
RJP_value irjp_boolean(RJP_bool b);
|
||||
RJP_value irjp_dfloat(RJP_float d);
|
||||
RJP_value irjp_string(char* c, RJP_index len);
|
||||
RJP_value irjp_string_copy(const char* c);
|
||||
RJP_value irjp_null(void);
|
||||
RJP_value irjp_object(void);
|
||||
RJP_value irjp_array(void);
|
||||
|
||||
char* _rjp__parse_string(struct JSON_value* root, const char* str, int* len, int* row, int* column);
|
||||
size_t _rjp__object_strlen(struct JSON_value* root);
|
||||
void _rjp__add_member(struct JSON_object* j, char* str, size_t len);
|
||||
void _rjp__add_member_no_alloc(struct JSON_object* j, char* str, size_t len);
|
||||
void _rjp__add_element(struct JSON_array* j);
|
||||
size_t _rjp__object_strlen(struct JSON_value* root);
|
||||
|
||||
#endif
|
||||
|
||||
36
include/rjp_object.h
Normal file
36
include/rjp_object.h
Normal 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
|
||||
30
include/rjp_object_member.h
Normal file
30
include/rjp_object_member.h
Normal 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
32
include/rjp_string.h
Normal 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
42
include/rjp_value.h
Normal 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
51
include/tree.h
Normal 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
|
||||
710
src/input.c
710
src/input.c
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
rjp
|
||||
Copyright (C) 2018 rexy712
|
||||
Copyright (C) 2018-2020 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -17,412 +17,362 @@
|
||||
*/
|
||||
|
||||
//TODO: Scientific notation
|
||||
//TODO: \e escape sequence in strings
|
||||
|
||||
#include "rjp.h"
|
||||
#include "rjp_internal.h"
|
||||
#include <string.h> //strncpy
|
||||
#include <stdlib.h> //malloc, calloc, free
|
||||
#include "rjp_value.h"
|
||||
#include "rjp_string.h"
|
||||
#include "memory.h"
|
||||
#include <stdlib.h> //strtod, strtol
|
||||
#include <stdio.h> //fprintf, stderr
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define MAYBE_UNUSED __attribute__((unused))
|
||||
#else
|
||||
#define MAYBE_UNUSED
|
||||
#endif
|
||||
|
||||
struct JSON_state{
|
||||
int in_array;
|
||||
int search_target;
|
||||
};
|
||||
#include <string.h> //memset
|
||||
|
||||
//types of searches in the text
|
||||
enum json_search_target{
|
||||
json_key,
|
||||
json_colon,
|
||||
json_comma,
|
||||
json_value
|
||||
};
|
||||
typedef enum rjp_json_search_target{
|
||||
rjp_json_target_key,
|
||||
rjp_json_target_colon,
|
||||
rjp_json_target_comma,
|
||||
rjp_json_target_value,
|
||||
rjp_json_target_string,
|
||||
rjp_json_target_numeral,
|
||||
rjp_json_target_none
|
||||
}json_search_target;
|
||||
|
||||
//Determine if the character is valid whitespace
|
||||
static int _rjp__is_whitespace(char c){
|
||||
return c == ' ' || c == '\n' || c == '\r' || c == '\t';
|
||||
}
|
||||
//add an element to an array
|
||||
void _rjp__add_element(struct JSON_array* j){
|
||||
++j->num_elements;
|
||||
if(!j->elements){
|
||||
j->elements = calloc(1, sizeof(struct JSON_array_element));
|
||||
j->last = j->elements;
|
||||
}else{
|
||||
j->last->next = calloc(1, sizeof(struct JSON_array_element));
|
||||
j->last = j->last->next;
|
||||
}
|
||||
}
|
||||
//create member of the object as a linked list member and assign a name with name allocation
|
||||
void _rjp__add_member(struct JSON_object* j, char* str, size_t len){
|
||||
++j->num_members;
|
||||
if(!j->members){
|
||||
j->members = calloc(1, sizeof(struct JSON_object_member));
|
||||
j->last = j->members;
|
||||
}else{
|
||||
j->last->next = calloc(1, sizeof(struct JSON_object_member));
|
||||
j->last = j->last->next;
|
||||
}
|
||||
j->last->name.value = malloc(len + 1);
|
||||
strncpy(j->last->name.value, str, len);
|
||||
j->last->name.value[len] = 0;
|
||||
j->last->name.length = len;
|
||||
}
|
||||
void _rjp__add_member_no_alloc(struct JSON_object* j, char* str, size_t len){
|
||||
++j->num_members;
|
||||
if(!j->members){
|
||||
j->members = calloc(1, sizeof(struct JSON_object_member));
|
||||
j->last = j->members;
|
||||
}else{
|
||||
j->last->next = calloc(1, sizeof(struct JSON_object_member));
|
||||
j->last = j->last->next;
|
||||
}
|
||||
j->last->name.value = str;
|
||||
j->last->name.length = len;
|
||||
}
|
||||
|
||||
static struct JSON_value* _rjp__add_element_to_array(struct JSON_value* curr, struct JSON_state* state, struct JSON_value* element){
|
||||
struct JSON_value* tmp = &curr->object.members->value;
|
||||
for(int i = state->in_array-1;i > 0;--i, tmp = &tmp->array.last->value);
|
||||
_rjp__add_element(&tmp->array);
|
||||
tmp->array.last->value = *element;
|
||||
return &tmp->array.last->value;
|
||||
}
|
||||
|
||||
//Assign object characteristics to previously allocated JSON_value
|
||||
static struct JSON_value* _rjp__add_object(struct JSON_value* curr, struct JSON_state* state){
|
||||
static RJP_value* irjp_add_value(RJP_value* curr, RJP_value* lastadded, RJP_value new_val){
|
||||
new_val.parent = curr;
|
||||
if(!curr){
|
||||
curr = calloc(1, sizeof(struct JSON_value));
|
||||
curr->type = json_object;
|
||||
curr = rjp_calloc(1, sizeof(RJP_value));
|
||||
*curr = new_val;
|
||||
return curr;
|
||||
}
|
||||
struct JSON_value new_object = {.type = json_object, .integer = 0, .parent = curr};
|
||||
if(state->in_array)
|
||||
return _rjp__add_element_to_array(curr, state, &new_object);
|
||||
curr->object.last->value = new_object;
|
||||
return &curr->object.last->value;
|
||||
}
|
||||
//Assign array characteristics to previously allocated JSON_value
|
||||
static struct JSON_value* _rjp__add_array(struct JSON_value* curr, struct JSON_state* state){
|
||||
struct JSON_value new_array = {.type = json_array, .array = {.num_elements = 0, .elements = NULL}, .parent = curr};
|
||||
if(state->in_array)
|
||||
return _rjp__add_element_to_array(curr, state, &new_array);
|
||||
curr->object.last->value = new_array;
|
||||
return &curr->object.last->value;
|
||||
}
|
||||
//Assign string characteristics to previously allocated JSON_value with no string allocation
|
||||
static struct JSON_value* _rjp__add_string_no_alloc(struct JSON_value* curr, char* str, int len, struct JSON_state* state){
|
||||
struct JSON_value new_string = {.type = json_string, .string = {.value = str, .length = len}, .parent = curr};
|
||||
if(state->in_array)
|
||||
return _rjp__add_element_to_array(curr, state, &new_string);
|
||||
curr->object.last->value = new_string;
|
||||
return &curr->object.last->value;
|
||||
}
|
||||
//Assign double characteristics to previously allocated JSON_value
|
||||
static struct JSON_value* _rjp__add_dfloat(struct JSON_value* curr, double value, struct JSON_state* state){
|
||||
struct JSON_value new_double = {.type = json_dfloat, .dfloat = value, .parent = curr};
|
||||
if(state->in_array)
|
||||
return _rjp__add_element_to_array(curr, state, &new_double);
|
||||
curr->object.last->value = new_double;
|
||||
return &curr->object.last->value;
|
||||
}
|
||||
//Assign integer characteristics to previously allocated JSON_value
|
||||
static struct JSON_value* _rjp__add_integer(struct JSON_value* curr, long value, struct JSON_state* state){
|
||||
struct JSON_value new_integer = {.type = json_integer, .integer = value, .parent = curr};
|
||||
if(state->in_array)
|
||||
return _rjp__add_element_to_array(curr, state, &new_integer);
|
||||
curr->object.last->value = new_integer;
|
||||
return &curr->object.last->value;
|
||||
}
|
||||
static struct JSON_value* _rjp__add_boolean(struct JSON_value* curr, int value, struct JSON_state* state){
|
||||
struct JSON_value new_boolean = {.type = json_boolean, .boolean = value, .parent = curr};
|
||||
if(state->in_array)
|
||||
return _rjp__add_element_to_array(curr, state, &new_boolean);
|
||||
curr->object.last->value = new_boolean;
|
||||
return &curr->object.last->value;
|
||||
}
|
||||
static struct JSON_value* _rjp__add_null(struct JSON_value* curr, struct JSON_state* state){
|
||||
struct JSON_value new_null = {.type = json_null, .integer = 0, .parent = curr};
|
||||
if(state->in_array)
|
||||
return _rjp__add_element_to_array(curr, state, &new_null);
|
||||
curr->object.last->value = new_null;
|
||||
return &curr->object.last->value;
|
||||
}
|
||||
|
||||
static void _rjp__free_object_recurse(struct JSON_value* root);
|
||||
|
||||
static void _rjp__free_array(struct JSON_value* root){
|
||||
struct JSON_array_element* arr = root->array.elements;
|
||||
for(struct JSON_array_element* i = arr;i != NULL;i = arr){
|
||||
arr = arr->next;
|
||||
if(i->value.type == json_object){
|
||||
_rjp__free_object_recurse(&i->value);
|
||||
}else if(i->value.type == json_array){
|
||||
_rjp__free_array(&i->value);
|
||||
}else if(i->value.type == json_string){
|
||||
free(i->value.string.value);
|
||||
}
|
||||
free(i);
|
||||
if(curr->type == rjp_json_array){
|
||||
RJP_value* last = rjp_add_element(curr);
|
||||
rjp_move_value(last, &new_val);
|
||||
return last;
|
||||
}
|
||||
*lastadded = new_val;
|
||||
return lastadded;
|
||||
}
|
||||
//Recursively free JSON objects
|
||||
static void _rjp__free_object_recurse(struct JSON_value* root){
|
||||
struct JSON_object_member* next;
|
||||
for(struct JSON_object_member* m = root->object.members;m;m = next){
|
||||
next = m->next;
|
||||
if(m->value.type == json_object)
|
||||
_rjp__free_object_recurse(&m->value);
|
||||
else if(m->value.type == json_string)
|
||||
free(m->value.string.value);
|
||||
else if(m->value.type == json_array)
|
||||
_rjp__free_array(&m->value);
|
||||
if(m->name.value)
|
||||
free(m->name.value);
|
||||
free(m);
|
||||
}
|
||||
}
|
||||
void rjp_free_json(char* json){
|
||||
free(json);
|
||||
}
|
||||
//Same as recurse but also frees root node
|
||||
void rjp_free(struct JSON_value* root){
|
||||
if(!root)
|
||||
return;
|
||||
_rjp__free_object_recurse(root);
|
||||
free(root);
|
||||
}
|
||||
|
||||
MAYBE_UNUSED static int _rjp__is_array_empty(struct JSON_value* curr){
|
||||
if(curr->object.members->value.type != json_array)
|
||||
return 0;
|
||||
return curr->object.members->value.array.num_elements == 0;
|
||||
}
|
||||
|
||||
#define syntax_error(msg, row, column)\
|
||||
do{fprintf(stderr, "Syntax error! %s (%i:%i)\n", msg, row, column);rjp_free(root);return NULL;}while(0)
|
||||
|
||||
#define MAX_DEPTH 16
|
||||
struct JSON_value* rjp_parse(const char* str){
|
||||
struct JSON_value* root = 0;
|
||||
struct JSON_value* curr = 0;
|
||||
int row = 1, column = 0;
|
||||
int in_line_comment = 0;
|
||||
int in_block_comment = 0;
|
||||
|
||||
//keep track of where we are in a given subobject
|
||||
struct JSON_state state_stack[MAX_DEPTH] = {0},*top = state_stack;
|
||||
typedef struct RJP_string_state{
|
||||
int escaped;
|
||||
int in_utf_sequence;
|
||||
char* buffer; //store partial string here only when chunked reading and chunk ends mid string
|
||||
}RJP_string_state;
|
||||
|
||||
typedef struct RJP_numeral_state{
|
||||
int numlen;
|
||||
char* buffer; //store partial number string here only when chunked reading and chunk ends mid number
|
||||
}RJP_numeral_state;
|
||||
|
||||
typedef struct RJP_parse_state{
|
||||
RJP_value* root;
|
||||
RJP_value* curr;
|
||||
RJP_value* lastadded;
|
||||
union{
|
||||
RJP_string_state str_state;
|
||||
RJP_numeral_state num_state;
|
||||
};
|
||||
int row, column;
|
||||
int in_line_comment;
|
||||
int in_block_comment;
|
||||
int target_stack[MAX_DEPTH];
|
||||
int* target;
|
||||
}RJP_parse_state;
|
||||
|
||||
void irjp_init_parse_state(RJP_parse_state* state){
|
||||
state->root = NULL;
|
||||
state->curr = NULL;
|
||||
state->row = state->column = 0;
|
||||
state->in_line_comment = 0;
|
||||
state->in_block_comment = 0;
|
||||
memset(state->target_stack, 0, MAX_DEPTH*sizeof(int));
|
||||
state->target = state->target_stack;
|
||||
}
|
||||
|
||||
static void syntax_error(const char* msg, RJP_parse_state* state){
|
||||
DIAG_PRINT(stderr, "Syntax error! %s (%i:%i)\n", msg, state->row, state->column);
|
||||
rjp_free_value(state->root);
|
||||
}
|
||||
|
||||
//Return number of characters handled while processing comment
|
||||
int irjp_handle_comment(const char* str, RJP_parse_state* state){
|
||||
char c = *str;
|
||||
if(state->in_line_comment){
|
||||
if(c == '\n')
|
||||
state->in_line_comment = 0;
|
||||
return 1;
|
||||
}else if(state->in_block_comment){
|
||||
if(c == '*' && *(str+1) == '/'){
|
||||
state->in_block_comment = 0;
|
||||
return 2;
|
||||
}
|
||||
return 1;
|
||||
}else if(c == '/' && *(str+1) == '*'){
|
||||
state->in_block_comment = 1;
|
||||
return 2;
|
||||
}else if(c == '/' && *(str+1) == '/'){
|
||||
state->in_line_comment = 1;
|
||||
return 2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
int irjp_handle_key(const char* str, RJP_parse_state* state){
|
||||
char c = *str;
|
||||
//start of key
|
||||
if(c == '"'){
|
||||
if(state->curr == NULL){
|
||||
syntax_error("Key found outside of object definition!", state);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int keylen;
|
||||
int inclen;
|
||||
char* new_string = irjp_parse_string(state->root, str+1, &inclen, &keylen, &state->row, &state->column);
|
||||
if(!new_string){
|
||||
if(!keylen)
|
||||
syntax_error("Cannot have empty key name!", state);
|
||||
return -1;
|
||||
}
|
||||
state->lastadded = rjp_add_member(state->curr, new_string, keylen);
|
||||
rjp_set_null(state->lastadded);
|
||||
*state->target = rjp_json_target_colon;
|
||||
return inclen+2;
|
||||
//end of this object (object is empty)
|
||||
}else if(c == '}'){
|
||||
state->curr = state->curr->parent;
|
||||
if(state->target != state->target_stack)
|
||||
--state->target;
|
||||
return 1;
|
||||
|
||||
//unrecognized character
|
||||
}else if(!irjp_is_whitespace(c)){
|
||||
syntax_error("Unexpected character, expected '\"'!", state);
|
||||
return -1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int irjp_handle_colon(const char* str, RJP_parse_state* state){
|
||||
char c = *str;
|
||||
//colon after a key
|
||||
if(c == ':'){
|
||||
*state->target = rjp_json_target_value;
|
||||
//unrecognized character
|
||||
}else if(!irjp_is_whitespace(c)){
|
||||
syntax_error( "Unexpected character, expected ':'!", state);
|
||||
return -1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
int irjp_handle_comma(const char* str, RJP_parse_state* state){
|
||||
char c = *str;
|
||||
//comma separating keys in an object or values in an array
|
||||
if(c == ','){
|
||||
*state->target = (state->curr->type == rjp_json_array ? rjp_json_target_value : rjp_json_target_key);
|
||||
|
||||
//end of object
|
||||
}else if(c == '}'){
|
||||
if(state->curr->type == rjp_json_array){
|
||||
syntax_error("Unexpected end of object within array!", state);
|
||||
return -1;
|
||||
}
|
||||
state->curr = state->curr->parent;
|
||||
if(state->target != state->target_stack)
|
||||
--state->target;
|
||||
//end of array
|
||||
}else if(c == ']' && state->curr->type == rjp_json_array){
|
||||
state->curr = state->curr->parent;
|
||||
//unrecognized character
|
||||
}else if(!irjp_is_whitespace(c)){
|
||||
syntax_error("Unexpected character, expected ','!", state);
|
||||
return -1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int irjp_handle_value(const char* str, RJP_parse_state* state){
|
||||
//object
|
||||
char c = *str;
|
||||
if(c == '{'){
|
||||
if(!state->root){
|
||||
state->root = irjp_add_value(NULL, NULL, irjp_object());
|
||||
state->curr = state->root;
|
||||
*state->target = rjp_json_target_key;
|
||||
}else{
|
||||
state->curr = irjp_add_value(state->curr, state->lastadded, irjp_object());
|
||||
*state->target = rjp_json_target_comma;
|
||||
++state->target;
|
||||
*state->target = rjp_json_target_key;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
else if(c == '['){
|
||||
if(!state->root){
|
||||
state->root = irjp_add_value(NULL, NULL, irjp_array());
|
||||
state->curr = state->root;
|
||||
|
||||
}else{
|
||||
state->curr = irjp_add_value(state->curr, state->lastadded, irjp_array());
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
else if(c == ']' && state->curr->type == rjp_json_array){ //empty array
|
||||
*state->target = rjp_json_target_comma;
|
||||
state->curr = state->curr->parent;
|
||||
return 1;
|
||||
}
|
||||
//strings
|
||||
else if(c == '"'){
|
||||
int vallen, inclen;
|
||||
char* new_string = irjp_parse_string(state->root, str+1, &inclen, &vallen, &state->row, &state->column);
|
||||
if(!new_string){
|
||||
if(vallen == 0){
|
||||
new_string = rjp_calloc(1, 1);
|
||||
}else{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
irjp_add_value(state->curr, state->lastadded, irjp_string(new_string, vallen));
|
||||
*state->target = rjp_json_target_comma;
|
||||
return inclen+2;
|
||||
}
|
||||
//numbers
|
||||
else if((c >= '0' && c <= '9') || c == '-'){
|
||||
if(!state->curr)
|
||||
*state->target = rjp_json_target_none;
|
||||
else
|
||||
*state->target = rjp_json_target_comma;
|
||||
int numlen;
|
||||
int floating = 0; //is an int or a double
|
||||
for(numlen = 1;*(str+numlen) >= '0' && *(str+numlen) <= '9';++numlen);
|
||||
if(*(str+numlen) == '.'){ //if we have a decimal, make it a double and continue parsing as a number
|
||||
int i = ++numlen;
|
||||
for(;*(str+numlen) >= '0' && *(str+numlen) <= '9';++numlen);
|
||||
if(i == numlen){ //no number after decimal
|
||||
syntax_error("Missing numerals after decimal place!", state);
|
||||
return -1;
|
||||
}
|
||||
floating = 1;
|
||||
}
|
||||
if(*(str+numlen) == '\0' && state->curr){ //hit EOF early
|
||||
syntax_error("Unexpected EOF before end of object!", state);
|
||||
return -1;
|
||||
}
|
||||
if(c == '-' && numlen == 1){ //only have a '-' with no numbers
|
||||
syntax_error("Missing numerals after '-' sign!", state);
|
||||
return -1;
|
||||
}
|
||||
if(floating){
|
||||
if(!state->root){
|
||||
state->root = state->curr = irjp_add_value(NULL, NULL, irjp_dfloat(strtod(str, NULL)));
|
||||
}else{
|
||||
irjp_add_value(state->curr, state->lastadded, irjp_dfloat(strtod(str, NULL)));
|
||||
}
|
||||
}else{
|
||||
if(!state->root){
|
||||
state->root = state->curr = irjp_add_value(NULL, NULL, irjp_integer(strtoll(str, NULL, 10)));
|
||||
}else{
|
||||
irjp_add_value(state->curr, state->lastadded, irjp_integer(strtoll(str, NULL, 10)));
|
||||
}
|
||||
}
|
||||
state->column += numlen;
|
||||
return numlen;
|
||||
}
|
||||
//booleans and null
|
||||
else if(!strncmp(str, "true", 4)){
|
||||
if(!state->curr){
|
||||
*state->target = rjp_json_target_none;
|
||||
state->root = state->curr = irjp_add_value(state->curr, NULL, irjp_boolean(1));
|
||||
}else{
|
||||
*state->target = rjp_json_target_comma;
|
||||
irjp_add_value(state->curr, state->lastadded, irjp_boolean(1));
|
||||
}
|
||||
state->column += 3;
|
||||
return 4;
|
||||
}else if(!strncmp(str, "false", 5)){
|
||||
if(!state->curr){
|
||||
*state->target = rjp_json_target_none;
|
||||
state->root = state->curr = irjp_add_value(state->curr, NULL, irjp_boolean(0));
|
||||
}else{
|
||||
*state->target = rjp_json_target_comma;
|
||||
irjp_add_value(state->curr, state->lastadded, irjp_boolean(0));
|
||||
}
|
||||
state->column += 4;
|
||||
return 5;
|
||||
}else if(!strncmp(str, "null", 4)){
|
||||
if(!state->curr){
|
||||
*state->target = rjp_json_target_none;
|
||||
state->root = state->curr = irjp_add_value(state->curr, NULL, irjp_null());
|
||||
}else{
|
||||
*state->target = rjp_json_target_comma;
|
||||
irjp_add_value(state->curr, state->lastadded, irjp_null());
|
||||
}
|
||||
state->column += 3;
|
||||
return 4;
|
||||
}
|
||||
//unrecognized character
|
||||
else if(!irjp_is_whitespace(c)){
|
||||
syntax_error("Unexpected character!", state);
|
||||
return -1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
RJP_value* rjp_parse(const char* str){
|
||||
RJP_parse_state state;
|
||||
irjp_init_parse_state(&state);
|
||||
|
||||
//initially search for the root object
|
||||
top->search_target = json_value;
|
||||
top->in_array = 0;
|
||||
*state.target = rjp_json_target_value;
|
||||
|
||||
for(;*str != '\0';++str){
|
||||
int inc = 0;
|
||||
for(;*str != '\0';str += inc){
|
||||
char c = *str;
|
||||
|
||||
//keep track of position in input file
|
||||
if(c == '\n'){
|
||||
++row;
|
||||
column = 0;
|
||||
++state.row;
|
||||
state.column = 0;
|
||||
}else{
|
||||
++column;
|
||||
++state.column;
|
||||
}
|
||||
|
||||
//Handle comments
|
||||
if(in_line_comment){
|
||||
if(c == '\n')
|
||||
in_line_comment = 0;
|
||||
}
|
||||
else if(in_block_comment){
|
||||
if(c == '*' && *(str+1) == '/'){
|
||||
in_block_comment = 0;
|
||||
++str;
|
||||
}
|
||||
}
|
||||
else if(c == '/' && *(str+1) == '/'){
|
||||
in_line_comment = 1;
|
||||
++str;
|
||||
}
|
||||
else if(c == '/' && *(str+1) == '*'){
|
||||
in_block_comment = 1;
|
||||
++str;
|
||||
if((inc = irjp_handle_comment(str, &state))){
|
||||
continue;
|
||||
}
|
||||
|
||||
else if(top->search_target == json_key){
|
||||
//start of key
|
||||
if(c == '"'){
|
||||
if(curr == NULL)
|
||||
syntax_error("Key found outside of object definition!", row, column);
|
||||
|
||||
int keylen;
|
||||
char* new_string = _rjp__parse_string(root, ++str, &keylen, &row, &column);
|
||||
if(!new_string){
|
||||
if(!keylen)
|
||||
syntax_error("Cannot have empty key name!", row, column);
|
||||
return NULL;
|
||||
}
|
||||
_rjp__add_member_no_alloc(&curr->object, new_string, keylen);
|
||||
str += keylen;
|
||||
top->search_target = json_colon;
|
||||
//end of this object (object is empty)
|
||||
}else if(c == '}'){
|
||||
curr = curr->parent;
|
||||
if(top != state_stack)
|
||||
--top;
|
||||
|
||||
//unrecognized character
|
||||
}else if(!_rjp__is_whitespace(c)){
|
||||
syntax_error("Unexpected character, expected '\"'!", row, column);
|
||||
switch(*state.target){
|
||||
case rjp_json_target_key:
|
||||
inc = irjp_handle_key(str, &state);
|
||||
break;
|
||||
case rjp_json_target_colon:
|
||||
inc = irjp_handle_colon(str, &state);
|
||||
break;
|
||||
case rjp_json_target_comma:
|
||||
inc = irjp_handle_comma(str, &state);
|
||||
break;
|
||||
case rjp_json_target_value:
|
||||
inc = irjp_handle_value(str, &state);
|
||||
break;
|
||||
case rjp_json_target_none:
|
||||
if(!irjp_is_whitespace(*str)){
|
||||
syntax_error("Unexpected character!", &state);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else if(top->search_target == json_colon){
|
||||
//colon after a key
|
||||
if(c == ':'){
|
||||
top->search_target = json_value;
|
||||
//unrecognized character
|
||||
}else if(!_rjp__is_whitespace(c)){
|
||||
syntax_error( "Unexpected character, expected ':'!", row, column);
|
||||
}
|
||||
}
|
||||
else if(top->search_target == json_comma){
|
||||
//comma separating keys in an object or values in an array
|
||||
if(c == ','){
|
||||
top->search_target = top->in_array ? json_value : json_key;
|
||||
|
||||
//end of object
|
||||
}else if(c == '}'){
|
||||
if(top->in_array){
|
||||
syntax_error("Unexpected end of object within array!", row, column);
|
||||
}
|
||||
curr = curr->parent;
|
||||
if(top != state_stack)
|
||||
--top;
|
||||
//end of array
|
||||
}else if(c == ']' && top->in_array){
|
||||
--top->in_array;
|
||||
//unrecognized character
|
||||
}else if(!_rjp__is_whitespace(c)){
|
||||
syntax_error("Unexpected character, expected ','!", row, column);
|
||||
}
|
||||
}
|
||||
else if(top->search_target == json_value){
|
||||
//object
|
||||
if(c == '{'){
|
||||
if(!root){
|
||||
root = _rjp__add_object(NULL, top);
|
||||
curr = root;
|
||||
top->search_target = json_key;
|
||||
}else{
|
||||
curr = _rjp__add_object(curr, top);
|
||||
top->search_target = json_comma;
|
||||
++top;
|
||||
top->in_array = 0;
|
||||
top->search_target = json_key;
|
||||
}
|
||||
}
|
||||
//something outside of any object
|
||||
else if(!curr){
|
||||
syntax_error("Unexpected character outside of object definition!", row, column);
|
||||
}
|
||||
//arrays
|
||||
else if(c == '['){
|
||||
_rjp__add_array(curr, top);
|
||||
++top->in_array;
|
||||
}
|
||||
else if(c == ']' && top->in_array){ //empty array
|
||||
--top->in_array;
|
||||
top->search_target = json_comma;
|
||||
}
|
||||
//strings
|
||||
else if(c == '"'){
|
||||
int vallen;
|
||||
++str;
|
||||
char* new_string = _rjp__parse_string(root, str, &vallen, &row, &column);
|
||||
if(!new_string){
|
||||
if(vallen == 0){
|
||||
new_string = calloc(1, 1);
|
||||
}else{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
_rjp__add_string_no_alloc(curr, new_string, vallen, top);
|
||||
str += vallen;
|
||||
top->search_target = json_comma;
|
||||
}
|
||||
//numbers
|
||||
else if((c >= '0' && c <= '9') || c == '-'){
|
||||
int numlen;
|
||||
int floating = 0; //is an int or a double
|
||||
for(numlen = 1;*(str+numlen) >= '0' && *(str+numlen) <= '9';++numlen);
|
||||
if(*(str+numlen) == '.'){ //if we have a decimal, make it a double and continue parsing as a number
|
||||
int i = ++numlen;
|
||||
for(;*(str+numlen) >= '0' && *(str+numlen) <= '9';++numlen);
|
||||
if(i == numlen){ //no number after decimal
|
||||
syntax_error("Missing numerals after decimal place!", row, column);
|
||||
}
|
||||
floating = 1;
|
||||
}
|
||||
if(*(str+numlen) == '\0'){ //hit EOF early
|
||||
syntax_error("Unexpected EOF before end of object!", row, column);
|
||||
}
|
||||
if(c == '-' && numlen == 1){ //only have a '-' with no numbers
|
||||
syntax_error("Missing numerals ofter '-' sign!", row, column);
|
||||
}
|
||||
if(floating){
|
||||
_rjp__add_dfloat(curr, strtod(str, NULL), top);
|
||||
}else{
|
||||
_rjp__add_integer(curr, strtol(str, NULL, 10), top);
|
||||
}
|
||||
str += (numlen-1);
|
||||
column += numlen;
|
||||
top->search_target = json_comma;
|
||||
}
|
||||
//booleans and null
|
||||
else if(!strncmp(str, "true", 4)){
|
||||
_rjp__add_boolean(curr, 1, top);
|
||||
str += 3;
|
||||
top->search_target = json_comma;
|
||||
}else if(!strncmp(str, "false", 5)){
|
||||
_rjp__add_boolean(curr, 0, top);
|
||||
str += 4;
|
||||
top->search_target = json_comma;
|
||||
}else if(!strncmp(str, "null", 4)){
|
||||
_rjp__add_null(curr, top);
|
||||
str += 3;
|
||||
top->search_target = json_comma;
|
||||
}
|
||||
//unrecognized character
|
||||
else if(!_rjp__is_whitespace(c)){
|
||||
syntax_error("Unexpected character!", row, column);
|
||||
}
|
||||
}
|
||||
inc = 1;
|
||||
break;
|
||||
default:
|
||||
inc = 1;
|
||||
break;
|
||||
};
|
||||
}
|
||||
return root;
|
||||
return state.root;
|
||||
}
|
||||
|
||||
#undef syntax_error
|
||||
|
||||
//TODO
|
||||
struct JSON_object_member* rjp_get_members(struct JSON_object* j){
|
||||
return j->members;
|
||||
}
|
||||
struct JSON_object_member* rjp_next_member(struct JSON_object_member* j){
|
||||
return j->next;
|
||||
}
|
||||
struct JSON_object* rjp_member_parent(struct JSON_object_member* j){
|
||||
return &j->value.parent->object;
|
||||
}
|
||||
struct JSON_value* rjp_get_member_value(struct JSON_object_member* j){
|
||||
return &j->value;
|
||||
}
|
||||
enum JSON_type rjp_get_member_type(struct JSON_object_member* j){
|
||||
return j->value.type;
|
||||
}
|
||||
enum JSON_type rjp_get_value_type(struct JSON_value* j){
|
||||
return j->type;
|
||||
RJP_value* rjp_parse_chunked(const char* str, RJP_value* prev_chunk){
|
||||
if(!prev_chunk){
|
||||
return rjp_parse(str);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
313
src/output.c
313
src/output.c
@@ -1,82 +1,239 @@
|
||||
/**
|
||||
rjp
|
||||
Copyright (C) 2018-2020 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "rjp.h"
|
||||
#include "rjp_internal.h"
|
||||
#include "memory.h"
|
||||
#include "rjp_string.h"
|
||||
#include "rjp_object.h"
|
||||
#include "rjp_value.h"
|
||||
|
||||
#include <string.h> //strlen
|
||||
#include <stdlib.h> //malloc, calloc, free
|
||||
#include <stdio.h> //sprintf
|
||||
|
||||
struct JSON_value* rjp_init_json(void){
|
||||
struct JSON_value* ret = calloc(1, sizeof(struct JSON_value));
|
||||
ret->type = json_object;
|
||||
RJP_value* rjp_new_null(void){
|
||||
RJP_value* ret = rjp_calloc(1, sizeof(RJP_value));
|
||||
ret->type = rjp_json_null;
|
||||
return ret;
|
||||
}
|
||||
struct JSON_value* rjp_add_member(struct JSON_value* dest, int alloc_key, char* key, size_t keylen, struct JSON_value value){
|
||||
if(!keylen && alloc_key)
|
||||
keylen = strlen(key);
|
||||
if(alloc_key)
|
||||
_rjp__add_member(&dest->object, key, keylen);
|
||||
else
|
||||
_rjp__add_member_no_alloc(&dest->object, key, keylen);
|
||||
dest->object.last->value = value;
|
||||
dest->object.last->value.parent = dest;
|
||||
return &dest->object.last->value;
|
||||
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;
|
||||
}
|
||||
struct JSON_value* rjp_add_element(struct JSON_value* dest, struct JSON_value value){
|
||||
_rjp__add_element(&dest->array);
|
||||
dest->array.last->value = value;
|
||||
dest->array.last->value.parent = dest;
|
||||
return &dest->array.last->value;
|
||||
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;
|
||||
}
|
||||
struct JSON_value rjp_integer(long i){
|
||||
return (struct JSON_value){.integer = i, .type = json_integer};
|
||||
RJP_value* rjp_new_bool(RJP_bool val){
|
||||
RJP_value* ret = rjp_calloc(1, sizeof(RJP_value));
|
||||
ret->type = rjp_json_boolean;
|
||||
ret->boolean = val;
|
||||
return ret;
|
||||
}
|
||||
struct JSON_value rjp_boolean(char b){
|
||||
return (struct JSON_value){.boolean = b, .type = json_boolean};
|
||||
RJP_value* rjp_new_string(char* val, RJP_index length){
|
||||
RJP_value* ret = rjp_calloc(1, sizeof(RJP_value));
|
||||
ret->type = rjp_json_string;
|
||||
ret->string.value = val;
|
||||
ret->string.length = length;
|
||||
return ret;
|
||||
}
|
||||
struct JSON_value rjp_dfloat(double d){
|
||||
return (struct JSON_value){.dfloat = d, .type = json_dfloat};
|
||||
RJP_value* rjp_new_string_copy(const char* value, RJP_index length){
|
||||
UNUSED_VARIABLE(length);
|
||||
RJP_value* ret = rjp_calloc(1, sizeof(RJP_value));
|
||||
ret->type = rjp_json_string;
|
||||
RJP_index esclen = rjp_escape_strlen(value);
|
||||
ret->string.value = rjp_alloc(esclen+1);
|
||||
rjp_escape_strcpy(ret->string.value, value);
|
||||
ret->string.value[esclen] = 0;
|
||||
ret->string.length = esclen;
|
||||
return ret;
|
||||
}
|
||||
struct JSON_value rjp_string(char* c, size_t len){
|
||||
return (struct JSON_value){.string = {.value = c, .length = len}, .type = json_string};
|
||||
RJP_value* rjp_new_object(void){
|
||||
RJP_value* ret = rjp_calloc(1, sizeof(RJP_value));
|
||||
ret->type = rjp_json_object;
|
||||
return ret;
|
||||
}
|
||||
struct JSON_value rjp_null(void){
|
||||
return (struct JSON_value){.integer = 0, .type = json_null};
|
||||
}
|
||||
struct JSON_value rjp_object(void){
|
||||
return (struct JSON_value){.object = {0}, .type = json_object};
|
||||
}
|
||||
struct JSON_value rjp_array(void){
|
||||
return (struct JSON_value){.array = {0}, .type = json_array};
|
||||
RJP_value* rjp_new_array(void){
|
||||
RJP_value* ret = rjp_calloc(1, sizeof(RJP_value));
|
||||
ret->type = rjp_json_array;
|
||||
return 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 size_t _rjp__write_value(char* dest, struct JSON_value* val){
|
||||
size_t ret;
|
||||
static RJP_index irjp_write_value(char* dest, const RJP_value* val){
|
||||
RJP_index ret;
|
||||
switch(val->type){
|
||||
case json_integer:
|
||||
ret = sprintf(dest, "%li", val->integer);
|
||||
case rjp_json_integer:
|
||||
ret = sprintf(dest, "%" PRId64, val->integer);
|
||||
break;
|
||||
case json_dfloat:
|
||||
case rjp_json_dfloat:
|
||||
ret = sprintf(dest, "%lf", val->dfloat);
|
||||
break;
|
||||
case json_boolean:
|
||||
case rjp_json_boolean:
|
||||
ret = sprintf(dest, val->boolean ? "true" : "false");
|
||||
break;
|
||||
case json_null:
|
||||
case rjp_json_null:
|
||||
ret = sprintf(dest, "null");
|
||||
break;
|
||||
case json_string:;
|
||||
case rjp_json_string:;
|
||||
ret = sprintf(dest, "\"%s\"", val->string.value);
|
||||
break;
|
||||
case json_array:
|
||||
ret = sprintf(dest, "[");
|
||||
ret += rjp_dump_array(val, dest+ret);
|
||||
ret += sprintf(dest+ret, "]");
|
||||
case rjp_json_array:
|
||||
ret = rjp_dump_array(val, dest);
|
||||
break;
|
||||
case json_object:
|
||||
ret = sprintf(dest, "{");
|
||||
ret += rjp_dump_object(val, dest+ret);
|
||||
ret += sprintf(dest+ret, "}");
|
||||
case rjp_json_object:
|
||||
ret = rjp_dump_object(val, dest);
|
||||
break;
|
||||
default:
|
||||
ret = 0;
|
||||
break;
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
static RJP_index irjp_write_value_pretty(char* dest, const RJP_value* val, int depth);
|
||||
static RJP_index irjp_dump_array_pretty(const RJP_value* arr, char* dest, int depth){
|
||||
RJP_array_iterator it;
|
||||
rjp_init_array_iterator(&it, arr);
|
||||
RJP_index pos = 2;
|
||||
sprintf(dest, "[\n");
|
||||
for(RJP_value* current = rjp_array_iterator_current(&it);current;current = rjp_array_iterator_next(&it)){
|
||||
for(int i = 0;i < (depth+1);++i)
|
||||
pos += sprintf(dest+pos, "\t");
|
||||
pos += irjp_write_value_pretty(dest+pos, current, depth+1);
|
||||
|
||||
if(rjp_array_iterator_peek(&it))
|
||||
pos += sprintf(dest+pos, ",");
|
||||
pos += sprintf(dest+pos, "\n");
|
||||
}
|
||||
for(int i = 0;i < depth;++i)
|
||||
pos += sprintf(dest+pos, "\t");
|
||||
pos += sprintf(dest+pos, "]");
|
||||
return pos;
|
||||
}
|
||||
static RJP_index irjp_dump_object_pretty(const RJP_value* root, char* dest, int depth){
|
||||
RJP_object_iterator it;
|
||||
rjp_init_object_iterator(&it, root);
|
||||
RJP_index pos = 2;
|
||||
sprintf(dest, "{\n");
|
||||
|
||||
for(RJP_value* current = rjp_object_iterator_current(&it);current;current = rjp_object_iterator_next(&it)){
|
||||
for(int i = 0;i < (depth+1);++i)
|
||||
pos += sprintf(dest+pos, "\t");
|
||||
pos += sprintf(dest+pos, "\"%s\": ", rjp_member_key(current)->value);
|
||||
pos += irjp_write_value_pretty(dest+pos, current, depth+1);
|
||||
|
||||
if(rjp_object_iterator_peek(&it))
|
||||
pos += sprintf(dest+pos, ",");
|
||||
pos += sprintf(dest+pos, "\n");
|
||||
}
|
||||
for(int i = 0;i < depth;++i)
|
||||
pos += sprintf(dest+pos, "\t");
|
||||
pos += sprintf(dest+pos, "}");
|
||||
rjp_delete_object_iterator(&it);
|
||||
return pos;
|
||||
}
|
||||
static RJP_index irjp_write_value_pretty(char* dest, const RJP_value* val, int depth){
|
||||
RJP_index ret;
|
||||
switch(val->type){
|
||||
case rjp_json_integer:
|
||||
ret = sprintf(dest, "%" PRId64, val->integer);
|
||||
break;
|
||||
case rjp_json_dfloat:
|
||||
ret = sprintf(dest, "%lf", val->dfloat);
|
||||
break;
|
||||
case rjp_json_boolean:
|
||||
ret = sprintf(dest, val->boolean ? "true" : "false");
|
||||
break;
|
||||
case rjp_json_null:
|
||||
ret = sprintf(dest, "null");
|
||||
break;
|
||||
case rjp_json_string:;
|
||||
ret = sprintf(dest, "\"%s\"", val->string.value);
|
||||
break;
|
||||
case rjp_json_array:
|
||||
ret = irjp_dump_array_pretty(val, dest, depth);
|
||||
break;
|
||||
case rjp_json_object:
|
||||
ret = irjp_dump_object_pretty(val, dest, depth);
|
||||
break;
|
||||
default:
|
||||
ret = 0;
|
||||
@@ -85,46 +242,56 @@ static size_t _rjp__write_value(char* dest, struct JSON_value* val){
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t rjp_dump_array(struct JSON_value* arr, char* dest){
|
||||
struct JSON_array* array = &arr->array;
|
||||
struct JSON_array_element* element_list = array->elements;
|
||||
size_t pos = 0;
|
||||
for(;element_list;element_list = element_list->next){
|
||||
pos += _rjp__write_value(dest+pos, &element_list->value);
|
||||
RJP_index rjp_dump_array(const RJP_value* arr, char* dest){
|
||||
RJP_array_iterator it;
|
||||
rjp_init_array_iterator(&it, arr);
|
||||
RJP_index pos = 1;
|
||||
sprintf(dest, "[");
|
||||
for(RJP_value* current = rjp_array_iterator_current(&it);current;current = rjp_array_iterator_next(&it)){
|
||||
pos += irjp_write_value(dest+pos, current);
|
||||
|
||||
if(element_list->next)
|
||||
if(rjp_array_iterator_peek(&it))
|
||||
pos += sprintf(dest+pos, ",");
|
||||
else
|
||||
break;
|
||||
}
|
||||
pos += sprintf(dest+pos, "]");
|
||||
return pos;
|
||||
}
|
||||
|
||||
size_t rjp_dump_object(struct JSON_value* root, char* dest){
|
||||
struct JSON_object* root_obj = &root->object;
|
||||
struct JSON_object_member* member_list = root_obj->members;
|
||||
size_t pos = 0;
|
||||
for(;member_list;member_list = member_list->next){
|
||||
pos += sprintf(dest+pos, "\"%s\":", member_list->name.value);
|
||||
pos += _rjp__write_value(dest+pos, &member_list->value);
|
||||
RJP_index rjp_dump_object(const RJP_value* root, char* dest){
|
||||
RJP_object_iterator it;
|
||||
rjp_init_object_iterator(&it, root);
|
||||
RJP_index pos = 1;
|
||||
sprintf(dest, "{");
|
||||
|
||||
if(member_list->next)
|
||||
for(RJP_value* current = rjp_object_iterator_current(&it);current;current = rjp_object_iterator_next(&it)){
|
||||
pos += sprintf(dest+pos, "\"%s\":", rjp_member_key(current)->value);
|
||||
pos += irjp_write_value(dest+pos, current);
|
||||
|
||||
if(rjp_object_iterator_peek(&it))
|
||||
pos += sprintf(dest+pos, ",");
|
||||
else
|
||||
break;
|
||||
}
|
||||
pos += sprintf(dest+pos, "}");
|
||||
rjp_delete_object_iterator(&it);
|
||||
return pos;
|
||||
}
|
||||
|
||||
char* rjp_to_json(struct JSON_value* root){
|
||||
size_t len = _rjp__object_strlen(root);
|
||||
char* tmp = malloc(len + 1);
|
||||
char* rjp_to_json(const RJP_value* root, int pretty){
|
||||
if(!root)
|
||||
return NULL;
|
||||
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;
|
||||
size_t pos = 1;
|
||||
|
||||
sprintf(tmp, "{");
|
||||
pos += rjp_dump_object(root, tmp+pos);
|
||||
sprintf(tmp+pos, "}");
|
||||
if(pretty)
|
||||
irjp_write_value_pretty(tmp, root, 0);
|
||||
else
|
||||
irjp_write_value(tmp, root);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
|
||||
168
src/rjp.c
Normal file
168
src/rjp.c
Normal file
@@ -0,0 +1,168 @@
|
||||
/**
|
||||
rjp
|
||||
Copyright (C) 2018-2020 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "rjp_internal.h"
|
||||
#include "rjp_string.h"
|
||||
#include "rjp_object.h"
|
||||
#include "rjp_value.h"
|
||||
|
||||
#include <stdlib.h> //free, malloc
|
||||
|
||||
void* rjp_alloc(RJP_index nbytes){
|
||||
return malloc(nbytes);
|
||||
}
|
||||
void* rjp_calloc(RJP_index num, RJP_index nbytes){
|
||||
return calloc(num, nbytes);
|
||||
}
|
||||
void* rjp_realloc(void* ptr, RJP_index nbytes){
|
||||
return realloc(ptr, nbytes);
|
||||
}
|
||||
void rjp_free(void* data){
|
||||
free(data);
|
||||
}
|
||||
|
||||
RJP_value* rjp_copy_value(RJP_value* dest, const RJP_value* src){
|
||||
RJP_value* ret;
|
||||
if(dest){
|
||||
irjp_delete_value(dest);
|
||||
ret = dest;
|
||||
}else{
|
||||
ret = rjp_new_null();
|
||||
}
|
||||
ret->type = src->type;
|
||||
|
||||
switch(src->type){
|
||||
case rjp_json_object:
|
||||
irjp_copy_object(ret, src);
|
||||
break;
|
||||
case rjp_json_array:
|
||||
irjp_copy_array(ret, src);
|
||||
break;
|
||||
case rjp_json_string:
|
||||
irjp_strcpy(&ret->string, &src->string);
|
||||
break;
|
||||
case rjp_json_integer:
|
||||
ret->integer = src->integer;
|
||||
break;
|
||||
case rjp_json_boolean:
|
||||
ret->boolean = src->boolean;
|
||||
break;
|
||||
case rjp_json_dfloat:
|
||||
ret->dfloat = src->dfloat;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
RJP_value* rjp_move_value(RJP_value* dest, RJP_value* src){
|
||||
*dest = *src;
|
||||
return dest;
|
||||
}
|
||||
|
||||
static void irjp_delete_array(RJP_value* root){
|
||||
RJP_array_iterator it;
|
||||
rjp_init_array_iterator(&it, root);
|
||||
RJP_value* next = rjp_array_iterator_current(&it);
|
||||
|
||||
for(RJP_value* current = next;current;current = next){
|
||||
next = rjp_array_iterator_next(&it);
|
||||
switch(current->type){
|
||||
case rjp_json_object:
|
||||
irjp_delete_object(current);
|
||||
break;
|
||||
case rjp_json_array:
|
||||
irjp_delete_array(current);
|
||||
break;
|
||||
case rjp_json_string:
|
||||
free(current->string.value);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
free(current);
|
||||
}
|
||||
}
|
||||
|
||||
void irjp_delete_value(RJP_value* root){
|
||||
if((root->type) == rjp_json_object)
|
||||
irjp_delete_object(root);
|
||||
else if((root->type) == rjp_json_array)
|
||||
irjp_delete_array(root);
|
||||
else if((root->type) == rjp_json_string)
|
||||
rjp_free(root->string.value);
|
||||
}
|
||||
void rjp_free_value(RJP_value* root){
|
||||
if(!root)
|
||||
return;
|
||||
irjp_delete_value(root);
|
||||
free(root);
|
||||
}
|
||||
|
||||
/* VALUE SETTING */
|
||||
/* GETTERS */
|
||||
RJP_value* rjp_value_parent(const RJP_value* value){
|
||||
return value->parent;
|
||||
}
|
||||
RJP_data_type rjp_value_type(const RJP_value* value){
|
||||
return value->type;
|
||||
}
|
||||
RJP_float rjp_get_float(const RJP_value* value){
|
||||
return value->dfloat;
|
||||
}
|
||||
RJP_int rjp_get_int(const RJP_value* value){
|
||||
return value->integer;
|
||||
}
|
||||
RJP_bool rjp_get_bool(const RJP_value* value){
|
||||
return value->boolean;
|
||||
}
|
||||
RJP_string* rjp_get_string(RJP_value* value){
|
||||
return &(value->string);
|
||||
}
|
||||
const RJP_string* rjp_get_cstring(const RJP_value* value){
|
||||
return &(value->string);
|
||||
}
|
||||
|
||||
|
||||
RJP_value irjp_integer(RJP_int i){
|
||||
return (RJP_value){.integer = i, .type = rjp_json_integer};
|
||||
}
|
||||
RJP_value irjp_boolean(RJP_bool b){
|
||||
return (RJP_value){.boolean = b, .type = rjp_json_boolean};
|
||||
}
|
||||
RJP_value irjp_dfloat(RJP_float d){
|
||||
return (RJP_value){.dfloat = d, .type = rjp_json_dfloat};
|
||||
}
|
||||
RJP_value irjp_string(char* c, RJP_index len){
|
||||
return (RJP_value){.string = {.value = c, .length = len}, .type = rjp_json_string};
|
||||
}
|
||||
RJP_value irjp_string_copy(const char* c){
|
||||
RJP_index esclen = rjp_escape_strlen(c);
|
||||
char* tmp = rjp_alloc(esclen+1);
|
||||
rjp_escape_strcpy(tmp, c);
|
||||
return (RJP_value){.string = {.value = tmp, .length = esclen}, .type = rjp_json_string};
|
||||
}
|
||||
RJP_value irjp_null(void){
|
||||
return (RJP_value){.integer = 0, .type = rjp_json_null};
|
||||
}
|
||||
RJP_value irjp_object(void){
|
||||
return (RJP_value){.object = {0}, .type = rjp_json_object};
|
||||
}
|
||||
RJP_value irjp_array(void){
|
||||
return (RJP_value){.array = {0}, .type = rjp_json_array};
|
||||
}
|
||||
94
src/rjp_array.c
Normal file
94
src/rjp_array.c
Normal 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
242
src/rjp_object.c
Normal file
@@ -0,0 +1,242 @@
|
||||
/**
|
||||
rjp
|
||||
Copyright (C) 2018-2020 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "rjp_internal.h"
|
||||
#include "tree.h"
|
||||
#include "rjp_object.h"
|
||||
#include "rjp_object_member.h"
|
||||
|
||||
#include <string.h> //strlen, strncpy
|
||||
|
||||
#define RJP_TREE_ITERATOR_STACK_START_SIZE 32
|
||||
|
||||
typedef struct RJP_tree_stack{
|
||||
RJP_tree_node** data;
|
||||
int size;
|
||||
int pos;
|
||||
}RJP_tree_stack;
|
||||
|
||||
typedef struct RJP_object_iterator_impl{
|
||||
RJP_tree_stack stack;
|
||||
}RJP_object_iterator_impl;
|
||||
|
||||
static int irjp_init_tree_stack(RJP_tree_stack* stack);
|
||||
static void irjp_delete_tree_stack(RJP_tree_stack* stack);
|
||||
static void irjp_resize_tree_stack(RJP_tree_stack* stack);
|
||||
static void irjp_push_tree_stack(RJP_tree_stack* stack, RJP_tree_node* value);
|
||||
static RJP_tree_node* irjp_pop_tree_stack(RJP_tree_stack* stack);
|
||||
static RJP_tree_node* irjp_peek_tree_stack(RJP_tree_stack* stack);
|
||||
|
||||
static int irjp_init_object_iterator(RJP_object_iterator_impl* it, const RJP_tree_node* root);
|
||||
static void irjp_delete_object_iterator(RJP_object_iterator_impl* it);
|
||||
static RJP_tree_node* irjp_object_iterator_next(RJP_object_iterator_impl* it);
|
||||
static RJP_tree_node* irjp_object_iterator_peek(RJP_object_iterator_impl* it);
|
||||
static RJP_tree_node* irjp_object_iterator_current(RJP_object_iterator_impl* it);
|
||||
|
||||
/* TREE STACK */
|
||||
//Stack construction / destruction
|
||||
static int irjp_init_tree_stack(RJP_tree_stack* stack){
|
||||
stack->data = rjp_alloc(sizeof(RJP_tree_node*)*RJP_TREE_ITERATOR_STACK_START_SIZE);
|
||||
stack->size = RJP_TREE_ITERATOR_STACK_START_SIZE;
|
||||
stack->pos = 0;
|
||||
return 0;
|
||||
}
|
||||
static void irjp_delete_tree_stack(RJP_tree_stack* stack){
|
||||
rjp_free(stack->data);
|
||||
}
|
||||
static void irjp_resize_tree_stack(RJP_tree_stack* stack){
|
||||
int newsize = stack->size*2;
|
||||
RJP_tree_node** newdata = rjp_alloc(sizeof(RJP_tree_node*)*newsize);
|
||||
for(int i = 0;i < stack->size;++i){
|
||||
newdata[i] = stack->data[i];
|
||||
}
|
||||
rjp_free(stack->data);
|
||||
stack->data = newdata;
|
||||
stack->size = newsize;
|
||||
}
|
||||
//Stack operations
|
||||
static void irjp_push_tree_stack(RJP_tree_stack* stack, RJP_tree_node* value){
|
||||
stack->data[stack->pos++] = value;
|
||||
if(stack->pos == stack->size)
|
||||
irjp_resize_tree_stack(stack);
|
||||
}
|
||||
static RJP_tree_node* irjp_pop_tree_stack(RJP_tree_stack* stack){
|
||||
return stack->data[--stack->pos];
|
||||
}
|
||||
static RJP_tree_node* irjp_peek_tree_stack(RJP_tree_stack* stack){
|
||||
return (stack->pos > 0) ? (stack->data[stack->pos-1]) : NULL;
|
||||
}
|
||||
static RJP_tree_node* irjp_double_peek_tree_stack(RJP_tree_stack* stack){
|
||||
return (stack->pos > 1) ? (stack->data[stack->pos-2]) : NULL;
|
||||
}
|
||||
|
||||
/* TREE ITERATOR */
|
||||
//Iterator construction / destruction
|
||||
static int irjp_init_object_iterator(RJP_object_iterator_impl* it, const RJP_tree_node* root){
|
||||
irjp_init_tree_stack(&it->stack);
|
||||
RJP_tree_node* current = (RJP_tree_node*)root;
|
||||
while(current){
|
||||
irjp_push_tree_stack(&it->stack, current);
|
||||
current = current->left;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
static void irjp_delete_object_iterator(RJP_object_iterator_impl* it){
|
||||
irjp_delete_tree_stack(&it->stack);
|
||||
}
|
||||
//Iterator operations
|
||||
static RJP_tree_node* irjp_object_iterator_next(RJP_object_iterator_impl* it){
|
||||
RJP_tree_node* last = irjp_pop_tree_stack(&it->stack);
|
||||
if(last->right){
|
||||
irjp_push_tree_stack(&it->stack, last->right);
|
||||
}
|
||||
return irjp_object_iterator_current(it);
|
||||
}
|
||||
static RJP_tree_node* irjp_object_iterator_peek(RJP_object_iterator_impl* it){
|
||||
RJP_tree_node* n = irjp_peek_tree_stack(&it->stack)->right;
|
||||
if(n)
|
||||
return n;
|
||||
return irjp_double_peek_tree_stack(&it->stack);
|
||||
}
|
||||
static RJP_tree_node* irjp_object_iterator_current(RJP_object_iterator_impl* it){
|
||||
return irjp_peek_tree_stack(&it->stack);
|
||||
}
|
||||
|
||||
|
||||
/* RJP_OBJECT */
|
||||
void irjp_copy_object(RJP_value* dest, const RJP_value* src){
|
||||
irjp_free_tree(dest->object.root);
|
||||
dest->object.root = irjp_copy_tree(src->object.root);
|
||||
}
|
||||
void irjp_delete_object(RJP_value* obj){
|
||||
irjp_free_tree(obj->object.root);
|
||||
}
|
||||
static RJP_value* irjp_add_member_impl(RJP_value* dest, char* key, RJP_index keylen){
|
||||
++dest->object.num_members;
|
||||
int status;
|
||||
RJP_value* retval;
|
||||
dest->object.root = irjp_tree_insert_value(dest->object.root, key, keylen, &retval, &status);
|
||||
retval->parent = dest;
|
||||
return retval;
|
||||
}
|
||||
RJP_value* rjp_add_member(RJP_value* dest, char* key, RJP_index keylen){
|
||||
if(!keylen)
|
||||
keylen = strlen(key);
|
||||
return irjp_add_member_impl(dest, key, keylen);
|
||||
}
|
||||
RJP_value* rjp_add_member_key_copy(RJP_value* dest, const char* key, RJP_index keylen){
|
||||
if(!keylen)
|
||||
keylen = rjp_escape_strlen(key);
|
||||
char* newkey = rjp_alloc(keylen+1);
|
||||
rjp_escape_strcpy(newkey, key);
|
||||
newkey[keylen] = 0;
|
||||
return irjp_add_member_impl(dest, newkey, keylen);
|
||||
}
|
||||
RJP_value* rjp_remove_member_by_key(RJP_value* obj, const char* key){
|
||||
RJP_value* member = rjp_search_member(obj, key);
|
||||
if(!member)
|
||||
return NULL;
|
||||
return rjp_remove_member(obj, member);
|
||||
}
|
||||
RJP_value* rjp_remove_member(RJP_value* obj, RJP_value* member){
|
||||
RJP_tree_node* removed_node = NULL;
|
||||
obj->object.root = irjp_tree_remove_value(obj->object.root, (RJP_tree_node*)member, &removed_node);
|
||||
rjp_free(((RJP_object_member*)removed_node)->name.value);
|
||||
return member;
|
||||
}
|
||||
void rjp_free_member_by_key(RJP_value* obj, const char* key){
|
||||
RJP_value* removed = rjp_remove_member_by_key(obj, key);
|
||||
rjp_free_value(removed);
|
||||
}
|
||||
void rjp_free_member(RJP_value* obj, RJP_value* member){
|
||||
rjp_free_member_by_key(obj, ((RJP_object_member*)member)->name.value);
|
||||
}
|
||||
|
||||
void rjp_set_key_copy(RJP_value* dest, const char* key, RJP_index keylen){
|
||||
if(key){
|
||||
if(!keylen){
|
||||
keylen = strlen(key);
|
||||
}
|
||||
}else{
|
||||
keylen = 0;
|
||||
}
|
||||
char* newkey = rjp_alloc(keylen + 1);
|
||||
strncpy(newkey, key, keylen);
|
||||
newkey[keylen] = 0;
|
||||
rjp_set_key(dest, newkey, keylen);
|
||||
}
|
||||
void rjp_set_key(RJP_value* dest, char* key, RJP_index keylen){
|
||||
if(key){
|
||||
if(!keylen){
|
||||
keylen = strlen(key);
|
||||
}
|
||||
}else{
|
||||
keylen = 0;
|
||||
}
|
||||
RJP_value* parent = dest->parent;
|
||||
RJP_tree_node* removed_node = NULL;
|
||||
parent->object.root = irjp_tree_remove_value(parent->object.root, (RJP_tree_node*)dest, &removed_node);
|
||||
|
||||
rjp_free(((RJP_object_member*)removed_node)->name.value);
|
||||
((RJP_object_member*)removed_node)->name.value = key;
|
||||
((RJP_object_member*)removed_node)->name.length = keylen;
|
||||
|
||||
parent->object.root = irjp_tree_insert_node(parent->object.root, removed_node);
|
||||
}
|
||||
RJP_index rjp_num_members(const RJP_value* object){
|
||||
return object->object.num_members;
|
||||
}
|
||||
const RJP_string* rjp_member_key(const RJP_value* member){
|
||||
return &(((RJP_object_member*)member)->name);
|
||||
}
|
||||
|
||||
RJP_value* rjp_search_member(const RJP_value* object, const char* search){
|
||||
RJP_tree_node* n = irjp_tree_search_value(object->object.root, search);
|
||||
if(!n)
|
||||
return NULL;
|
||||
return &n->data.value;
|
||||
}
|
||||
|
||||
void rjp_init_object_iterator(RJP_object_iterator* it, const RJP_value* object){
|
||||
it->it = rjp_alloc(sizeof(RJP_object_iterator_impl));
|
||||
irjp_init_object_iterator(it->it, object->object.root);
|
||||
}
|
||||
void rjp_delete_object_iterator(RJP_object_iterator* it){
|
||||
irjp_delete_object_iterator(it->it);
|
||||
rjp_free(it->it);
|
||||
it->it = NULL;
|
||||
}
|
||||
RJP_value* rjp_object_iterator_current(const RJP_object_iterator* it){
|
||||
RJP_tree_node* n = irjp_object_iterator_current(it->it);
|
||||
if(!n)
|
||||
return NULL;
|
||||
return &n->data.value;
|
||||
}
|
||||
RJP_value* rjp_object_iterator_next(RJP_object_iterator* it){
|
||||
RJP_tree_node* n = irjp_object_iterator_next(it->it);
|
||||
if(!n)
|
||||
return NULL;
|
||||
return &n->data.value;
|
||||
}
|
||||
RJP_value* rjp_object_iterator_peek(const RJP_object_iterator* it){
|
||||
RJP_tree_node* n = irjp_object_iterator_peek(it->it);
|
||||
if(!n)
|
||||
return NULL;
|
||||
return &n->data.value;
|
||||
}
|
||||
|
||||
412
src/rjp_string.c
Normal file
412
src/rjp_string.c
Normal file
@@ -0,0 +1,412 @@
|
||||
/**
|
||||
rjp
|
||||
Copyright (C) 2018-2020 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "rjp_string.h"
|
||||
#include "rjp_internal.h"
|
||||
#include "rjp_object.h"
|
||||
#include "rjp_value.h"
|
||||
|
||||
#include <stdio.h> //fprintf
|
||||
#include <stdlib.h> //malloc, free
|
||||
#include <stdint.h> //uintN_t
|
||||
#include <string.h> //strcpy
|
||||
|
||||
//Determine if the character is valid whitespace
|
||||
int irjp_is_whitespace(char c){
|
||||
return c == ' ' || c == '\n' || c == '\r' || c == '\t';
|
||||
}
|
||||
|
||||
static uint32_t utf_strtol_4(const char* c){
|
||||
uint32_t ret = 0;
|
||||
for(RJP_index i = 0;i < 4;++i){
|
||||
if(c[i] >= '0' && c[i] <= '9'){
|
||||
ret |= ((c[i] ^ 0x30) << (4*(3-i)));
|
||||
}else if(c[i] >= 'A' && c[i] <= 'F'){
|
||||
ret |= ((c[i] - 0x37) << (4*(3-i)));
|
||||
}else if(c[i] >= 'a' && c[i] <= 'f'){
|
||||
ret |= ((c[i] - 0x57) << (4*(3-i)));
|
||||
}else{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int decode_unicode_escape(const char* str, uint32_t* high, uint32_t* low){
|
||||
if(*str != '\\' || *(str+1) != 'u'){ //invalid
|
||||
return *low = *high = 0;
|
||||
}
|
||||
*high = utf_strtol_4(str+2);
|
||||
if(!*high)
|
||||
return *low = *high = 0;
|
||||
if((*high & 0xF800) == 0xD800){ //utf-16
|
||||
if(*(str+6) != '\\' || *(str+7) != 'u'){
|
||||
return *low = *high = 0;
|
||||
}
|
||||
*low = utf_strtol_4(str+8);
|
||||
return 12;
|
||||
}else{
|
||||
*low = 0;
|
||||
}
|
||||
return 6;
|
||||
}
|
||||
|
||||
static uint32_t u16_surrogate_pair_to_codepoint(uint32_t high, uint32_t low){
|
||||
uint32_t codepoint;
|
||||
|
||||
codepoint = ((high & 0x07FF) << 10) | (1 << 16);
|
||||
codepoint = codepoint | (low & 0x03FF);
|
||||
|
||||
return codepoint;
|
||||
}
|
||||
|
||||
static uint32_t utf_to_codepoint(uint32_t high, uint32_t low){
|
||||
if(!low) //utf8
|
||||
return high;
|
||||
return u16_surrogate_pair_to_codepoint(high, low);
|
||||
}
|
||||
static int codepoint_strlen(uint32_t codepoint){
|
||||
if(codepoint <= 0x007F){
|
||||
return 1;
|
||||
}else if(codepoint <= 0x07FF){
|
||||
return 2;
|
||||
}else if(codepoint <= 0xFFFF){
|
||||
return 3;
|
||||
}else if(codepoint <= 0x10FFFF){
|
||||
return 4;
|
||||
}else{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int codepoint_to_u8(char* dest, uint32_t codepoint){
|
||||
if(codepoint <= 0x007F){
|
||||
dest[0] = codepoint;
|
||||
return 1;
|
||||
}else if(codepoint <= 0x07FF){
|
||||
dest[0] = (codepoint >> 6) | 0xC0;
|
||||
dest[1] = (codepoint & 0x3F) | 0x80;
|
||||
return 2;
|
||||
}else if(codepoint <= 0xFFFF){
|
||||
dest[0] = (codepoint >> 12) | 0xE0;
|
||||
dest[1] = ((codepoint >> 6) & 0x3F) | 0x80;
|
||||
dest[2] = (codepoint & 0x3F) | 0x80;
|
||||
return 3;
|
||||
}else if(codepoint <= 0x10FFFF){
|
||||
dest[0] = (codepoint >> 18) | 0xF0;
|
||||
dest[1] = ((codepoint >> 12) & 0x3F) | 0x80;
|
||||
dest[2] = ((codepoint >> 6) & 0x3F) | 0x80;
|
||||
dest[3] = (codepoint & 0x3F) | 0x80;
|
||||
return 4;
|
||||
}else{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
/*static uint32_t u8_to_codepoint(char* u){
|
||||
if((u[0] & 0x80) == 0){
|
||||
//one byte
|
||||
return u[0];
|
||||
}else if((u[0] & 0xE0) == 0xC0){
|
||||
//two byte
|
||||
uint32_t codepoint;
|
||||
codepoint = (u[0] & 0x1F) << 6;
|
||||
codepoint |= (u[1] & 0x3F);
|
||||
return codepoint;
|
||||
}else if((u[0] & 0xF0) == 0xE0){
|
||||
//three byte
|
||||
uint32_t codepoint;
|
||||
codepoint = (u[0] & 0x0F) << 12;
|
||||
codepoint |= (u[1] & 0x3F) << 6;
|
||||
codepoint |= (u[2] & 0x3F);
|
||||
return codepoint;
|
||||
}else if((u[0] & 0xF8) == 0xF0){
|
||||
//four byte
|
||||
uint32_t codepoint;
|
||||
codepoint = (u[0] & 0x07) << 18;
|
||||
codepoint |= (u[1] & 0x3F) << 12;
|
||||
codepoint |= (u[2] & 0x3F) << 6;
|
||||
codepoint |= (u[3] & 0x3F);
|
||||
return codepoint;
|
||||
}else{
|
||||
//invalid
|
||||
return 0;
|
||||
}
|
||||
}*/
|
||||
|
||||
//Convert escape sequences in strings
|
||||
char* irjp_parse_string(RJP_value* root, const char* str, int* inclen, int* len, int* row, int* column){
|
||||
char* new_string;
|
||||
++(*column); //account for starting quotation mark
|
||||
int oldpos = 0;
|
||||
int newpos = 0;
|
||||
for(;*(str+oldpos) != '"';++oldpos, ++(newpos), ++(*column)){
|
||||
if(*(str+oldpos) == '\\'){
|
||||
if(*(str+oldpos+1) == 'u'){
|
||||
uint32_t high, low;
|
||||
oldpos += (decode_unicode_escape(str+oldpos, &high, &low)-1);
|
||||
newpos += (codepoint_strlen(utf_to_codepoint(high, low))-1);
|
||||
}else{
|
||||
++oldpos;
|
||||
++(*column);
|
||||
}
|
||||
}else if(*(str+oldpos) == '\0'){
|
||||
newpos = 1;
|
||||
fprintf(stderr, "Syntax error! %s (%i:%i)\n", "Unexpected EOF in string!", *row, *column);
|
||||
rjp_free_value(root);
|
||||
return NULL;
|
||||
}else if(*(str+oldpos) == '\n'){
|
||||
++(*row);
|
||||
*column = 0;
|
||||
}
|
||||
}
|
||||
*inclen = oldpos;
|
||||
*len = newpos;
|
||||
if(newpos == 0){
|
||||
return NULL;
|
||||
}
|
||||
new_string = rjp_alloc(newpos + 1);
|
||||
new_string[newpos] = 0;
|
||||
for(int i = 0;*str != '"';++i,++str){
|
||||
if(*str == '\\'){
|
||||
++str;
|
||||
switch(*str){
|
||||
case '"':
|
||||
new_string[i] = '"';
|
||||
break;
|
||||
case 'n':
|
||||
new_string[i] = '\n';
|
||||
break;
|
||||
case 'r':
|
||||
new_string[i] = '\r';
|
||||
break;
|
||||
case 'b':
|
||||
new_string[i] = '\b';
|
||||
break;
|
||||
case '\\':
|
||||
new_string[i] = '\\';
|
||||
break;
|
||||
case 't':
|
||||
new_string[i] = '\t';
|
||||
break;
|
||||
case 'f':
|
||||
new_string[i] = '\f';
|
||||
break;
|
||||
case 'u':;
|
||||
uint32_t high, low;
|
||||
uint32_t codepoint;
|
||||
--str;
|
||||
str += (decode_unicode_escape(str, &high, &low) - 1);
|
||||
if(!high){
|
||||
rjp_free(new_string);
|
||||
return NULL;
|
||||
}
|
||||
codepoint = utf_to_codepoint(high, low);
|
||||
i += (codepoint_to_u8(new_string+i, codepoint)-1);
|
||||
break;
|
||||
default:
|
||||
new_string[i] = *str;
|
||||
break;
|
||||
}
|
||||
}else{
|
||||
new_string[i] = *str;
|
||||
}
|
||||
}
|
||||
return new_string;
|
||||
}
|
||||
|
||||
RJP_index rjp_escape_strcpy(char* dest, const char* src){
|
||||
RJP_index j = 0;
|
||||
for(RJP_index i = 0;src[i];++i){
|
||||
switch(src[i]){
|
||||
case '"':
|
||||
dest[j++] = '\\';
|
||||
dest[j++] = '"';
|
||||
break;
|
||||
case '\n':
|
||||
dest[j++] = '\\';
|
||||
dest[j++] = 'n';
|
||||
break;
|
||||
case '\r':
|
||||
dest[j++] = '\\';
|
||||
dest[j++] = 'r';
|
||||
break;
|
||||
case '\b':
|
||||
dest[j++] = '\\';
|
||||
dest[j++] = 'b';
|
||||
break;
|
||||
case '\\':
|
||||
dest[j++] = '\\';
|
||||
dest[j++] = '\\';
|
||||
break;
|
||||
case '\t':
|
||||
dest[j++] = '\\';
|
||||
dest[j++] = 't';
|
||||
break;
|
||||
case '\f':
|
||||
dest[j++] = '\\';
|
||||
dest[j++] = 'f';
|
||||
break;
|
||||
default:
|
||||
dest[j++] = src[i];
|
||||
break;
|
||||
};
|
||||
}
|
||||
dest[j] = 0;
|
||||
return j;
|
||||
}
|
||||
void irjp_strcpy(RJP_string* dest, const RJP_string* src){
|
||||
dest->value = rjp_alloc(src->length + 1);
|
||||
strcpy(dest->value, src->value);
|
||||
dest->value[src->length] = 0;
|
||||
dest->length = src->length;
|
||||
}
|
||||
|
||||
RJP_index rjp_escape_strlen(const char* str){
|
||||
RJP_index count = 0;
|
||||
for(RJP_index i = 0;str[i];++i){
|
||||
switch(str[i]){
|
||||
case '"':
|
||||
case '\n':
|
||||
case '\r':
|
||||
case '\b':
|
||||
case '\\':
|
||||
case '\t':
|
||||
case '\f':
|
||||
++count;
|
||||
//fallthrough
|
||||
default:
|
||||
++count;
|
||||
};
|
||||
}
|
||||
return count;
|
||||
}
|
||||
RJP_index irjp_array_strlen(const RJP_value* arr){
|
||||
RJP_index count = 2; //[]
|
||||
RJP_array_iterator it;
|
||||
rjp_init_array_iterator(&it, arr);
|
||||
|
||||
for(RJP_value* current = rjp_array_iterator_current(&it);current;current = rjp_array_iterator_next(&it)){
|
||||
count += irjp_value_strlen(current);
|
||||
if(rjp_array_iterator_peek(&it))
|
||||
++count; //,
|
||||
}
|
||||
return count;
|
||||
}
|
||||
RJP_index irjp_array_strlen_pretty(const RJP_value* arr, int depth){
|
||||
RJP_index count = 3 + depth; //[\n\t]
|
||||
++depth;
|
||||
RJP_array_iterator it;
|
||||
rjp_init_array_iterator(&it, arr);
|
||||
for(RJP_value* current = rjp_array_iterator_current(&it);current;current = rjp_array_iterator_next(&it)){
|
||||
count += irjp_value_strlen_pretty(current, depth);
|
||||
count += depth; //tabs
|
||||
++count; //newline
|
||||
if(rjp_array_iterator_peek(&it))
|
||||
++count; //,
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
RJP_index irjp_object_strlen(const RJP_value* root){
|
||||
RJP_index count = 2; //{}
|
||||
RJP_object_iterator it;
|
||||
rjp_init_object_iterator(&it, root);
|
||||
RJP_value* current = rjp_object_iterator_current(&it);
|
||||
while(current){
|
||||
RJP_index namelen = rjp_member_key(current)->length;
|
||||
const char* name = rjp_member_key(current)->value;
|
||||
if(namelen == 0 || name[0] == 0){
|
||||
rjp_delete_object_iterator(&it);
|
||||
return 0;
|
||||
}
|
||||
count += namelen + 3; //"":
|
||||
count += irjp_value_strlen(current);
|
||||
if((current = rjp_object_iterator_next(&it)))
|
||||
++count; //,
|
||||
}
|
||||
rjp_delete_object_iterator(&it);
|
||||
return count;
|
||||
}
|
||||
RJP_index irjp_object_strlen_pretty(const RJP_value* root, int depth){
|
||||
RJP_index count = 3 + depth; //{\n\t}
|
||||
++depth;
|
||||
RJP_object_iterator it;
|
||||
rjp_init_object_iterator(&it, root);
|
||||
RJP_value* current = rjp_object_iterator_current(&it);
|
||||
while(current){
|
||||
RJP_index namelen = rjp_member_key(current)->length;
|
||||
const char* name = rjp_member_key(current)->value;
|
||||
if(namelen == 0 || name[0] == 0){
|
||||
rjp_delete_object_iterator(&it);
|
||||
return 0;
|
||||
}
|
||||
count += namelen + 4; //"":space
|
||||
count += irjp_value_strlen_pretty(current, depth);
|
||||
count += depth; //tabs
|
||||
++count; //newline
|
||||
if((current = rjp_object_iterator_next(&it)))
|
||||
++count; //,
|
||||
}
|
||||
rjp_delete_object_iterator(&it);
|
||||
return count;
|
||||
}
|
||||
|
||||
RJP_index irjp_value_strlen(const RJP_value* root){
|
||||
switch(root->type){
|
||||
case rjp_json_integer:
|
||||
return snprintf(NULL, 0, "%" PRId64, root->integer);
|
||||
case rjp_json_dfloat:
|
||||
return snprintf(NULL, 0, "%lf", root->dfloat);
|
||||
case rjp_json_boolean:
|
||||
return root->boolean ? 4 : 5; //true, false
|
||||
case rjp_json_null:
|
||||
return 4;
|
||||
case rjp_json_string:
|
||||
return rjp_escape_strlen(root->string.value) + 2; //"";
|
||||
case rjp_json_array:
|
||||
return irjp_array_strlen(root);
|
||||
case rjp_json_object:
|
||||
return irjp_object_strlen(root);
|
||||
default:
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
RJP_index irjp_value_strlen_pretty(const RJP_value* root, int depth){
|
||||
switch(root->type){
|
||||
case rjp_json_integer:
|
||||
return snprintf(NULL, 0, "%" PRId64, root->integer);
|
||||
case rjp_json_dfloat:
|
||||
return snprintf(NULL, 0, "%lf", root->dfloat);
|
||||
case rjp_json_boolean:
|
||||
return root->boolean ? 4 : 5; //true, false
|
||||
case rjp_json_null:
|
||||
return 4;
|
||||
case rjp_json_string:
|
||||
return rjp_escape_strlen(root->string.value) + 2; //"";
|
||||
case rjp_json_array:
|
||||
return irjp_array_strlen_pretty(root, depth);
|
||||
case rjp_json_object:
|
||||
return irjp_object_strlen_pretty(root, depth);
|
||||
default:
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
196
src/strings.c
196
src/strings.c
@@ -1,196 +0,0 @@
|
||||
#include "rjp.h"
|
||||
#include "rjp_internal.h"
|
||||
|
||||
#include <stdio.h> //fprintf
|
||||
#include <stdlib.h> //malloc, free
|
||||
|
||||
//Convert escape sequences in strings
|
||||
char* _rjp__parse_string(struct JSON_value* root, const char* str, int* len, int* row, int* column){
|
||||
char* new_string;
|
||||
++(*column); //account for starting quotation mark
|
||||
for(*len = 0;*(str+*len) != '"';++(*len), ++(*column)){
|
||||
if(*(str+*len) == '\\'){
|
||||
++(*len);
|
||||
++(*column);
|
||||
}else if(*(str+*len) == '\0'){
|
||||
*len = 1;
|
||||
fprintf(stderr, "Syntax error! %s (%i:%i)\n", "Unexpected EOF in string!", *row, *column);
|
||||
rjp_free(root);
|
||||
return NULL;
|
||||
}else if(*(str+*len) == '\n'){
|
||||
++(*row);
|
||||
*column = 0;
|
||||
}
|
||||
}
|
||||
if(*len == 0){
|
||||
return NULL;
|
||||
}
|
||||
new_string = malloc(*len + 1);
|
||||
new_string[*len] = 0;
|
||||
for(int i = 0;*str != '"';++i,++str){
|
||||
if(*str == '\\'){
|
||||
++str;
|
||||
switch(*str){
|
||||
case '"':
|
||||
new_string[i] = '"';
|
||||
break;
|
||||
case 'n':
|
||||
new_string[i] = '\n';
|
||||
break;
|
||||
case 'r':
|
||||
new_string[i] = '\r';
|
||||
break;
|
||||
case 'b':
|
||||
new_string[i] = '\b';
|
||||
break;
|
||||
case '\\':
|
||||
new_string[i] = '\\';
|
||||
break;
|
||||
case 't':
|
||||
new_string[i] = '\t';
|
||||
break;
|
||||
case 'f':
|
||||
new_string[i] = '\f';
|
||||
break;
|
||||
default:
|
||||
new_string[i] = *str;
|
||||
break;
|
||||
}
|
||||
}else{
|
||||
new_string[i] = *str;
|
||||
}
|
||||
}
|
||||
return new_string;
|
||||
}
|
||||
|
||||
size_t rjp_escape_strcpy(char* dest, const char* src){
|
||||
size_t j = 0;
|
||||
for(size_t i = 0;src[i];++i){
|
||||
switch(src[i]){
|
||||
case '"':
|
||||
dest[j++] = '\\';
|
||||
dest[j++] = '"';
|
||||
break;
|
||||
case '\n':
|
||||
dest[j++] = '\\';
|
||||
dest[j++] = 'n';
|
||||
break;
|
||||
case '\r':
|
||||
dest[j++] = '\\';
|
||||
dest[j++] = 'r';
|
||||
break;
|
||||
case '\b':
|
||||
dest[j++] = '\\';
|
||||
dest[j++] = 'b';
|
||||
break;
|
||||
case '\\':
|
||||
dest[j++] = '\\';
|
||||
dest[j++] = '\\';
|
||||
break;
|
||||
case '\t':
|
||||
dest[j++] = '\\';
|
||||
dest[j++] = 't';
|
||||
break;
|
||||
case '\f':
|
||||
dest[j++] = '\\';
|
||||
dest[j++] = 'f';
|
||||
break;
|
||||
default:
|
||||
dest[j++] = src[i];
|
||||
break;
|
||||
};
|
||||
}
|
||||
return j;
|
||||
}
|
||||
size_t rjp_escape_strlen(const char* str){
|
||||
size_t count = 0;
|
||||
for(size_t i = 0;str[i];++i){
|
||||
switch(str[i]){
|
||||
case '"':
|
||||
case '\n':
|
||||
case '\r':
|
||||
case '\b':
|
||||
case '\\':
|
||||
case '\t':
|
||||
case '\f':
|
||||
++count;
|
||||
//fallthrough
|
||||
default:
|
||||
++count;
|
||||
};
|
||||
}
|
||||
return count;
|
||||
}
|
||||
size_t _rjp__array_strlen(struct JSON_value* arr){
|
||||
size_t count = 2; //[]
|
||||
struct JSON_array* array = &arr->array;
|
||||
struct JSON_array_element* element_list = array->elements;
|
||||
for(;element_list;element_list = element_list->next){
|
||||
switch(element_list->value.type){
|
||||
case json_integer:
|
||||
count += snprintf(NULL, 0, "%li", element_list->value.integer);
|
||||
break;
|
||||
case json_dfloat:
|
||||
count += snprintf(NULL, 0, "%lf", element_list->value.dfloat);
|
||||
break;
|
||||
case json_boolean:
|
||||
count += element_list->value.boolean ? 4 : 5;
|
||||
break;
|
||||
case json_null:
|
||||
count += 4;
|
||||
break;
|
||||
case json_string:
|
||||
count += element_list->value.string.length;
|
||||
break;
|
||||
case json_array:
|
||||
count += _rjp__array_strlen(&element_list->value);
|
||||
break;
|
||||
case json_object:
|
||||
count += _rjp__object_strlen(&element_list->value);
|
||||
break;
|
||||
};
|
||||
if(element_list->next)
|
||||
++count;
|
||||
else
|
||||
break;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
size_t _rjp__object_strlen(struct JSON_value* root){
|
||||
size_t count = 2; //{}
|
||||
struct JSON_object* root_obj = &root->object;
|
||||
struct JSON_object_member* member_list = root_obj->members;
|
||||
for(;member_list;member_list = member_list->next){
|
||||
count += member_list->name.length + 3; //"":
|
||||
switch(member_list->value.type){
|
||||
case json_integer:
|
||||
count += snprintf(NULL, 0, "%li", member_list->value.integer);
|
||||
break;
|
||||
case json_dfloat:
|
||||
count += snprintf(NULL, 0, "%lf", member_list->value.dfloat);
|
||||
break;
|
||||
case json_boolean:
|
||||
count += member_list->value.boolean ? 4 : 5; //true, false
|
||||
break;
|
||||
case json_null:
|
||||
count += 4; //null
|
||||
break;
|
||||
case json_string:
|
||||
count += member_list->value.string.length;
|
||||
break;
|
||||
case json_array:
|
||||
count += _rjp__array_strlen(&member_list->value);
|
||||
break;
|
||||
case json_object:
|
||||
count += _rjp__object_strlen(&member_list->value);
|
||||
break;
|
||||
};
|
||||
if(member_list->next)
|
||||
++count; //,
|
||||
else
|
||||
break;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
454
src/tree.c
Normal file
454
src/tree.c
Normal 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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user