49 Commits

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

3
.gitignore vendored
View File

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

View File

@@ -4,8 +4,8 @@ include(GNUInstallDirs)
cmake_minimum_required(VERSION 3.0.2)
project(rjp)
set(rjp_VERSION_MAJOR 0)
set(rjp_VERSION_MINOR 6)
set(rjp_VERSION_REVISION 2)
set(rjp_VERSION_MINOR 9)
set(rjp_VERSION_REVISION 0)
set(INCLUDE_PATH ${CMAKE_SOURCE_DIR}/include)
configure_file(
"${INCLUDE_PATH}/config.h.in"
@@ -16,15 +16,15 @@ include_directories("${INCLUDE_PATH}")
option(ENABLE_DIAGNOSTICS "Print diagnostic messages when parsing json to help identify issues" ON)
option(ENABLE_SHARED "Build shared library" OFF)
set(SOURCE_LIST "src/input.c" "src/output.c" "src/strings.c" "src/memory.c" "src/rjp.c")
set(SOURCE_LIST "src/rjp_ordered_object.c" "src/rjp_unordered_object.c" "src/input.c" "src/output.c" "src/rjp_array.c" "src/rjp.c" "src/rjp_object.c" "src/rjp_string.c" "src/tree.c")
if(ENABLE_SHARED)
add_library(rjp SHARED ${SOURCE_LIST})
set_target_properties(rjp PROPERTIES SOVERSION "${rjp_VERSION_MAJOR}.${rjp_VERSION_MINOR}.${rjp_VERSION_REVISION}")
else()
add_library(rjp STATIC ${SOURCE_LIST})
endif()
set_target_properties(rjp PROPERTIES PUBLIC_HEADER ${INCLUDE_PATH}/rjp.h)
set_target_properties(rjp PROPERTIES SOVERSION "${rjp_VERSION_MAJOR}.${rjp_VERSION_MINOR}.${rjp_VERSION_REVISION}")
target_compile_options(rjp PRIVATE -Wall -Wextra -pedantic)
if(ENABLE_DIAGNOSTICS)
target_compile_definitions(rjp PRIVATE RJP_DIAGNOSTICS)

5
TODO Normal file
View File

@@ -0,0 +1,5 @@
Change string handling to work with chunked reading
Change numeral handling to work with chunked reading
handle scientific notation
Add ability to specify object order explicitly

View File

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

View File

@@ -1,6 +1,6 @@
/**
rjp
Copyright (C) 2018-2019 rexy712
Copyright (C) 2018-2020 rexy712
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -16,8 +16,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CONFIG_H
#define CONFIG_H
#ifndef RJP_CONFIG_H
#define RJP_CONFIG_H
#define RJP_VERSION_MAJOR @rjp_VERSION_MAJOR@
#define RJP_VERSION_MINOR @rjp_VERSION_MINOR@

View File

@@ -1,6 +1,6 @@
/**
rjp
Copyright (C) 2018-2019 rexy712
Copyright (C) 2018-2020 rexy712
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -19,164 +19,192 @@
#ifndef RJP_H
#define RJP_H
#include <stddef.h> //size_t
#ifdef __cplusplus
extern "C"{
#endif
#if defined(__GNUC__) || defined(__clang__)
#define DEPRECATED(str) __attribute__((deprecated(str)))
#else
#define DEPRECATED(str)
#endif
#ifndef RJP_int
#include <stdint.h>
#define RJP_int int_fast64_t
#endif
#ifndef RJP_index
#include <stddef.h>
#define RJP_index size_t
#endif
#ifndef RJP_float
#include <stdint.h>
#define RJP_float double
#endif
#ifndef RJP_bool
#define RJP_bool char
#endif
//used with rjp_to_json
#define RJP_FORMAT_NONE 0
#define RJP_FORMAT_PRETTY 1
//type of data
typedef enum RJP_type{
json_object = 0,
json_string,
json_integer,
json_dfloat,
json_boolean,
json_array,
json_null
}RJP_type;
typedef enum RJP_data_type{
rjp_json_object = 1,
rjp_json_ordered_object = 3,
rjp_json_string = 2,
rjp_json_integer = 4,
rjp_json_dfloat = 8,
rjp_json_boolean = 16,
rjp_json_array = 32,
rjp_json_null = 64
}RJP_data_type;
typedef struct RJP_value RJP_value;
//Hold a C string and the length excluding NULL terminator
typedef struct RJP_string{
char* value;
size_t length;
RJP_index length;
}RJP_string;
//Forward declarations
struct RJP_object_member;
struct RJP_array_element;
//Represents a json object
typedef struct RJP_object{
struct RJP_object_member* members; //linked list of members
struct RJP_object_member* last; //final member of linked list
size_t num_members;
}RJP_object;
//Represents a json array
typedef struct RJP_array{
struct RJP_array_element* elements; //linked list of elements
struct RJP_array_element* last; //final member of linked list
size_t num_elements;
}RJP_array;
//Represents json data
//hold any json data type
typedef struct RJP_value{
struct RJP_object_iterator_impl;
typedef struct RJP_object_iterator{
union{
long integer;
double dfloat;
char boolean;
struct RJP_object object;
struct RJP_string string;
struct RJP_array array;
struct RJP_object_iterator_impl* it;
RJP_value* current;
};
struct RJP_value* parent; //pointer to parent (either an array or object or NULL)
enum RJP_type type; //flag to determine active member of union
}RJP_value;
RJP_data_type type;
}RJP_object_iterator;
//Result of an rjp search operation
typedef struct RJP_search_res{
RJP_value* value; //pointer to match
size_t searchindex; //index in the search list
size_t objindex; //index in the searched object
}RJP_search_res;
//Convert C string consisting of json data into RJP's format
RJP_value* rjp_parse(const char* str);
//Initialize a root RJP_value to NULL
RJP_value* rjp_init_json(void);
//Initialize a root RJP_value to copy of value
RJP_value* rjp_init_json_as(RJP_value value);
//Free an RJP_value and all members/elements
void rjp_free_value(RJP_value* root);
//Deep copy RJP_value
void rjp_copy_value(RJP_value* dest, const RJP_value* src);
typedef struct RJP_array_iterator{
RJP_value* current;
}RJP_array_iterator;
/***************** NON OBJECT OPERATIONS *******************/
//Allocate heap space for rjp to use. Use if the memory is to be freed by rjp
void* rjp_alloc(size_t nbytes);
void* rjp_alloc(RJP_index nbytes);
//Allocate heap space for rjp and initialize to 0.
void* rjp_calloc(size_t num, size_t nbytes);
void* rjp_calloc(RJP_index num, RJP_index nbytes);
//Resize heap allocated space for rjp
void* rjp_realloc(void* ptr, size_t nbytes);
void* rjp_realloc(void* ptr, RJP_index nbytes);
//Free memory allocated by rjp_alloc or rjp_calloc
void rjp_free(void* dest);
//add a member to a json object, allocating a copy of the key
RJP_value* rjp_add_member(RJP_value* dest, const char* key, size_t keylen, RJP_value value);
//add a member to a json object without allocation. key must be allocated using rjp_alloc/rjp_calloc
RJP_value* rjp_add_member_no_alloc(RJP_value* dest, char* key, size_t keylen, RJP_value value);
//add an element to a json array
RJP_value* rjp_add_element(RJP_value* dest, RJP_value value);
//copy input string and add json escape sequences
RJP_index rjp_escape_strcpy(char* dest, const char* src);
//length of string including escape sequences
RJP_index rjp_escape_strlen(const char* str);
/***************** GENERIC OPERATIONS *******************/
//Convert C string consisting of json data into RJP's format
RJP_value* rjp_parse(const char* str);
//RJP_value* rjp_parse_chunked(const char* str, RJP_value* prev_chunk);
char* rjp_to_json(const RJP_value* root, int pretty);
//Initialize a root RJP_value to copy of value
RJP_value* rjp_new_null(void);
RJP_value* rjp_new_int(RJP_int val);
RJP_value* rjp_new_float(RJP_float val);
RJP_value* rjp_new_bool(RJP_bool val);
RJP_value* rjp_new_string_copy(const char* val, RJP_index length);
RJP_value* rjp_new_string(char* val, RJP_index length);
RJP_value* rjp_new_object(void);
RJP_value* rjp_new_ordered_object(void);
RJP_value* rjp_new_array(void);
//Deallocate RJP_value
void rjp_free_value(RJP_value* root);
//Deep copy RJP_value
//When dest is not null, cleanup and then fill the object pointed to by dest. Otherwise allocate a new value.
RJP_value* rjp_copy_value(RJP_value* dest, const RJP_value* src);
RJP_value* rjp_move_value(RJP_value* dest, RJP_value* src);
//set existing value
void rjp_set_value(RJP_value* dest, RJP_value value);
//initialize a RJP_value to the requested type and value
RJP_value rjp_integer(long i);
RJP_value rjp_boolean(char b);
RJP_value rjp_dfloat(double d);
RJP_value rjp_string(char* c, size_t len);
RJP_value rjp_null(void);
RJP_value rjp_object(void);
RJP_value rjp_array(void);
RJP_value* rjp_set_null(RJP_value* v);
RJP_value* rjp_set_int(RJP_value* v, RJP_int val);
RJP_value* rjp_set_float(RJP_value* v, RJP_float val);
RJP_value* rjp_set_bool(RJP_value* v, RJP_bool val);
RJP_value* rjp_set_string_copy(RJP_value* v, const char* val, RJP_index length);
RJP_value* rjp_set_string(RJP_value* v, char* val, RJP_index length);
RJP_value* rjp_set_object(RJP_value* v);
RJP_value* rjp_set_ordered_object(RJP_value* v);
RJP_value* rjp_set_array(RJP_value* v);
//Access first member of a json object
RJP_value* rjp_get_member(const RJP_value* object);
//Access next member of a json object given the previous member
RJP_value* rjp_next_member(const RJP_value* member);
//Return number of members in the object
size_t rjp_num_members(const RJP_value* object);
//Return the object member's key name
char* rjp_member_name(const RJP_value* member);
//Return the object member's key name length excluding null terminator
size_t rjp_member_name_length(RJP_value* member);
//Search for an object member with given key
RJP_search_res rjp_search_member(const RJP_value* object, const char* search, size_t skip);
//Search for first occurance of multiple keys. Returns first occurance of each.
//Assumes dest is large enough to hold maximum num items.
size_t rjp_search_members(const RJP_value* object, size_t num, const char* const* searches, RJP_search_res* dest, size_t skip);
//Search for multiple occurances of multiple keys. Returns pointer to list of matches.
//Returned pointer must be freed using rjp_free
RJP_search_res* rjp_search_members_multi(const RJP_value* object, size_t num, const char* const* searches, size_t* rsize, size_t skip);
//Access first element of a json array
RJP_value* rjp_get_element(const RJP_value* array);
//Access next element of a json array given the previous element
RJP_value* rjp_next_element(const RJP_value* element);
//Return number of elements in the array
size_t rjp_num_elements(const RJP_value* array);
RJP_float rjp_get_float(const RJP_value* value);
RJP_int rjp_get_int(const RJP_value* value);
RJP_bool rjp_get_bool(const RJP_value* value);
RJP_string* rjp_get_string(RJP_value* value);
const RJP_string* rjp_get_cstring(const RJP_value* value);
//Return handle to parent of given value
RJP_value* rjp_value_parent(RJP_value* element);
/***************** OBJECT/ARRAY SHARED OPERATIONS *******************/
RJP_value* rjp_value_parent(const RJP_value* element);
//Return type of value
RJP_type rjp_value_type(RJP_value* value);
RJP_data_type rjp_value_type(const RJP_value* value);
//value accessors
double rjp_value_dfloat(RJP_value* value);
int rjp_value_integer(RJP_value* value);
char rjp_value_boolean(RJP_value* value);
char* rjp_value_string(RJP_value* value);
size_t rjp_value_string_length(RJP_value* value);
/***************** OBJECT OPERATIONS *******************/
//add a member to a json object, allocating a copy of the key
RJP_value* rjp_add_member_key_copy(RJP_value* dest, const char* key, RJP_index keylen);
//add a member to a json object without allocation. key must be allocated using rjp_alloc/rjp_calloc
RJP_value* rjp_add_member(RJP_value* dest, char* key, RJP_index keylen);
//Remove member without freeing
RJP_value* rjp_remove_member_by_key(RJP_value* obj, const char* key);
RJP_value* rjp_remove_member(RJP_value* obj, RJP_value* member);
//Remove and free member
void rjp_free_member_by_key(RJP_value* obj, const char* key);
void rjp_free_member(RJP_value* obj, RJP_value* member);
//copy input string and add json escape sequences
size_t rjp_escape_strcpy(char* dest, const char* src);
//set existing object member's key without allocation. key must be allocated using rjp_alloc/rjp_calloc
void rjp_set_key(RJP_value* dest, char* key, RJP_index keylen);
//set existing object member's key
void rjp_set_key_copy(RJP_value* dest, const char* key, RJP_index keylen);
//length of string including escape sequences
size_t rjp_escape_strlen(const char* str);
//Return number of members in the object
RJP_index rjp_num_members(const RJP_value* object);
//Return the object member's key
const RJP_string* rjp_member_key(const RJP_value* member);
//Search for an object member with given key
RJP_value* rjp_search_member(const RJP_value* object, const char* search);
RJP_value* rjp_object_to_ordered(RJP_value* object);
RJP_value* rjp_object_to_unordered(RJP_value* object);
/***************** OBJECT ITERATOR OPERATIONS *******************/
//Access first member of a json object
void rjp_init_object_iterator(RJP_object_iterator* iter, const RJP_value* object);
void rjp_delete_object_iterator(RJP_object_iterator* it);
RJP_value* rjp_object_iterator_current(const RJP_object_iterator* it);
RJP_value* rjp_object_iterator_next(RJP_object_iterator* it);
RJP_value* rjp_object_iterator_peek(const RJP_object_iterator* it);
/***************** ARRAY OPERATIONS *******************/
//add an element to a json array
RJP_value* rjp_add_element(RJP_value* dest);
RJP_value* rjp_remove_element(RJP_value* arr, RJP_value* elem);
void rjp_free_element(RJP_value* arr, RJP_value* elem);
RJP_index rjp_num_elements(const RJP_value* arr);
/***************** ARRAY ITERATOR OPERATIONS *******************/
void rjp_init_array_iterator(RJP_array_iterator* iter, const RJP_value* array);
void rjp_delete_array_iterator(RJP_array_iterator* iter);
RJP_value* rjp_array_iterator_current(const RJP_array_iterator* it);
RJP_value* rjp_array_iterator_next(RJP_array_iterator* it);
RJP_value* rjp_array_iterator_peek(const RJP_array_iterator* it);
//Convert RJP_object to json string
size_t rjp_dump_object(RJP_value* root, char* dest);
//Convert RJP_array to json string
size_t rjp_dump_array(RJP_value* arr, char* dest);
//Convert root rjp value into json string
char* rjp_to_json(RJP_value* root);
#ifdef __cplusplus
}
#endif
#undef DEPRECATED
#endif

39
include/rjp_array.h Normal file
View File

@@ -0,0 +1,39 @@
/**
rjp
Copyright (C) 2018-2020 rexy712
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef RJP_ARRAY_H
#define RJP_ARRAY_H
#include "rjp.h"
struct RJP_array_element;
//Represents a json array
typedef struct RJP_array{
struct RJP_array_element* elements; //linked list of elements
struct RJP_array_element* last; //final member of linked list
RJP_index num_elements;
}RJP_array;
void irjp_add_element(RJP_array* j);
void irjp_delete_value(RJP_value* root);
void irjp_copy_array(RJP_value* dest, const RJP_value* src);
RJP_index rjp_dump_array(const RJP_value* arr, char* dest);
#endif

View File

@@ -1,6 +1,6 @@
/**
rjp
Copyright (C) 2018-2019 rexy712
Copyright (C) 2018-2020 rexy712
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -16,17 +16,16 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef STRINGS_H
#define STRINGS_H
#ifndef RJP_ARRAY_ELEMENT_H
#define RJP_ARRAY_ELEMENT_H
#include "rjp.h"
#include <stddef.h> //size_t
#include "rjp_internal.h"
#include "rjp_value.h"
int _rjp__is_whitespace(char c);
char* _rjp__parse_string(RJP_value* root, const char* str, int* inclen, int* len, int* row, int* column);
size_t _rjp__array_strlen(RJP_value* arr);
size_t _rjp__object_strlen(RJP_value* root);
size_t _rjp__value_strlen(RJP_value* root);
void _rjp__strcpy(RJP_string* dest, const RJP_string* src);
typedef struct RJP_array_element{
struct RJP_value value;
struct RJP_array_element* next;
struct RJP_array_element* prev;
}RJP_array_element;
#endif

View File

@@ -1,6 +1,6 @@
/**
rjp
Copyright (C) 2018-2019 rexy712
Copyright (C) 2018-2020 rexy712
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -20,6 +20,7 @@
#define RJP_INTERNAL_H
#include "rjp.h"
#include <stdio.h>
#ifdef __GNUC__
#define MAYBE_UNUSED __attribute__((unused))
@@ -27,27 +28,29 @@
#define MAYBE_UNUSED
#endif
#define UNUSED_VARIABLE(thing) (void)(thing)
#ifdef RJP_DIAGNOSTICS
#include <stdio.h>
#define DIAG_PRINT(...) fprintf(__VA_ARGS__)
#else
#define DIAG_PRINT(...)
#define DIAG_PRINT(...) irjp_ignore_unused(__VA_ARGS__)
static inline void irjp_ignore_unused(FILE* fp, ...){
UNUSED_VARIABLE(fp);
}
#endif
//
typedef struct RJP_array_element{
struct RJP_value value;
struct RJP_array_element* next;
}RJP_array_element;
//A member of an object
typedef struct RJP_object_member{
struct RJP_value value;
struct RJP_string name;
struct RJP_object_member* next;
}RJP_object_member;
void _rjp__add_element(RJP_array* j);
void _rjp__add_member(RJP_object* j, const char* str, size_t len);
void _rjp__add_member_no_alloc(RJP_object* j, char* str, size_t len);
RJP_value irjp_integer(RJP_int i);
RJP_value irjp_boolean(RJP_bool b);
RJP_value irjp_dfloat(RJP_float d);
RJP_value irjp_string(char* c, RJP_index len);
RJP_value irjp_string_copy(const char* c);
RJP_value irjp_null(void);
RJP_value irjp_object(void);
RJP_value irjp_ordered_object(void);
RJP_value irjp_array(void);
#endif

31
include/rjp_object.h Normal file
View File

@@ -0,0 +1,31 @@
/**
rjp
Copyright (C) 2018-2020 rexy712
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef RJP_OBJECT_H
#define RJP_OBJECT_H
#include "rjp_unordered_object.h"
#include "rjp_ordered_object.h"
#include "rjp_internal.h"
void irjp_copy_object(RJP_value* dest, const RJP_value* src);
void irjp_delete_object(RJP_value* obj);
RJP_index rjp_dump_object(const RJP_value* root, char* dest);
#endif

View File

@@ -0,0 +1,30 @@
/**
rjp
Copyright (C) 2018-2020 rexy712
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef RJP_OBJECT_MEMBER_H
#define RJP_OBJECT_MEMBER_H
#include "rjp_internal.h"
#include "rjp_value.h"
typedef struct RJP_object_member{
RJP_value value;
RJP_string name;
}RJP_object_member;
#endif

View File

@@ -0,0 +1,50 @@
/**
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_ORDERED_OBJECT_H
#define RJP_ORDERED_OBJECT_H
#include "rjp_internal.h"
struct RJP_ordered_member;
typedef struct RJP_ordered_object{
struct RJP_ordered_member* members;
struct RJP_ordered_member* last;
RJP_index num_members;
}RJP_ordered_object;
void irjp_copy_ordered_object(RJP_value* dest, const RJP_value* src);
void irjp_delete_ordered_object(RJP_value* obj);
void irjp_copy_ordered_object(RJP_value* dest, const RJP_value* src);
void irjp_delete_ordered_object(RJP_value* obj);
RJP_value* irjp_add_ordered_member(RJP_value* dest, char* key, RJP_index keylen);
RJP_value* irjp_remove_ordered_member(RJP_value* obj, RJP_value* member);
void irjp_ordered_set_key(RJP_value* dest, char* key, RJP_index keylen);
RJP_index irjp_ordered_num_members(const RJP_value* object);
RJP_value* irjp_ordered_search_member(const RJP_value* object, const char* search);
void irjp_init_ordered_object_iterator(RJP_object_iterator* it, const RJP_value* object);
void irjp_delete_ordered_object_iterator(RJP_object_iterator* it);
RJP_value* irjp_ordered_object_iterator_current(const RJP_object_iterator* it);
RJP_value* irjp_ordered_object_iterator_next(RJP_object_iterator* it);
RJP_value* irjp_ordered_object_iterator_peek(const RJP_object_iterator* it);
RJP_index irjp_dump_ordered_object(const RJP_value* root, char* dest);
#endif

32
include/rjp_string.h Normal file
View File

@@ -0,0 +1,32 @@
/**
rjp
Copyright (C) 2018-2020 rexy712
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef RJP_STRINGS_H
#define RJP_STRINGS_H
#include "rjp.h"
int irjp_is_whitespace(char c);
char* irjp_parse_string(RJP_value* root, const char* str, int* inclen, int* len, int* row, int* column);
RJP_index irjp_array_strlen(const RJP_value* arr);
RJP_index irjp_object_strlen(const RJP_value* root);
RJP_index irjp_value_strlen(const RJP_value* root);
RJP_index irjp_value_strlen_pretty(const RJP_value* root, int depth);
void irjp_strcpy(RJP_string* dest, const RJP_string* src);
#endif

View File

@@ -0,0 +1,48 @@
/**
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_UNORDERED_OBJECT_H
#define RJP_UNORDERED_OBJECT_H
#include "rjp_internal.h"
struct RJP_tree_node;
struct RJP_object_member;
typedef struct RJP_unordered_object{
struct RJP_tree_node* root;
RJP_index num_members;
}RJP_unordered_object;
void irjp_copy_unordered_object(RJP_value* dest, const RJP_value* src);
void irjp_delete_unordered_object(RJP_value* obj);
RJP_value* irjp_add_unordered_member(RJP_value* dest, char* key, RJP_index keylen);
RJP_value* irjp_remove_unordered_member(RJP_value* obj, RJP_value* member);
void irjp_unordered_set_key(RJP_value* dest, char* key, RJP_index keylen);
RJP_index irjp_unordered_num_members(const RJP_value* object);
RJP_value* irjp_unordered_search_member(const RJP_value* object, const char* search);
void irjp_init_unordered_object_iterator(RJP_object_iterator* it, const RJP_value* object);
void irjp_delete_unordered_object_iterator(RJP_object_iterator* it);
RJP_value* irjp_unordered_object_iterator_current(const RJP_object_iterator* it);
RJP_value* irjp_unordered_object_iterator_next(RJP_object_iterator* it);
RJP_value* irjp_unordered_object_iterator_peek(const RJP_object_iterator* it);
RJP_index irjp_dump_unordered_object(const RJP_value* root, char* dest);
#endif

43
include/rjp_value.h Normal file
View File

@@ -0,0 +1,43 @@
/**
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;
RJP_ordered_object orobject;
RJP_unordered_object object;
RJP_string string;
RJP_array array;
};
struct RJP_value* parent; //pointer to parent (either an array or object or NULL)
enum RJP_data_type type; //flag to determine active member of union
}RJP_value;
#endif

51
include/tree.h Normal file
View File

@@ -0,0 +1,51 @@
/**
rjp
Copyright (C) 2018-2020 rexy712
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef RJP_TREE_H
#define RJP_TREE_H
#include "rjp.h"
#include "rjp_object.h"
#include "rjp_object_member.h"
#define RJP_TREE_SUCCESS 0
#define RJP_TREE_ERR_NULL_ROOT 1
#define RJP_TREE_ERR_NOT_FOUND 2
typedef struct RJP_object_iterator RJP_object_iterator;
typedef struct RJP_tree_node RJP_tree_node;
struct RJP_tree_node{
RJP_object_member data;
RJP_tree_node* parent;
RJP_tree_node* left;
RJP_tree_node* right;
unsigned color:1;
};
RJP_tree_node* irjp_new_node(char* key, RJP_index keylen);
RJP_tree_node* irjp_tree_insert_value(RJP_tree_node* root, char* key, RJP_index keylen, RJP_value** added, int* status);
RJP_tree_node* irjp_tree_insert_node(RJP_tree_node *restrict root, RJP_tree_node *restrict newnode);
RJP_tree_node* irjp_tree_remove_value(RJP_tree_node* restrict root, RJP_tree_node* restrict member, RJP_tree_node** removed_node);
RJP_tree_node* irjp_tree_search_value(RJP_tree_node* root, const char* key);
RJP_tree_node* irjp_copy_tree(const RJP_tree_node* root);
void irjp_free_tree(RJP_tree_node* root);
void irjp_dbg_print_tree(RJP_tree_node* root);
void irjp_dbg_print_tree_bfs(RJP_tree_node* root);
#endif

View File

@@ -1,6 +1,6 @@
/**
rjp
Copyright (C) 2018-2019 rexy712
Copyright (C) 2018-2020 rexy712
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -20,263 +20,359 @@
#include "rjp.h"
#include "rjp_internal.h"
#include "strings.h"
#include "rjp_value.h"
#include "rjp_string.h"
#include "memory.h"
#include <stdlib.h> //strtod, strtol
#include <stdio.h> //fprintf, stderr
#include <string.h> //memset
//types of searches in the text
typedef enum json_search_target{
json_key,
json_colon,
json_comma,
json_value,
json_none
typedef enum rjp_json_search_target{
rjp_json_target_key,
rjp_json_target_colon,
rjp_json_target_comma,
rjp_json_target_value,
rjp_json_target_string,
rjp_json_target_numeral,
rjp_json_target_none
}json_search_target;
static RJP_value* _rjp__add_value(RJP_value* curr, RJP_value new_val){
static RJP_value* irjp_add_value(RJP_value* curr, RJP_value* lastadded, RJP_value new_val){
new_val.parent = curr;
if(!curr){
curr = rjp_calloc(1, sizeof(RJP_value));
*curr = new_val;
return curr;
}
if(curr->type == json_array){
_rjp__add_element(&curr->array);
curr->array.last->value = new_val;
return &curr->array.last->value;
if(curr->type == rjp_json_array){
RJP_value* last = rjp_add_element(curr);
rjp_move_value(last, &new_val);
return last;
}
curr->object.last->value = new_val;
return &curr->object.last->value;
*lastadded = new_val;
return lastadded;
}
#define syntax_error(msg, row, column)\
do{DIAG_PRINT(stderr, "Syntax error! %s (%i:%i)\n", msg, row, column);rjp_free_value(root);return NULL;}while(0)
#define MAX_DEPTH 16
RJP_value* rjp_parse(const char* str){
RJP_value* root = 0;
RJP_value* curr = 0;
int row = 1, column = 0;
int in_line_comment = 0;
int in_block_comment = 0;
//keep track of where we are in a given subobject
int state_stack[MAX_DEPTH] = {0},*top = state_stack;
typedef struct RJP_string_state{
int escaped;
int in_utf_sequence;
char* buffer; //store partial string here only when chunked reading and chunk ends mid string
}RJP_string_state;
typedef struct RJP_numeral_state{
int numlen;
char* buffer; //store partial number string here only when chunked reading and chunk ends mid number
}RJP_numeral_state;
typedef struct RJP_parse_state{
RJP_value* root;
RJP_value* curr;
RJP_value* lastadded;
union{
RJP_string_state str_state;
RJP_numeral_state num_state;
};
int row, column;
int in_line_comment;
int in_block_comment;
int target_stack[MAX_DEPTH];
int* target;
}RJP_parse_state;
void irjp_init_parse_state(RJP_parse_state* state){
state->root = NULL;
state->curr = NULL;
state->row = state->column = 0;
state->in_line_comment = 0;
state->in_block_comment = 0;
memset(state->target_stack, 0, MAX_DEPTH*sizeof(int));
state->target = state->target_stack;
}
static void syntax_error(const char* msg, RJP_parse_state* state){
DIAG_PRINT(stderr, "Syntax error! %s (%i:%i)\n", msg, state->row, state->column);
rjp_free_value(state->root);
}
//Return number of characters handled while processing comment
int irjp_handle_comment(const char* str, RJP_parse_state* state){
char c = *str;
if(state->in_line_comment){
if(c == '\n')
state->in_line_comment = 0;
return 1;
}else if(state->in_block_comment){
if(c == '*' && *(str+1) == '/'){
state->in_block_comment = 0;
return 2;
}
return 1;
}else if(c == '/' && *(str+1) == '*'){
state->in_block_comment = 1;
return 2;
}else if(c == '/' && *(str+1) == '/'){
state->in_line_comment = 1;
return 2;
}
return 0;
}
int irjp_handle_key(const char* str, RJP_parse_state* state){
char c = *str;
//start of key
if(c == '"'){
if(state->curr == NULL){
syntax_error("Key found outside of object definition!", state);
return -1;
}
int keylen;
int inclen;
char* new_string = irjp_parse_string(state->root, str+1, &inclen, &keylen, &state->row, &state->column);
if(!new_string){
if(!keylen)
syntax_error("Cannot have empty key name!", state);
return -1;
}
state->lastadded = rjp_add_member(state->curr, new_string, keylen);
rjp_set_null(state->lastadded);
*state->target = rjp_json_target_colon;
return inclen+2;
//end of this object (object is empty)
}else if(c == '}'){
state->curr = state->curr->parent;
if(state->target != state->target_stack)
--state->target;
return 1;
//unrecognized character
}else if(!irjp_is_whitespace(c)){
syntax_error("Unexpected character, expected '\"'!", state);
return -1;
}
return 1;
}
int irjp_handle_colon(const char* str, RJP_parse_state* state){
char c = *str;
//colon after a key
if(c == ':'){
*state->target = rjp_json_target_value;
//unrecognized character
}else if(!irjp_is_whitespace(c)){
syntax_error( "Unexpected character, expected ':'!", state);
return -1;
}
return 1;
}
int irjp_handle_comma(const char* str, RJP_parse_state* state){
char c = *str;
//comma separating keys in an object or values in an array
if(c == ','){
*state->target = (state->curr->type == rjp_json_array ? rjp_json_target_value : rjp_json_target_key);
//end of object
}else if(c == '}'){
if(state->curr->type == rjp_json_array){
syntax_error("Unexpected end of object within array!", state);
return -1;
}
state->curr = state->curr->parent;
if(state->target != state->target_stack)
--state->target;
//end of array
}else if(c == ']' && state->curr->type == rjp_json_array){
state->curr = state->curr->parent;
//unrecognized character
}else if(!irjp_is_whitespace(c)){
syntax_error("Unexpected character, expected ','!", state);
return -1;
}
return 1;
}
int irjp_handle_value(const char* str, RJP_parse_state* state){
//object
char c = *str;
if(c == '{'){
if(!state->root){
state->root = irjp_add_value(NULL, NULL, irjp_object());
state->curr = state->root;
*state->target = rjp_json_target_key;
}else{
state->curr = irjp_add_value(state->curr, state->lastadded, irjp_object());
*state->target = rjp_json_target_comma;
++state->target;
*state->target = rjp_json_target_key;
}
return 1;
}
else if(c == '['){
if(!state->root){
state->root = irjp_add_value(NULL, NULL, irjp_array());
state->curr = state->root;
}else{
state->curr = irjp_add_value(state->curr, state->lastadded, irjp_array());
}
return 1;
}
else if(c == ']' && state->curr->type == rjp_json_array){ //empty array
*state->target = rjp_json_target_comma;
state->curr = state->curr->parent;
return 1;
}
//strings
else if(c == '"'){
int vallen, inclen;
char* new_string = irjp_parse_string(state->root, str+1, &inclen, &vallen, &state->row, &state->column);
if(!new_string){
if(vallen == 0){
new_string = rjp_calloc(1, 1);
}else{
return -1;
}
}
irjp_add_value(state->curr, state->lastadded, irjp_string(new_string, vallen));
*state->target = rjp_json_target_comma;
return inclen+2;
}
//numbers
else if((c >= '0' && c <= '9') || c == '-'){
if(!state->curr)
*state->target = rjp_json_target_none;
else
*state->target = rjp_json_target_comma;
int numlen;
int floating = 0; //is an int or a double
for(numlen = 1;*(str+numlen) >= '0' && *(str+numlen) <= '9';++numlen);
if(*(str+numlen) == '.'){ //if we have a decimal, make it a double and continue parsing as a number
int i = ++numlen;
for(;*(str+numlen) >= '0' && *(str+numlen) <= '9';++numlen);
if(i == numlen){ //no number after decimal
syntax_error("Missing numerals after decimal place!", state);
return -1;
}
floating = 1;
}
if(*(str+numlen) == '\0' && state->curr){ //hit EOF early
syntax_error("Unexpected EOF before end of object!", state);
return -1;
}
if(c == '-' && numlen == 1){ //only have a '-' with no numbers
syntax_error("Missing numerals after '-' sign!", state);
return -1;
}
if(floating){
if(!state->root){
state->root = state->curr = irjp_add_value(NULL, NULL, irjp_dfloat(strtod(str, NULL)));
}else{
irjp_add_value(state->curr, state->lastadded, irjp_dfloat(strtod(str, NULL)));
}
}else{
if(!state->root){
state->root = state->curr = irjp_add_value(NULL, NULL, irjp_integer(strtoll(str, NULL, 10)));
}else{
irjp_add_value(state->curr, state->lastadded, irjp_integer(strtoll(str, NULL, 10)));
}
}
state->column += numlen;
return numlen;
}
//booleans and null
else if(!strncmp(str, "true", 4)){
if(!state->curr){
*state->target = rjp_json_target_none;
state->root = state->curr = irjp_add_value(state->curr, NULL, irjp_boolean(1));
}else{
*state->target = rjp_json_target_comma;
irjp_add_value(state->curr, state->lastadded, irjp_boolean(1));
}
state->column += 3;
return 4;
}else if(!strncmp(str, "false", 5)){
if(!state->curr){
*state->target = rjp_json_target_none;
state->root = state->curr = irjp_add_value(state->curr, NULL, irjp_boolean(0));
}else{
*state->target = rjp_json_target_comma;
irjp_add_value(state->curr, state->lastadded, irjp_boolean(0));
}
state->column += 4;
return 5;
}else if(!strncmp(str, "null", 4)){
if(!state->curr){
*state->target = rjp_json_target_none;
state->root = state->curr = irjp_add_value(state->curr, NULL, irjp_null());
}else{
*state->target = rjp_json_target_comma;
irjp_add_value(state->curr, state->lastadded, irjp_null());
}
state->column += 3;
return 4;
}
//unrecognized character
else if(!irjp_is_whitespace(c)){
syntax_error("Unexpected character!", state);
return -1;
}
return 1;
}
RJP_value* rjp_parse(const char* str){
RJP_parse_state state;
irjp_init_parse_state(&state);
//initially search for the root object
*top = json_value;
*state.target = rjp_json_target_value;
for(;*str != '\0';++str){
int inc = 0;
for(;*str != '\0';str += inc){
char c = *str;
//keep track of position in input file
if(c == '\n'){
++row;
column = 0;
++state.row;
state.column = 0;
}else{
++column;
++state.column;
}
//Handle comments
if(in_line_comment){
if(c == '\n')
in_line_comment = 0;
}
else if(in_block_comment){
if(c == '*' && *(str+1) == '/'){
in_block_comment = 0;
++str;
}
}
else if(c == '/' && *(str+1) == '/'){
in_line_comment = 1;
++str;
}
else if(c == '/' && *(str+1) == '*'){
in_block_comment = 1;
++str;
if((inc = irjp_handle_comment(str, &state))){
continue;
}
else if(*top == json_key){
//start of key
if(c == '"'){
if(curr == NULL)
syntax_error("Key found outside of object definition!", row, column);
int keylen;
int inclen;
char* new_string = _rjp__parse_string(root, ++str, &inclen, &keylen, &row, &column);
if(!new_string){
if(!keylen)
syntax_error("Cannot have empty key name!", row, column);
return NULL;
}
_rjp__add_member_no_alloc(&curr->object, new_string, keylen);
str += inclen;
*top = json_colon;
//end of this object (object is empty)
}else if(c == '}'){
curr = curr->parent;
if(top != state_stack)
--top;
//unrecognized character
}else if(!_rjp__is_whitespace(c)){
syntax_error("Unexpected character, expected '\"'!", row, column);
switch(*state.target){
case rjp_json_target_key:
inc = irjp_handle_key(str, &state);
break;
case rjp_json_target_colon:
inc = irjp_handle_colon(str, &state);
break;
case rjp_json_target_comma:
inc = irjp_handle_comma(str, &state);
break;
case rjp_json_target_value:
inc = irjp_handle_value(str, &state);
break;
case rjp_json_target_none:
if(!irjp_is_whitespace(*str)){
syntax_error("Unexpected character!", &state);
return NULL;
}
}
else if(*top == json_colon){
//colon after a key
if(c == ':'){
*top = json_value;
//unrecognized character
}else if(!_rjp__is_whitespace(c)){
syntax_error( "Unexpected character, expected ':'!", row, column);
}
}
else if(*top == json_comma){
//comma separating keys in an object or values in an array
if(c == ','){
*top = (curr->type == json_array ? json_value : json_key);
//end of object
}else if(c == '}'){
if(curr->type == json_array){
syntax_error("Unexpected end of object within array!", row, column);
}
curr = curr->parent;
if(top != state_stack)
--top;
//end of array
}else if(c == ']' && curr->type == json_array){
curr = curr->parent;
//unrecognized character
}else if(!_rjp__is_whitespace(c)){
syntax_error("Unexpected character, expected ','!", row, column);
}
}
else if(*top == json_value){
//object
if(c == '{'){
if(!root){
root = _rjp__add_value(NULL, rjp_object());
curr = root;
*top = json_key;
}else{
curr = _rjp__add_value(curr, rjp_object());
*top = json_comma;
++top;
*top = json_key;
}
}
else if(c == '['){
if(!root){
root = _rjp__add_value(NULL, rjp_array());
curr = root;
}else{
curr = _rjp__add_value(curr, rjp_array());
}
}
else if(c == ']' && curr->type == json_array){ //empty array
*top = json_comma;
curr = curr->parent;
}
//strings
else if(c == '"'){
int vallen, inclen;
++str;
char* new_string = _rjp__parse_string(root, str, &inclen, &vallen, &row, &column);
if(!new_string){
if(vallen == 0){
new_string = rjp_calloc(1, 1);
}else{
return NULL;
}
}
_rjp__add_value(curr, rjp_string(new_string, vallen));
str += inclen;
*top = json_comma;
}
//numbers
else if((c >= '0' && c <= '9') || c == '-'){
if(!curr)
*top = json_none;
else
*top = json_comma;
int numlen;
int floating = 0; //is an int or a double
for(numlen = 1;*(str+numlen) >= '0' && *(str+numlen) <= '9';++numlen);
if(*(str+numlen) == '.'){ //if we have a decimal, make it a double and continue parsing as a number
int i = ++numlen;
for(;*(str+numlen) >= '0' && *(str+numlen) <= '9';++numlen);
if(i == numlen){ //no number after decimal
syntax_error("Missing numerals after decimal place!", row, column);
}
floating = 1;
}
if(*(str+numlen) == '\0' && curr){ //hit EOF early
syntax_error("Unexpected EOF before end of object!", row, column);
}
if(c == '-' && numlen == 1){ //only have a '-' with no numbers
syntax_error("Missing numerals ofter '-' sign!", row, column);
}
if(floating){
if(!root){
root = curr = _rjp__add_value(NULL, rjp_dfloat(strtod(str, NULL)));
}else{
_rjp__add_value(curr, rjp_dfloat(strtod(str, NULL)));
}
}else{
if(!root){
root = curr = _rjp__add_value(NULL, rjp_integer(strtol(str, NULL, 10)));
}else{
_rjp__add_value(curr, rjp_integer(strtol(str, NULL, 10)));
}
}
str += (numlen-1);
column += numlen;
}
//booleans and null
else if(!strncmp(str, "true", 4)){
if(!curr){
*top = json_none;
root = curr = _rjp__add_value(curr, rjp_boolean(1));
}else{
*top = json_comma;
_rjp__add_value(curr, rjp_boolean(1));
}
str += 3;column += 3;
}else if(!strncmp(str, "false", 5)){
if(!curr){
*top = json_none;
root = curr = _rjp__add_value(curr, rjp_boolean(0));
}else{
*top = json_comma;
_rjp__add_value(curr, rjp_boolean(0));
}
str += 4;column += 4;
}else if(!strncmp(str, "null", 4)){
if(!curr){
*top = json_none;
root = curr = _rjp__add_value(curr, rjp_null());
}else{
*top = json_comma;
_rjp__add_value(curr, rjp_null());
}
str += 3;column += 3;
}
//unrecognized character
else if(!_rjp__is_whitespace(c)){
syntax_error("Unexpected character!", row, column);
}
}else if(*top == json_none && !_rjp__is_whitespace(c)){
syntax_error("Unexpected character!", row, column);
}
inc = 1;
break;
default:
inc = 1;
break;
};
}
return root;
return state.root;
}
RJP_value* rjp_parse_chunked(const char* str, RJP_value* prev_chunk){
if(!prev_chunk){
return rjp_parse(str);
}
return NULL;
}
#undef syntax_error

View File

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

View File

@@ -1,6 +1,6 @@
/**
rjp
Copyright (C) 2018-2019 rexy712
Copyright (C) 2018-2020 rexy712
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -16,45 +16,165 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
#include "rjp.h"
#include "rjp_internal.h"
#include "memory.h"
#include "rjp_string.h"
#include "rjp_object.h"
#include "rjp_value.h"
#include <string.h> //strlen
#include <stdio.h> //sprintf
RJP_value* rjp_init_json(void){
RJP_value* rjp_new_null(void){
RJP_value* ret = rjp_calloc(1, sizeof(RJP_value));
ret->type = rjp_json_null;
return ret;
}
RJP_value* rjp_init_json_as(RJP_value value){
RJP_value* ret = rjp_alloc(sizeof(RJP_value));
*ret = value;
RJP_value* rjp_new_int(RJP_int val){
RJP_value* ret = rjp_calloc(1, sizeof(RJP_value));
ret->type = rjp_json_integer;
ret->integer = val;
return ret;
}
RJP_value* rjp_new_float(RJP_float val){
RJP_value* ret = rjp_calloc(1, sizeof(RJP_value));
ret->type = rjp_json_dfloat;
ret->dfloat = val;
return ret;
}
RJP_value* rjp_new_bool(RJP_bool val){
RJP_value* ret = rjp_calloc(1, sizeof(RJP_value));
ret->type = rjp_json_boolean;
ret->boolean = val;
return ret;
}
RJP_value* rjp_new_string(char* val, RJP_index length){
RJP_value* ret = rjp_calloc(1, sizeof(RJP_value));
ret->type = rjp_json_string;
ret->string.value = val;
ret->string.length = length;
return ret;
}
RJP_value* rjp_new_string_copy(const char* value, RJP_index length){
UNUSED_VARIABLE(length);
RJP_value* ret = rjp_calloc(1, sizeof(RJP_value));
ret->type = rjp_json_string;
RJP_index esclen = rjp_escape_strlen(value);
ret->string.value = rjp_alloc(esclen+1);
rjp_escape_strcpy(ret->string.value, value);
ret->string.value[esclen] = 0;
ret->string.length = esclen;
return ret;
}
RJP_value* rjp_new_object(void){
RJP_value* ret = rjp_calloc(1, sizeof(RJP_value));
ret->type = rjp_json_object;
return ret;
}
RJP_value* rjp_new_ordered_object(void){
RJP_value* ret = rjp_calloc(1, sizeof(RJP_value));
ret->type = rjp_json_ordered_object;
return ret;
}
RJP_value* rjp_new_array(void){
RJP_value* ret = rjp_calloc(1, sizeof(RJP_value));
ret->type = rjp_json_array;
return ret;
}
static size_t _rjp__write_value(char* dest, RJP_value* val){
size_t ret;
RJP_value* rjp_set_null(RJP_value* v){
irjp_delete_value(v);
v->type = rjp_json_null;
return v;
}
RJP_value* rjp_set_int(RJP_value* v, RJP_int val){
irjp_delete_value(v);
v->type = rjp_json_integer;
v->integer = val;
return v;
}
RJP_value* rjp_set_float(RJP_value* v, RJP_float val){
irjp_delete_value(v);
v->type = rjp_json_dfloat;
v->dfloat = val;
return v;
}
RJP_value* rjp_set_bool(RJP_value* v, RJP_bool val){
irjp_delete_value(v);
v->type = rjp_json_boolean;
v->boolean = val;
return v;
}
RJP_value* rjp_set_string(RJP_value* v, char* val, RJP_index len){
irjp_delete_value(v);
v->type = rjp_json_string;
v->string.value = val;
v->string.length = len;
return v;
}
RJP_value* rjp_set_string_copy(RJP_value* v, const char* val, RJP_index length){
UNUSED_VARIABLE(length);
irjp_delete_value(v);
v->type = rjp_json_string;
RJP_index esclen = rjp_escape_strlen(val);
v->string.value = rjp_alloc(esclen+1);
rjp_escape_strcpy(v->string.value, val);
v->string.value[esclen] = 0;
v->string.length = esclen;
return v;
}
RJP_value* rjp_set_object(RJP_value* v){
if(v->type == rjp_json_object)
return v;
irjp_delete_value(v);
memset(&v->object, 0, sizeof(RJP_unordered_object));
v->type = rjp_json_object;
return v;
}
RJP_value* rjp_set_ordered_object(RJP_value* v){
if(v->type == rjp_json_ordered_object)
return v;
irjp_delete_value(v);
memset(&v->orobject, 0, sizeof(RJP_ordered_object));
v->type = rjp_json_ordered_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->array, 0, sizeof(RJP_array));
v->type = rjp_json_array;
return v;
}
static RJP_index irjp_write_value(char* dest, const RJP_value* val){
RJP_index ret;
switch(val->type){
case json_integer:
ret = sprintf(dest, "%li", val->integer);
case rjp_json_integer:
ret = sprintf(dest, "%" PRId64, val->integer);
break;
case json_dfloat:
case rjp_json_dfloat:
ret = sprintf(dest, "%lf", val->dfloat);
break;
case json_boolean:
case rjp_json_boolean:
ret = sprintf(dest, val->boolean ? "true" : "false");
break;
case json_null:
case rjp_json_null:
ret = sprintf(dest, "null");
break;
case json_string:;
case rjp_json_string:;
ret = sprintf(dest, "\"%s\"", val->string.value);
break;
case json_array:
case rjp_json_array:
ret = rjp_dump_array(val, dest);
break;
case json_object:
case rjp_json_object:
case rjp_json_ordered_object:
ret = rjp_dump_object(val, dest);
break;
default:
@@ -63,53 +183,148 @@ static size_t _rjp__write_value(char* dest, RJP_value* val){
};
return ret;
}
static RJP_index irjp_write_value_pretty(char* dest, const RJP_value* val, int depth);
static RJP_index irjp_dump_array_pretty(const RJP_value* arr, char* dest, int depth){
RJP_array_iterator it;
rjp_init_array_iterator(&it, arr);
RJP_index pos = 0;
RJP_value* current = rjp_array_iterator_current(&it);
size_t rjp_dump_array(RJP_value* arr, char* dest){
RJP_array* array = &arr->array;
RJP_array_element* element_list = array->elements;
size_t pos = 1;
if(!current){
pos += sprintf(dest, "[]");
rjp_delete_array_iterator(&it);
return pos;
}
pos += sprintf(dest+pos, "[\n");
for(;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, "]");
rjp_delete_array_iterator(&it);
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 = 0;
RJP_value* current = rjp_object_iterator_current(&it);
if(!current){
pos += sprintf(dest+pos, "{}");
rjp_delete_object_iterator(&it);
return pos;
}
pos += sprintf(dest, "{\n");
for(;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:
case rjp_json_ordered_object:
ret = irjp_dump_object_pretty(val, dest, depth);
break;
default:
ret = 0;
break;
};
return ret;
}
RJP_index rjp_dump_array(const RJP_value* arr, char* dest){
RJP_array_iterator it;
rjp_init_array_iterator(&it, arr);
RJP_index pos = 1;
sprintf(dest, "[");
for(;element_list;element_list = element_list->next){
pos += _rjp__write_value(dest+pos, &element_list->value);
for(RJP_value* current = rjp_array_iterator_current(&it);current;current = rjp_array_iterator_next(&it)){
pos += irjp_write_value(dest+pos, current);
if(element_list->next)
if(rjp_array_iterator_peek(&it))
pos += sprintf(dest+pos, ",");
else
break;
}
pos += sprintf(dest+pos, "]");
rjp_delete_array_iterator(&it);
return pos;
}
size_t rjp_dump_object(RJP_value* root, char* dest){
RJP_object* root_obj = &root->object;
RJP_object_member* member_list = root_obj->members;
size_t pos = 1;
RJP_index rjp_dump_object(const RJP_value* root, char* dest){
RJP_object_iterator it;
rjp_init_object_iterator(&it, root);
RJP_index pos = 1;
sprintf(dest, "{");
for(;member_list;member_list = member_list->next){
pos += sprintf(dest+pos, "\"%s\":", member_list->name.value);
pos += _rjp__write_value(dest+pos, &member_list->value);
for(RJP_value* current = rjp_object_iterator_current(&it);current;current = rjp_object_iterator_next(&it)){
pos += sprintf(dest+pos, "\"%s\":", rjp_member_key(current)->value);
pos += irjp_write_value(dest+pos, current);
if(member_list->next)
if(rjp_object_iterator_peek(&it))
pos += sprintf(dest+pos, ",");
else
break;
}
pos += sprintf(dest+pos, "}");
rjp_delete_object_iterator(&it);
return pos;
}
char* rjp_to_json(RJP_value* root){
char* rjp_to_json(const RJP_value* root, int pretty){
if(!root)
return NULL;
size_t len = _rjp__value_strlen(root);
RJP_index len = pretty ? irjp_value_strlen_pretty(root, 0) : irjp_value_strlen(root);
if(!len)
return NULL;
char* tmp = rjp_alloc(len + 1);
tmp[len] = 0;
_rjp__write_value(tmp, root);
if(pretty)
irjp_write_value_pretty(tmp, root, 0);
else
irjp_write_value(tmp, root);
return tmp;
}

320
src/rjp.c
View File

@@ -1,6 +1,6 @@
/**
rjp
Copyright (C) 2018-2019 rexy712
Copyright (C) 2018-2020 rexy712
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -17,218 +17,152 @@
*/
#include "rjp_internal.h"
#include "rjp_string.h"
#include "rjp_object.h"
#include "rjp_value.h"
#include <string.h> //strncpy, strlen
#include <stdlib.h> //free, malloc
void _rjp__add_element(RJP_array* j){
++j->num_elements;
if(!j->elements){
j->elements = rjp_calloc(1, sizeof(RJP_array_element));
j->last = j->elements;
void* rjp_alloc(RJP_index nbytes){
return malloc(nbytes);
}
void* rjp_calloc(RJP_index num, RJP_index nbytes){
return calloc(num, nbytes);
}
void* rjp_realloc(void* ptr, RJP_index nbytes){
return realloc(ptr, nbytes);
}
void rjp_free(void* data){
free(data);
}
RJP_value* rjp_copy_value(RJP_value* dest, const RJP_value* src){
RJP_value* ret;
if(dest){
irjp_delete_value(dest);
ret = dest;
}else{
j->last->next = rjp_calloc(1, sizeof(RJP_array_element));
j->last = j->last->next;
ret = rjp_new_null();
}
}
//create member of the object as a linked list member and assign a name with name allocation
void _rjp__add_member(RJP_object* j, const char* str, size_t len){
++j->num_members;
if(!j->members){
j->members = rjp_calloc(1, sizeof(RJP_object_member));
j->last = j->members;
}else{
j->last->next = rjp_calloc(1, sizeof(RJP_object_member));
j->last = j->last->next;
}
j->last->name.value = rjp_alloc(len + 1);
strncpy(j->last->name.value, str, len);
j->last->name.value[len] = 0;
j->last->name.length = len;
}
void _rjp__add_member_no_alloc(RJP_object* j, char* str, size_t len){
++j->num_members;
if(!j->members){
j->members = rjp_calloc(1, sizeof(RJP_object_member));
j->last = j->members;
}else{
j->last->next = rjp_calloc(1, sizeof(RJP_object_member));
j->last = j->last->next;
}
j->last->name.value = str;
j->last->name.length = len;
}
ret->type = src->type;
RJP_value* rjp_add_member(RJP_value* dest, const char* key, size_t keylen, RJP_value value){
if(!keylen)
keylen = strlen(key);
_rjp__add_member(&dest->object, key, keylen);
dest->object.last->value = value;
dest->object.last->value.parent = dest;
return &dest->object.last->value;
}
RJP_value* rjp_add_member_no_alloc(RJP_value* dest, char* key, size_t keylen, RJP_value value){
if(!keylen)
keylen = strlen(key);
_rjp__add_member_no_alloc(&dest->object, key, keylen);
dest->object.last->value = value;
dest->object.last->value.parent = dest;
return &dest->object.last->value;
}
RJP_value* rjp_add_element(RJP_value* dest, RJP_value value){
_rjp__add_element(&dest->array);
dest->array.last->value = value;
dest->array.last->value.parent = dest;
return &dest->array.last->value;
}
void rjp_set_value(RJP_value* dest, RJP_value value){
struct RJP_value* p = dest->parent;
*dest = value;
dest->parent = p;
}
RJP_value rjp_integer(long i){
return (RJP_value){.integer = i, .type = json_integer};
}
RJP_value rjp_boolean(char b){
return (RJP_value){.boolean = b, .type = json_boolean};
}
RJP_value rjp_dfloat(double d){
return (RJP_value){.dfloat = d, .type = json_dfloat};
}
RJP_value rjp_string(char* c, size_t len){
return (RJP_value){.string = {.value = c, .length = len}, .type = json_string};
}
RJP_value rjp_null(void){
return (RJP_value){.integer = 0, .type = json_null};
}
RJP_value rjp_object(void){
return (RJP_value){.object = {0}, .type = json_object};
}
RJP_value rjp_array(void){
return (RJP_value){.array = {0}, .type = json_array};
}
RJP_value* rjp_get_member(const RJP_value* object){
return &object->object.members->value;
}
RJP_value* rjp_next_member(const RJP_value* member){
//polymorphism
return (RJP_value*)(((RJP_object_member*)member)->next);
}
size_t rjp_num_members(const RJP_value* object){
return object->object.num_members;
}
char* rjp_member_name(const RJP_value* member){
return ((RJP_object_member*)member)->name.value;
}
size_t rjp_member_name_length(RJP_value* member){
return ((RJP_object_member*)member)->name.length;
}
RJP_search_res rjp_search_member(const RJP_value* object, const char* search, size_t skip){
RJP_value* member = rjp_get_member(object);
size_t i = 0;
for(;i < skip && member;member = rjp_next_member(member),++i);
for(;member;member = rjp_next_member(member),++i){
if(!strcmp(((RJP_object_member*)member)->name.value, search)){
return (RJP_search_res){member, 0, i};
}
}
return (RJP_search_res){0};
}
size_t rjp_search_members(const RJP_value* object, size_t num, const char* const* searches, RJP_search_res* dest, size_t skip){
const char** tmp = rjp_alloc(num*sizeof(char*));
for(size_t i = 0;i < num;++i){
tmp[i] = searches[i];
dest[i] = (RJP_search_res){0};
}
RJP_value* member = rjp_get_member(object);
size_t objindex = 0;
for(;objindex < skip && member;member = rjp_next_member(member),++objindex);
size_t matches = 0;
for(;member;member = rjp_next_member(member),++objindex){
for(size_t i = 0;i < num;++i){
if(tmp[i] == NULL)
continue;
if(!strcmp(((RJP_object_member*)member)->name.value, tmp[i])){
dest[i].value = member;
dest[i].searchindex = i;
dest[i].objindex = objindex;
tmp[i] = NULL;
++matches;
break;
}
}
}
rjp_free(tmp);
return matches;
}
RJP_search_res* rjp_search_members_multi(const RJP_value* object, size_t num, const char* const* searches, size_t* rsize, size_t skip){
RJP_search_res* ret = rjp_alloc(num*sizeof(RJP_search_res));
size_t ret_size = num;
size_t j = 0;
RJP_value* member = rjp_get_member(object);
size_t objindex = 0;
for(;objindex < skip && member;member = rjp_next_member(member),++objindex);
for(;member;member = rjp_next_member(member),++objindex){
for(size_t i = 0;i < num;++i){
if(!strcmp(((RJP_object_member*)member)->name.value, searches[i])){
ret[j].value = member;
ret[j].searchindex = i;
ret[j].objindex = objindex;
++j;
if(j == ret_size){
ret_size *= 2;
ret = rjp_realloc(ret, ret_size*sizeof(RJP_search_res));
}
break;
}
}
}
if(rsize)
*rsize = j;
if(j == 0){
rjp_free(ret);
return NULL;
}
switch(src->type){
case rjp_json_object:
irjp_copy_object(ret, src);
break;
case rjp_json_ordered_object:
irjp_copy_ordered_object(ret, src);
break;
case rjp_json_array:
irjp_copy_array(ret, src);
break;
case rjp_json_string:
irjp_strcpy(&ret->string, &src->string);
break;
case rjp_json_integer:
ret->integer = src->integer;
break;
case rjp_json_boolean:
ret->boolean = src->boolean;
break;
case rjp_json_dfloat:
ret->dfloat = src->dfloat;
break;
default:
break;
};
return ret;
}
RJP_value* rjp_get_element(const RJP_value* array){
return &array->array.elements->value;
}
RJP_value* rjp_next_element(const RJP_value* element){
return (RJP_value*)(((RJP_array_element*)element)->next);
}
size_t rjp_num_elements(const RJP_value* array){
return array->array.num_elements;
RJP_value* rjp_move_value(RJP_value* dest, RJP_value* src){
*dest = *src;
return dest;
}
RJP_value* rjp_value_parent(RJP_value* value){
static void irjp_delete_array(RJP_value* root){
RJP_array_iterator it;
rjp_init_array_iterator(&it, root);
RJP_value* next = rjp_array_iterator_current(&it);
for(RJP_value* current = next;current;current = next){
next = rjp_array_iterator_next(&it);
rjp_free_value(current);
}
}
void irjp_delete_value(RJP_value* root){
switch(root->type){
case rjp_json_object:
case rjp_json_ordered_object:
irjp_delete_object(root);
break;
case rjp_json_array:
irjp_delete_array(root);
break;
case rjp_json_string:
rjp_free(root->string.value);
break;
default: break;
};
}
void rjp_free_value(RJP_value* root){
if(!root)
return;
irjp_delete_value(root);
free(root);
}
/* VALUE SETTING */
/* GETTERS */
RJP_value* rjp_value_parent(const RJP_value* value){
return value->parent;
}
RJP_type rjp_value_type(RJP_value* value){
RJP_data_type rjp_value_type(const RJP_value* value){
return value->type;
}
double rjp_value_dfloat(RJP_value* value){
RJP_float rjp_get_float(const RJP_value* value){
return value->dfloat;
}
int rjp_value_integer(RJP_value* value){
RJP_int rjp_get_int(const RJP_value* value){
return value->integer;
}
char rjp_value_boolean(RJP_value* value){
RJP_bool rjp_get_bool(const RJP_value* value){
return value->boolean;
}
char* rjp_value_string(RJP_value* value){
return value->string.value;
RJP_string* rjp_get_string(RJP_value* value){
return &(value->string);
}
size_t rjp_value_string_length(RJP_value* value){
return value->string.length;
const RJP_string* rjp_get_cstring(const RJP_value* value){
return &(value->string);
}
RJP_value irjp_integer(RJP_int i){
return (RJP_value){.integer = i, .type = rjp_json_integer};
}
RJP_value irjp_boolean(RJP_bool b){
return (RJP_value){.boolean = b, .type = rjp_json_boolean};
}
RJP_value irjp_dfloat(RJP_float d){
return (RJP_value){.dfloat = d, .type = rjp_json_dfloat};
}
RJP_value irjp_string(char* c, RJP_index len){
return (RJP_value){.string = {.value = c, .length = len}, .type = rjp_json_string};
}
RJP_value irjp_string_copy(const char* c){
RJP_index esclen = rjp_escape_strlen(c);
char* tmp = rjp_alloc(esclen+1);
rjp_escape_strcpy(tmp, c);
return (RJP_value){.string = {.value = tmp, .length = esclen}, .type = rjp_json_string};
}
RJP_value irjp_null(void){
return (RJP_value){.integer = 0, .type = rjp_json_null};
}
RJP_value irjp_object(void){
return (RJP_value){.object = {0}, .type = rjp_json_object};
}
RJP_value irjp_ordered_object(void){
return (RJP_value){.object = {0}, .type = rjp_json_ordered_object};
}
RJP_value irjp_array(void){
return (RJP_value){.array = {0}, .type = rjp_json_array};
}

94
src/rjp_array.c Normal file
View File

@@ -0,0 +1,94 @@
/**
rjp
Copyright (C) 2018-2020 rexy712
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "rjp_array.h"
#include "rjp_internal.h"
#include "rjp_value.h"
#include "rjp_array_element.h"
void irjp_copy_array(RJP_value* dest, const RJP_value* src){
dest->array.elements = dest->array.last = NULL;
for(RJP_array_element* curr = src->array.elements;curr;curr = curr->next){
RJP_value* copy_mem;
copy_mem = rjp_add_element(dest);
rjp_copy_value(copy_mem, &curr->value);
}
}
void irjp_add_element(RJP_array* j){
++j->num_elements;
if(!j->elements){
j->elements = rjp_calloc(1, sizeof(RJP_array_element));
j->last = j->elements;
}else{
j->last->next = rjp_calloc(1, sizeof(RJP_array_element));
j->last = j->last->next;
j->last->prev = j->last;
}
}
RJP_value* rjp_add_element(RJP_value* dest){
irjp_add_element(&dest->array);
dest->array.last->value.parent = dest;
return &dest->array.last->value;
}
RJP_value* rjp_remove_element(RJP_value* dest, RJP_value* value){
if(value->parent != dest)
return NULL;
RJP_array* j = &dest->array;
--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;
}

253
src/rjp_object.c Normal file
View File

@@ -0,0 +1,253 @@
/**
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 <string.h> //strlen, strncpy
void irjp_copy_object(RJP_value* dest, const RJP_value* src){
switch(src->type){
case rjp_json_object:
irjp_copy_unordered_object(dest, src);
break;
case rjp_json_ordered_object:
irjp_copy_ordered_object(dest, src);
break;
default: break;
};
}
void irjp_delete_object(RJP_value* obj){
switch(obj->type){
case rjp_json_object:
irjp_delete_unordered_object(obj);
break;
case rjp_json_ordered_object:
irjp_delete_ordered_object(obj);
break;
default: break;
};
}
RJP_value* rjp_add_member(RJP_value* dest, char* key, RJP_index keylen){
switch(dest->type){
case rjp_json_object:
return irjp_add_unordered_member(dest, key, keylen);
case rjp_json_ordered_object:
return irjp_add_ordered_member(dest, key, keylen);
default: break;
};
return NULL;
}
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 rjp_add_member(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){
if(member->parent != obj)
return NULL;
switch(obj->type){
case rjp_json_object:
return irjp_remove_unordered_member(obj, member);
case rjp_json_ordered_object:
return irjp_remove_ordered_member(obj, member);
default: break;
};
return NULL;
}
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;
}
switch(dest->parent->type){
case rjp_json_object:
irjp_unordered_set_key(dest, key, keylen);
break;
case rjp_json_ordered_object:
irjp_ordered_set_key(dest, key, keylen);
break;
default: break;
};
}
RJP_index rjp_num_members(const RJP_value* object){
switch(object->type){
case rjp_json_object:
return irjp_unordered_num_members(object);
case rjp_json_ordered_object:
return irjp_ordered_num_members(object);
default: break;
};
return 0;
}
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){
switch(object->type){
case rjp_json_object:
return irjp_unordered_search_member(object, search);
case rjp_json_ordered_object:
return irjp_ordered_search_member(object, search);
default: break;
};
return NULL;
}
void rjp_init_object_iterator(RJP_object_iterator* it, const RJP_value* object){
switch(object->type){
case rjp_json_object:
irjp_init_unordered_object_iterator(it, object);
it->type = rjp_json_object;
break;
case rjp_json_ordered_object:
irjp_init_ordered_object_iterator(it, object);
it->type = rjp_json_ordered_object;
break;
default: break;
};
}
void rjp_delete_object_iterator(RJP_object_iterator* it){
switch(it->type){
case rjp_json_object:
irjp_delete_unordered_object_iterator(it);
break;
case rjp_json_ordered_object:
irjp_delete_ordered_object_iterator(it);
break;
default: break;
};
}
RJP_value* rjp_object_iterator_current(const RJP_object_iterator* it){
switch(it->type){
case rjp_json_object:
return irjp_unordered_object_iterator_current(it);
break;
case rjp_json_ordered_object:
return irjp_ordered_object_iterator_current(it);
break;
default: break;
};
return NULL;
}
RJP_value* rjp_object_iterator_next(RJP_object_iterator* it){
switch(it->type){
case rjp_json_object:
return irjp_unordered_object_iterator_next(it);
break;
case rjp_json_ordered_object:
return irjp_ordered_object_iterator_next(it);
break;
default: break;
};
return NULL;
}
RJP_value* rjp_object_iterator_peek(const RJP_object_iterator* it){
switch(it->type){
case rjp_json_object:
return irjp_unordered_object_iterator_peek(it);
break;
case rjp_json_ordered_object:
return irjp_ordered_object_iterator_peek(it);
break;
default: break;
};
return NULL;
}
RJP_value* rjp_object_to_ordered(RJP_value* object){
if(object->type == rjp_json_ordered_object)
return object;
RJP_value newobj = {0};
newobj.type = rjp_json_ordered_object;
RJP_object_iterator it;
irjp_init_unordered_object_iterator(&it, object);
RJP_value* next = irjp_unordered_object_iterator_current(&it);
for(RJP_value* current = next;current;current = next){
RJP_value* newcur = irjp_add_ordered_member(&newobj, rjp_member_key(current)->value, rjp_member_key(current)->length);
((RJP_object_member*)current)->name.value = NULL;
rjp_move_value(newcur, current);
next = irjp_unordered_object_iterator_next(&it);
rjp_free(current);
}
irjp_delete_unordered_object_iterator(&it);
object->type = rjp_json_ordered_object;
object->orobject = newobj.orobject;
return object;
}
RJP_value* rjp_object_to_unordered(RJP_value* object){
if(object->type == rjp_json_object)
return object;
RJP_value newobj = {0};
newobj.type = rjp_json_object;
RJP_object_iterator it;
irjp_init_ordered_object_iterator(&it, object);
RJP_value* next = irjp_ordered_object_iterator_current(&it);
for(RJP_value* current = next;current;current = next){
RJP_value* newcur = irjp_add_unordered_member(&newobj, rjp_member_key(current)->value, rjp_member_key(current)->length);
((RJP_object_member*)current)->name.value = NULL;
rjp_move_value(newcur, current);
next = irjp_ordered_object_iterator_next(&it);
rjp_free(current);
}
irjp_delete_ordered_object_iterator(&it);
object->type = rjp_json_object;
object->object = newobj.object;
return object;
}

144
src/rjp_ordered_object.c Normal file
View File

@@ -0,0 +1,144 @@
/**
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_ordered_object.h"
#include "rjp_object_member.h"
#include "rjp_value.h"
#include "rjp_string.h"
#include <string.h> //strcmp
typedef struct RJP_ordered_member{
RJP_object_member member;
struct RJP_ordered_member* prev;
struct RJP_ordered_member* next;
}RJP_ordered_member;
static void irjp_copy_ordered_member(RJP_ordered_member* dest, const RJP_ordered_member* src){
irjp_strcpy(&dest->member.name, &src->member.name);
rjp_copy_value(&dest->member.value, &src->member.value);
}
void irjp_copy_ordered_object(RJP_value* dest, const RJP_value* src){
irjp_delete_ordered_object(dest);
dest->orobject.num_members = 0;
RJP_object_iterator it;
irjp_init_ordered_object_iterator(&it, src);
for(const RJP_value* current = irjp_ordered_object_iterator_current(&it);current;current = irjp_ordered_object_iterator_next(&it)){
RJP_string keycopy;
irjp_strcpy(&keycopy, &((RJP_object_member*)current)->name);
RJP_ordered_member* newmemb = (RJP_ordered_member*)irjp_add_ordered_member(dest, keycopy.value, keycopy.length);
irjp_copy_ordered_member(newmemb, (RJP_ordered_member*)current);
}
irjp_delete_ordered_object_iterator(&it);
}
void irjp_delete_ordered_object(RJP_value* obj){
RJP_object_iterator it;
irjp_init_ordered_object_iterator(&it, obj);
RJP_value* next = irjp_ordered_object_iterator_current(&it);
for(RJP_value* current = next;current;current = next){
next = irjp_ordered_object_iterator_next(&it);
rjp_free(((RJP_object_member*)current)->name.value);
rjp_free_value(current);
}
irjp_delete_ordered_object_iterator(&it);
}
static RJP_ordered_member* irjp_append_ordered_member(RJP_ordered_object* dest, RJP_ordered_member* newmemb){
++dest->num_members;
if(!dest->members){
dest->members = dest->last = newmemb;
newmemb->next = newmemb->prev = NULL;
return dest->members;
}
dest->last->next = newmemb;
newmemb->prev = dest->last;
newmemb->next = NULL;
dest->last = newmemb;
return newmemb;
}
RJP_value* irjp_add_ordered_member(RJP_value* dest, char* key, RJP_index keylen){
RJP_ordered_member* newmemb = rjp_calloc(1, sizeof(RJP_ordered_member));
irjp_append_ordered_member((RJP_ordered_object*)dest, newmemb);
irjp_ordered_set_key((RJP_value*)newmemb, key, keylen);
((RJP_value*)newmemb)->parent = dest;
((RJP_value*)newmemb)->type = rjp_json_null;
return (RJP_value*)newmemb;
}
RJP_value* irjp_remove_ordered_member(RJP_value* obj, RJP_value* member){
RJP_ordered_member* current = (RJP_ordered_member*)member;
RJP_ordered_member* prev = current->prev;
RJP_ordered_member* next = current->next;
--obj->orobject.num_members;
if(prev)
prev->next = next;
else
obj->orobject.members = next;
if(next)
next->prev = prev;
else
obj->orobject.last = prev;
member->parent = NULL;
return member;
}
void irjp_ordered_set_key(RJP_value* dest, char* key, RJP_index keylen){
RJP_object_member* mem = (RJP_object_member*)dest;
rjp_free(mem->name.value);
mem->name.value = key;
mem->name.length = keylen;
}
RJP_index irjp_ordered_num_members(const RJP_value* object){
return object->orobject.num_members;
}
RJP_value* irjp_ordered_search_member(const RJP_value* object, const char* search){
RJP_value* retval = NULL;
RJP_object_iterator it;
irjp_init_ordered_object_iterator(&it, object);
for(RJP_value* current = irjp_ordered_object_iterator_current(&it);current;current = irjp_ordered_object_iterator_next(&it)){
if(!strcmp(((RJP_object_member*)current)->name.value, search)){
retval = current;
break;
}
}
irjp_delete_ordered_object_iterator(&it);
return retval;
}
void irjp_init_ordered_object_iterator(RJP_object_iterator* it, const RJP_value* object){
it->current = &(object->orobject.members->member.value);
it->type = rjp_json_ordered_object;
}
void irjp_delete_ordered_object_iterator(RJP_object_iterator* it){
it->current = NULL;
}
RJP_value* irjp_ordered_object_iterator_current(const RJP_object_iterator* it){
return it->current;
}
RJP_value* irjp_ordered_object_iterator_next(RJP_object_iterator* it){
RJP_ordered_member* curr = (RJP_ordered_member*)it->current;
it->current = curr ? &(curr->next->member.value) : NULL;
return it->current;
}
RJP_value* irjp_ordered_object_iterator_peek(const RJP_object_iterator* it){
RJP_ordered_member* curr = (RJP_ordered_member*)it->current;
return curr ? &(curr->next->member.value) : NULL;
}

View File

@@ -1,6 +1,6 @@
/**
rjp
Copyright (C) 2018-2019 rexy712
Copyright (C) 2018-2020 rexy712
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -16,8 +16,13 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "strings.h"
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
#include "rjp_string.h"
#include "rjp_internal.h"
#include "rjp_object.h"
#include "rjp_value.h"
#include <stdio.h> //fprintf
#include <stdlib.h> //malloc, free
@@ -25,13 +30,13 @@
#include <string.h> //strcpy
//Determine if the character is valid whitespace
int _rjp__is_whitespace(char c){
int irjp_is_whitespace(char c){
return c == ' ' || c == '\n' || c == '\r' || c == '\t';
}
static uint32_t utf_strtol_4(const char* c){
uint32_t ret = 0;
for(size_t i = 0;i < 4;++i){
for(RJP_index i = 0;i < 4;++i){
if(c[i] >= '0' && c[i] <= '9'){
ret |= ((c[i] ^ 0x30) << (4*(3-i)));
}else if(c[i] >= 'A' && c[i] <= 'F'){
@@ -116,7 +121,7 @@ static int codepoint_to_u8(char* dest, uint32_t codepoint){
return 0;
}
}
static uint32_t u8_to_codepoint(char* u){
/*static uint32_t u8_to_codepoint(char* u){
if((u[0] & 0x80) == 0){
//one byte
return u[0];
@@ -145,10 +150,10 @@ static uint32_t u8_to_codepoint(char* u){
//invalid
return 0;
}
}
}*/
//Convert escape sequences in strings
char* _rjp__parse_string(RJP_value* root, const char* str, int* inclen, int* len, int* row, int* column){
char* irjp_parse_string(RJP_value* root, const char* str, int* inclen, int* len, int* row, int* column){
char* new_string;
++(*column); //account for starting quotation mark
int oldpos = 0;
@@ -228,9 +233,9 @@ char* _rjp__parse_string(RJP_value* root, const char* str, int* inclen, int* len
return new_string;
}
size_t rjp_escape_strcpy(char* dest, const char* src){
size_t j = 0;
for(size_t i = 0;src[i];++i){
RJP_index rjp_escape_strcpy(char* dest, const char* src){
RJP_index j = 0;
for(RJP_index i = 0;src[i];++i){
switch(src[i]){
case '"':
dest[j++] = '\\';
@@ -268,16 +273,16 @@ size_t rjp_escape_strcpy(char* dest, const char* src){
dest[j] = 0;
return j;
}
void _rjp__strcpy(RJP_string* dest, const RJP_string* src){
void irjp_strcpy(RJP_string* dest, const RJP_string* src){
dest->value = rjp_alloc(src->length + 1);
strcpy(dest->value, src->value);
dest->value[src->length] = 0;
dest->length = src->length;
}
size_t rjp_escape_strlen(const char* str){
size_t count = 0;
for(size_t i = 0;str[i];++i){
RJP_index rjp_escape_strlen(const char* str){
RJP_index count = 0;
for(RJP_index i = 0;str[i];++i){
switch(str[i]){
case '"':
case '\n':
@@ -294,75 +299,115 @@ size_t rjp_escape_strlen(const char* str){
}
return count;
}
size_t _rjp__array_strlen(RJP_value* arr){
size_t count = 2; //[]
RJP_array* array = &arr->array;
RJP_array_element* element_list = array->elements;
for(;element_list;element_list = element_list->next){
switch(element_list->value.type){
case json_integer:
count += snprintf(NULL, 0, "%li", element_list->value.integer);
break;
case json_dfloat:
count += snprintf(NULL, 0, "%lf", element_list->value.dfloat);
break;
case json_boolean:
count += element_list->value.boolean ? 4 : 5;
break;
case json_null:
count += 4;
break;
case json_string:
count += element_list->value.string.length;
break;
case json_array:
count += _rjp__array_strlen(&element_list->value);
break;
case json_object:
count += _rjp__object_strlen(&element_list->value);
break;
};
if(element_list->next)
++count;
else
break;
}
return count;
}
RJP_index irjp_array_strlen(const RJP_value* arr){
RJP_index count = 2; //[]
RJP_array_iterator it;
rjp_init_array_iterator(&it, arr);
size_t _rjp__object_strlen(RJP_value* root){
size_t count = 2; //{}
RJP_object* root_obj = &root->object;
RJP_object_member* member_list = root_obj->members;
for(;member_list;member_list = member_list->next){
if(member_list->name.length == 0 || member_list->name.value[0] == 0)
return 0;
count += member_list->name.length + 3; //"":
count += _rjp__value_strlen(&member_list->value);
if(member_list->next)
for(RJP_value* current = rjp_array_iterator_current(&it);current;current = rjp_array_iterator_next(&it)){
count += irjp_value_strlen(current);
if(rjp_array_iterator_peek(&it))
++count; //,
}
return count;
}
RJP_index irjp_array_strlen_pretty(const RJP_value* arr, int depth){
RJP_index count = 3 + depth; //[\n\t]
++depth;
RJP_array_iterator it;
rjp_init_array_iterator(&it, arr);
for(RJP_value* current = rjp_array_iterator_current(&it);current;current = rjp_array_iterator_next(&it)){
count += irjp_value_strlen_pretty(current, depth);
count += depth; //tabs
++count; //newline
if(rjp_array_iterator_peek(&it))
++count; //,
else
break;
}
return count;
}
size_t _rjp__value_strlen(RJP_value* root){
RJP_index irjp_object_strlen(const RJP_value* root){
RJP_index count = 2; //{}
RJP_object_iterator it;
rjp_init_object_iterator(&it, root);
RJP_value* current = rjp_object_iterator_current(&it);
while(current){
RJP_index namelen = rjp_member_key(current)->length;
const char* name = rjp_member_key(current)->value;
if(namelen == 0 || name[0] == 0){
rjp_delete_object_iterator(&it);
return 0;
}
count += namelen + 3; //"":
count += irjp_value_strlen(current);
if((current = rjp_object_iterator_next(&it)))
++count; //,
}
rjp_delete_object_iterator(&it);
return count;
}
RJP_index irjp_object_strlen_pretty(const RJP_value* root, int depth){
RJP_index count = 3 + depth; //{\n\t}
++depth;
RJP_object_iterator it;
rjp_init_object_iterator(&it, root);
RJP_value* current = rjp_object_iterator_current(&it);
while(current){
RJP_index namelen = rjp_member_key(current)->length;
const char* name = rjp_member_key(current)->value;
if(namelen == 0 || name[0] == 0){
rjp_delete_object_iterator(&it);
return 0;
}
count += namelen + 4; //"":space
count += irjp_value_strlen_pretty(current, depth);
count += depth; //tabs
++count; //newline
if((current = rjp_object_iterator_next(&it)))
++count; //,
}
rjp_delete_object_iterator(&it);
return count;
}
RJP_index irjp_value_strlen(const RJP_value* root){
switch(root->type){
case json_integer:
return snprintf(NULL, 0, "%li", root->integer);
case json_dfloat:
case rjp_json_integer:
return snprintf(NULL, 0, "%" PRId64, root->integer);
case rjp_json_dfloat:
return snprintf(NULL, 0, "%lf", root->dfloat);
case json_boolean:
case rjp_json_boolean:
return root->boolean ? 4 : 5; //true, false
case json_null:
case rjp_json_null:
return 4;
case json_string:
case rjp_json_string:
return rjp_escape_strlen(root->string.value) + 2; //"";
case json_array:
return _rjp__array_strlen(root);
case json_object:
return _rjp__object_strlen(root);
case rjp_json_array:
return irjp_array_strlen(root);
case rjp_json_object:
case rjp_json_ordered_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:
case rjp_json_ordered_object:
return irjp_object_strlen_pretty(root, depth);
default:
return 0;
};

177
src/rjp_unordered_object.c Normal file
View File

@@ -0,0 +1,177 @@
/**
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_unordered_object.h"
#include "rjp_value.h"
#include "rjp_string.h"
#include "tree.h"
#include <string.h> //strlen
#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);
/* 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;
}
/* RJP_OBJECT */
void irjp_copy_unordered_object(RJP_value* dest, const RJP_value* src){
dest->object.root = irjp_copy_tree(src->object.root);
}
void irjp_delete_unordered_object(RJP_value* obj){
irjp_free_tree(obj->object.root);
}
RJP_value* irjp_add_unordered_member(RJP_value* dest, char* key, RJP_index keylen){
if(!keylen)
keylen = strlen(key);
++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;
retval->type = rjp_json_null;
return retval;
}
RJP_value* irjp_remove_unordered_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);
member->parent = NULL;
--obj->object.num_members;
return member;
}
void irjp_unordered_set_key(RJP_value* dest, char* key, RJP_index keylen){
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 irjp_unordered_num_members(const RJP_value* object){
return object->object.num_members;
}
RJP_value* irjp_unordered_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 irjp_init_unordered_object_iterator(RJP_object_iterator* it, const RJP_value* object){
it->it = rjp_alloc(sizeof(RJP_object_iterator_impl));
RJP_object_iterator_impl* itimpl = it->it;
RJP_tree_node* root = object->object.root;
irjp_init_tree_stack(&itimpl->stack);
RJP_tree_node* current = (RJP_tree_node*)root;
while(current){
irjp_push_tree_stack(&itimpl->stack, current);
current = current->left;
}
}
void irjp_delete_unordered_object_iterator(RJP_object_iterator* it){
irjp_delete_tree_stack(&it->it->stack);
rjp_free(it->it);
it->it = NULL;
}
RJP_value* irjp_unordered_object_iterator_current(const RJP_object_iterator* it){
RJP_tree_node* n = irjp_peek_tree_stack(&it->it->stack);
if(!n)
return NULL;
return &n->data.value;
}
RJP_value* irjp_unordered_object_iterator_next(RJP_object_iterator* it){
RJP_object_iterator_impl* itimp = it->it;
RJP_tree_node* last = irjp_pop_tree_stack(&itimp->stack);
if(last->right){
irjp_push_tree_stack(&itimp->stack, last->right);
last = last->right;
while(last->left){
irjp_push_tree_stack(&itimp->stack, last->left);
last = last->left;
}
}
return irjp_unordered_object_iterator_current(it);
}
RJP_value* irjp_unordered_object_iterator_peek(const RJP_object_iterator* it){
RJP_object_iterator_impl* itimp = it->it;
RJP_tree_node* n = irjp_peek_tree_stack(&itimp->stack)->right;
if(n)
return &n->data.value;
n = irjp_double_peek_tree_stack(&itimp->stack);
if(!n)
return NULL;
return &n->data.value;
}

454
src/tree.c Normal file
View File

@@ -0,0 +1,454 @@
/**
rjp
Copyright (C) 2018-2020 rexy712
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "tree.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "rjp_string.h"
#include "rjp_internal.h"
#include "rjp_object.h"
#define BLACK 0
#define RED 1
//Forward declare static functions
static inline RJP_tree_node* irjp_grandparent(RJP_tree_node* n);
static inline RJP_tree_node* irjp_sibling(RJP_tree_node* n);
static inline RJP_tree_node* irjp_uncle(RJP_tree_node* n);
static inline RJP_tree_node* irjp_rotate_left(RJP_tree_node* pivot);
static inline RJP_tree_node* irjp_rotate_right(RJP_tree_node* pivot);
static inline int irjp_is_inside_left(RJP_tree_node* n);
static inline int irjp_is_inside_right(RJP_tree_node* n);
static void irjp_replace_node(RJP_tree_node *restrict node, RJP_tree_node *restrict child);
static void irjp_copy_node_data(RJP_tree_node *restrict dest, RJP_tree_node *restrict src);
static RJP_tree_node* irjp_clone_node(const RJP_tree_node* src, RJP_tree_node* parent);
static RJP_tree_node* irjp_copy_node(const RJP_tree_node* root, RJP_tree_node* parent);
static void irjp_free_node(RJP_tree_node* node);
static void irjp_delete_node(RJP_tree_node* node);
static RJP_tree_node* irjp_tree_insert_impl(RJP_tree_node *restrict root, RJP_tree_node *restrict newnode);
static RJP_tree_node* irjp_tree_repair(RJP_tree_node* node);
static RJP_tree_node* irjp_tree_remove_node(RJP_tree_node* target, RJP_tree_node** removed_node);
//Tree helpers
static inline RJP_tree_node* irjp_grandparent(RJP_tree_node* n){
if(!n->parent)
return NULL;
return n->parent->parent;
}
static inline RJP_tree_node* irjp_sibling(RJP_tree_node* n){
RJP_tree_node* p = n->parent;
if(!p)
return NULL;
if(p->left == n)
return p->right;
return p->left;
}
static inline RJP_tree_node* irjp_uncle(RJP_tree_node* n){
if(!n->parent)
return NULL;
return irjp_sibling(n->parent);
}
static inline RJP_tree_node* irjp_rotate_right(RJP_tree_node* pivot){
RJP_tree_node* newroot = pivot->left;
pivot->left = newroot->right;
newroot->right = pivot;
newroot->parent = pivot->parent;
pivot->parent = newroot;
if(newroot->parent){
if(newroot->parent->left == pivot)
newroot->parent->left = newroot;
else
newroot->parent->right = newroot;
}
if(pivot->left)
pivot->left->parent = pivot;
return newroot;
}
static inline RJP_tree_node* irjp_rotate_left(RJP_tree_node* pivot){
RJP_tree_node* newroot = pivot->right;
pivot->right = newroot->left;
newroot->left = pivot;
newroot->parent = pivot->parent;
pivot->parent = newroot;
if(newroot->parent){
if(newroot->parent->left == pivot)
newroot->parent->left = newroot;
else
newroot->parent->right = newroot;
}
if(pivot->right)
pivot->right->parent = pivot;
return newroot;
}
static inline int irjp_is_inside_left(RJP_tree_node* n){
RJP_tree_node* p = n->parent;
RJP_tree_node* g = irjp_grandparent(n);
return n == p->right && p == g->left;
}
static inline int irjp_is_inside_right(RJP_tree_node* n){
RJP_tree_node* p = n->parent;
RJP_tree_node* g = irjp_grandparent(n);
return n == p->left && p == g->right;
}
static void irjp_replace_node(RJP_tree_node *restrict node, RJP_tree_node *restrict child){
if(child)
child->parent = node->parent;
if(!node->parent)
return;
if(node == node->parent->left)
node->parent->left = child;
else
node->parent->right = child;
}
//Node construction / destruction
RJP_tree_node* irjp_new_node(char* key, RJP_index keylen){
RJP_tree_node* node = rjp_calloc(sizeof(RJP_tree_node), 1);
node->data.name.value = key;
node->data.name.length = keylen;
node->parent = node->left = node->right = NULL;
node->color = BLACK;
return node;
}
static void irjp_copy_node_data(RJP_tree_node *restrict dest, RJP_tree_node *restrict src){
dest->data = src->data;
}
static RJP_tree_node* irjp_clone_node(const RJP_tree_node* src, RJP_tree_node* parent){
RJP_tree_node* dest = rjp_alloc(sizeof(RJP_tree_node));
rjp_copy_value(&dest->data.value, &src->data.value);
irjp_strcpy(&dest->data.name, &src->data.name);
dest->parent = parent;
return dest;
}
static RJP_tree_node* irjp_copy_node(const RJP_tree_node* root, RJP_tree_node* parent){
if(!root){
return NULL;
}
RJP_tree_node* newnode = irjp_clone_node(root, parent);
newnode->left = irjp_copy_node(root->left, newnode);
newnode->right = irjp_copy_node(root->right, newnode);
return newnode;
}
static void irjp_free_node(RJP_tree_node* node){
irjp_delete_node(node);
rjp_free(node);
}
static void irjp_delete_node(RJP_tree_node* node){
rjp_free(node->data.name.value);
irjp_delete_value(&node->data.value);
}
/* TREE */
//Tree construction / destruction
RJP_tree_node* irjp_copy_tree(const RJP_tree_node* root){
return irjp_copy_node(root, NULL);
}
void irjp_free_tree(RJP_tree_node* root){
if(!root)
return;
irjp_free_tree(root->left);
irjp_free_tree(root->right);
irjp_free_node(root);
}
//Tree operations
RJP_tree_node* irjp_tree_insert_value(RJP_tree_node* root, char* key, RJP_index keylen, RJP_value** added, int* status){
*status = RJP_TREE_SUCCESS;
if(!root){
root = irjp_new_node(key, keylen);
if(added)
*added = &root->data.value;
return root;
}
RJP_tree_node* newnode = irjp_new_node(key, keylen);
if(added)
*added = &newnode->data.value;
return irjp_tree_insert_node(root, newnode);
}
RJP_tree_node* irjp_tree_insert_node(RJP_tree_node *restrict root, RJP_tree_node *restrict newnode){
irjp_tree_insert_impl(root, newnode);
irjp_tree_repair(newnode);
while(root->parent)
root = root->parent;
return root;
}
static RJP_tree_node* irjp_tree_insert_impl(RJP_tree_node *restrict root, RJP_tree_node *restrict newnode){
if(!root){
return newnode;
}
RJP_tree_node* n = root;
while(1){
//compare json key strings
int cmpval = strcmp(newnode->data.name.value, n->data.name.value);
if(cmpval < 0){
if(n->left){
n = n->left;
}else{
n->left = newnode;
newnode->parent = n;
break;
}
}else{
if(n->right){
n = n->right;
}else{
n->right = newnode;
newnode->parent = n;
break;
}
}
}
return newnode;
}
static RJP_tree_node* irjp_tree_repair(RJP_tree_node* node){
node->color = RED;
while(1){
if(!node->parent){
node->color = BLACK;
return node;
}else if(node->parent->color == BLACK){
return node;
}else if(irjp_uncle(node) && irjp_uncle(node)->color == RED){
node->parent->color = BLACK;
irjp_uncle(node)->color = BLACK;
node = irjp_grandparent(node);
}else{
if(irjp_is_inside_left(node)){
irjp_rotate_left(node->parent);
node = node->left;
}else if(irjp_is_inside_right(node)){
irjp_rotate_right(node->parent);
node = node->right;
}
RJP_tree_node* pa = node->parent;
RJP_tree_node* gp = irjp_grandparent(node);
if(node == pa->left){
irjp_rotate_right(gp);
}else{
irjp_rotate_left(gp);
}
pa->color = BLACK;
gp->color = RED;
return node;
}
}
}
RJP_tree_node* irjp_tree_search_value(RJP_tree_node* root, const char* key){
while(root != NULL){
int cmpval = strcmp(key, root->data.name.value);
if(cmpval < 0){
root = root->left;
}else if(cmpval > 0){
root = root->right;
}else{
return root;
}
}
return NULL;
}
//Debug printouts
void irjp_dbg_print_tree(RJP_tree_node* root){
if(!root){
return;
}
RJP_tree_node* current = root, *pre;
while(current){
if(!current->left){
printf("%s\n", current->data.name.value);
current = current->right;
}else{
pre = current->left;
while(pre->right && pre->right != current){
pre = pre->right;
}
if(!pre->right){
pre->right = current;
current = current->left;
}else{
pre->right = NULL;
printf("%s\n", current->data.name.value);
current = current->right;
}
}
}
}
#define pop() front = (front+1)%queuelen
#define push(val, dp) do{queue[rear].n = (val);queue[rear].depth = (dp);rear = (rear+1)%queuelen;}while(0)
struct tmpthing{
RJP_tree_node* n;
int depth;
};
void irjp_dbg_print_tree_bfs(RJP_tree_node* root){
int queuelen = 64;
struct tmpthing queue[64];
int front = 0, rear = 0;
int lastdepth = 0;
push(root, lastdepth);
while(front != rear){
if(lastdepth != queue[front].depth){
lastdepth = queue[front].depth;
printf("\n");
}
if(!queue[front].n){
printf("*");
}else{
printf("%s ", queue[front].n->data.name.value);
if(queue[front].n->left){
push(queue[front].n->left, queue[front].depth+1);
}else{
push(NULL,queue[front].depth+1);
}
if(queue[front].n->right){
push(queue[front].n->right, queue[front].depth+1);
}else{
push(NULL,queue[front].depth+1);
}
}
pop();
}
printf("\n");
}
#undef pop
#undef push
RJP_tree_node* irjp_tree_remove_value(RJP_tree_node* restrict root, RJP_tree_node* restrict member, RJP_tree_node** removed_node){
if(!root)
return root;
if(!member)
return root;
return irjp_tree_remove_node(member, removed_node);
}
static RJP_tree_node* irjp_tree_remove_node(RJP_tree_node* target, RJP_tree_node** removed_node){
while(target->right && target->left){
irjp_copy_node_data(target, target->right);
target = target->right;
}
*removed_node = target;
RJP_tree_node* retval = target->parent;
do{
RJP_tree_node* child = (target->left) ? target->left : target->right;
//handle trivial cases
if(target->color == RED){
irjp_replace_node(target, child);
target->parent = NULL;
break;
}else{
if(child && child->color == RED){
child->color = BLACK;
}
irjp_replace_node(target, child);
if(!retval)
retval = child;
break;
}
if(!target->parent){
retval = child;
break;
//handle harder cases
}else{
//parent and sibling guaranteed to exist at this point
RJP_tree_node* sibling = irjp_sibling(target);
//Deal with red sibling
if(sibling->color == RED){
sibling->color = BLACK;
target->parent->color = RED;
if(target == target->parent->left){
irjp_rotate_left(target->parent);
}else{
irjp_rotate_right(target->parent);
}
sibling = irjp_sibling(target);
}
//sibling guaranteed to be black here
if(target->parent->color == BLACK &&
((!sibling->left) || sibling->left->color == BLACK) &&
((!sibling->right) || sibling->right->color == BLACK))
{
sibling->color = RED;
target = target->parent;
continue;
}
//sibling guaranteed to be black here
if(target->parent->color == RED &&
((!sibling->left) || sibling->left->color == BLACK) &&
((!sibling->right) || sibling->right->color == BLACK))
{
sibling->color = RED;
target->parent->color = BLACK;
break;
}
//Orient the subtree for the following section
if((target == target->parent->left) && (!sibling->right || sibling->right->color == BLACK) &&
(sibling->left && sibling->left->color == RED))
{
sibling->color = RED;
if(sibling->left)
sibling->left->color = BLACK;
irjp_rotate_right(sibling);
sibling = irjp_sibling(target);
}else if((target == target->parent->right) && (sibling->right && sibling->right->color == RED) &&
(!sibling->left || sibling->left->color == BLACK))
{
sibling->color = RED;
if(sibling->right)
sibling->right->color = BLACK;
irjp_rotate_left(sibling);
sibling = irjp_sibling(target);
}
sibling->color = target->parent->color;
target->parent->color = BLACK;
if(target == target->parent->left){
//guaranteed to not be NULL
sibling->right->color = BLACK;
irjp_rotate_left(target->parent);
}else{
//guaranteed to not be NULL
sibling->left->color = BLACK;
irjp_rotate_right(target->parent);
}
break;
}
}while(1);
(*removed_node)->left = NULL;
(*removed_node)->right = NULL;
//return new root
if(!retval)
return NULL;
while(retval->parent)
retval = retval->parent;
return retval;
}