more docs!

This commit is contained in:
cozis
2022-04-25 13:43:19 +02:00
parent f3fde81d01
commit a0fd25132e
2 changed files with 180 additions and 26 deletions
+1 -2
View File
@@ -33,7 +33,6 @@ struct xj_value {
};
};
```
objects and arrays are represented as linked lists of `xj_value`s.
Although the user can make as many `xj_value` nodes as he wants, some constructor functions are also provided
@@ -73,7 +72,7 @@ xj_alloc *xj_alloc_new(int size, int ext);
```
the first lets you specify the memory that the allocator will use to operate, while the second tells the allocator to call `malloc` to get the memory he needs.
When an `xj_alloc` is instanciated, you can call all of the functions that require you to provide an 'xj_alloc*'. All of the objects that those functions return, will be stored in the allocator. You deallocate all of the nodes by freeing up the whole allocator using
When an `xj_alloc` is instanciated, you can call all of the functions that require you to provide an `xj_alloc*`. The objects that those functions return will be stored in the allocator. You deallocate all of the nodes by freeing up the whole allocator using
```c
void xj_alloc_del(xj_alloc *alloc);
```
+179 -24
View File
@@ -7,6 +7,16 @@
#include <ctype.h>
#include "xjson.h"
/* OVERVIEW
* This file implements the routines required to
* decode and encode JSON text. These features are
* exposed through the [xj_decode] and [xj_encode]
* functions.
*
* Here is also designed the object model required
* to represent the encoded form of the JSON text.
*/
typedef struct chunk_t chunk_t;
struct chunk_t {
chunk_t *prev;
@@ -21,6 +31,9 @@ struct xj_alloc {
int ext_size;
};
/* Symbol: xj_alloc_new
* Instanciate an allocator.
*/
xj_alloc *xj_alloc_new(int size, int ext)
{
assert(size >= 0 && ext >= 0);
@@ -387,7 +400,7 @@ typedef struct {
** [nbytes] are ever written. If one of those conitions isn't true, -1 is
** returned.
*/
int xutf8_sequence_from_utf32_codepoint(char *utf8_data, int nbytes, uint32_t utf32_code)
static int xutf8_sequence_from_utf32_codepoint(char *utf8_data, int nbytes, uint32_t utf32_code)
{
if(utf32_code < 128)
{
@@ -465,7 +478,7 @@ int xutf8_sequence_from_utf32_codepoint(char *utf8_data, int nbytes, uint32_t ut
** NOTE: By calling this function with a NULL [utf32_code], you can check the
** validity of a UTF-8 sequence.
*/
int xutf8_sequence_to_utf32_codepoint(const char *utf8_data, int nbytes, uint32_t *utf32_code)
static int xutf8_sequence_to_utf32_codepoint(const char *utf8_data, int nbytes, uint32_t *utf32_code)
{
assert(utf8_data != NULL);
assert(nbytes >= 0);
@@ -592,7 +605,7 @@ typedef struct {
char maybe[256];
} string_parsing_context_t;
_Bool spc_append(string_parsing_context_t *spc, const char *str, int len)
static _Bool spc_append(string_parsing_context_t *spc, const char *str, int len)
{
if(spc->size + len > spc->capacity)
{
@@ -631,7 +644,7 @@ _Bool spc_append(string_parsing_context_t *spc, const char *str, int len)
return 1;
}
void spc_free(string_parsing_context_t *spc)
static void spc_free(string_parsing_context_t *spc)
{
if(spc->maybe != spc->buffer)
free(spc->buffer);
@@ -1277,6 +1290,39 @@ static xj_value *parse_value(context_t *ctx)
return NULL;
}
/* Symbol:
* xj_decode
*
* Description:
* Transform a JSON UTF-8 string to a tree of [xj_value] nodes.
*
* Arguments:
* str: The string to be parsed. It's doesn't need to be
* zero-terminated. If NULL, an empty string is assumed.
*
* len: The length of [str] (in bytes). If negative, [str] is
* assumed to be zero-terminated and [len] is computed
* using [strlen].
*
* alloc: The allocator that will be used to store the parsing
* result. It's not optional (can't be NULL).
*
* error: The reference to a caller-allocated [xj_error]. If
* an error occurres (NULL is returned) then this is
* used to provide the caller with useful information
* regarting the failure. It's not required and can be
* NULL.
*
* Returns:
* The pointer to a tree of [xj_value] nodes, or NULL on failure.
* If NULL is returned and an [xj_error] is provided, than it's
* fields are set to provide the caller with extra information
* related to the failure.
*
* Notes:
* The returned objects are deallocated with the whole allocator
* when calling [xj_alloc_del].
*/
xj_value *xj_decode(const char *str, int len,
xj_alloc *alloc, xj_error *error)
{
@@ -1308,22 +1354,89 @@ xj_value *xj_decode(const char *str, int len,
}
typedef struct bucket_t bucket_t;
/* Symbol:
* bucket_t
*
* Description:
* A memory region that linked with other [bucket_t]
* can represent long strings of text. It's a sub-type
* of [bucket_t].
*
* Notes:
* This is a big structure.
*
* The [body]'s was chosen to be such that the whole
* [bucket_t] is 4kb big, but it's not really necessary.
*/
struct bucket_t {
bucket_t *next;
char body[4096-sizeof(void*)];
};
/* Symbol:
* buffer_t
*
* Description:
* A buffer that can be used to build large strings
* without the degradation of performance that one
* would get by using a plain dinamically growing
* array.
* It's implemented as a linked list of chunks, so
* it grows by adding new chunks, without the need
* to move the old chunks.
*
* Fields:
* size: The absolute string size (in bytes) that is
* contained in the buffer. When the buffer is
* serialized, the resulting string will have
* this size.
*
* used: The amount of bytes held by the last chunk.
*
* tail: The pointer to the last chunk.
*
* head: The first chunk of the buffer. It's not a
* pointer because it's pre-allocated with
* the [buffer_t].
*
* Notes:
* The fact that the first chunk comes preallocated with
* the buffer makes it a large structure. A [bucket_t] is
* around 4kb, so a buffer will be bigger than that.
*
* The [head] is the last field so that the other fields
* are contiguous in memory. If [head] were between other
* fields, then there would be a 4kb distance between them.
*/
typedef struct {
int size, used;
bucket_t *tail, head;
} buffer_t;
static xj_bool append_string(buffer_t *buff, const char *str, int len)
/* Symbol:
* buffer_append
*
* Description:
* Appends a string to a [buffer_t].
*
* Returns:
* 1 if all went well or 0 if an error occurred.
*/
static xj_bool buffer_append(buffer_t *buff, const char *str, int len)
{
assert(str != NULL && len >= 0);
// If there's not enough memory in the tail chunk
// then create a new tail chunk!
if(buff->used + len > (int) sizeof(buff->tail->body))
{
// It's not possible to add a string that
// is bigger than a chunk.
if(len > (int) sizeof(buff->tail->body))
retunr 0;
bucket_t *buck = malloc(sizeof(bucket_t));
if(buck == NULL)
@@ -1334,15 +1447,27 @@ static xj_bool append_string(buffer_t *buff, const char *str, int len)
buff->tail = buck;
buff->used = 0;
}
memcpy(buff->tail->body + buff->used, str, len);
buff->used += len;
buff->size += len;
return 1;
}
/* Symbol:
* encode_string
*
* Description:
* Serializes a string to a [buffer_t] in JSON form.
*
* Returns:
* 1 if all went well or 0 if an error occurred.
*/
static _Bool encode_string(const char *str, int len, buffer_t *buff)
{
if(!append_string(buff, "\"", 1))
assert(str != NULL && len >= 0);
if(!buffer_append(buff, "\"", 1))
return 0;
int i = 0;
@@ -1357,7 +1482,7 @@ static _Bool encode_string(const char *str, int len, buffer_t *buff)
int end = i;
if(!append_string(buff, str + start, end - start))
if(!buffer_append(buff, str + start, end - start))
return 0;
if(i == len)
@@ -1365,13 +1490,13 @@ static _Bool encode_string(const char *str, int len, buffer_t *buff)
if(str[i] == '"')
{
if(!append_string(buff, "\\\"", 2))
if(!buffer_append(buff, "\\\"", 2))
return 0;
i += 1;
}
else if(str[i] == '\\')
{
if(!append_string(buff, "\\\\", 2))
if(!buffer_append(buff, "\\\\", 2))
return 0;
i += 1;
}
@@ -1393,7 +1518,7 @@ static _Bool encode_string(const char *str, int len, buffer_t *buff)
assert(m != NULL);
if(!append_string(buff, m, 2))
if(!buffer_append(buff, m, 2))
return 0;
i += 1;
@@ -1443,30 +1568,39 @@ static _Bool encode_string(const char *str, int len, buffer_t *buff)
buffer[12] = '\0';
}
if(!append_string(buff, buffer, used))
if(!buffer_append(buff, buffer, used))
return 0;
i += scanned;
}
}
if(!append_string(buff, "\"", 1))
if(!buffer_append(buff, "\"", 1))
return 0;
return 1;
}
/* Symbol:
* encode_value
*
* Description:
* Serializes an [xj_value] to a [buffer_t]
*
* Returns:
* 1 if all went well or 0 if an error occurred.
*/
static _Bool encode_value(xj_value *val, buffer_t *buff)
{
switch(val == NULL ? XJ_NULL : val->type)
{
case XJ_NULL:
return append_string(buff, "null", 4);
return buffer_append(buff, "null", 4);
case XJ_BOOL:
return val->as_bool
? append_string(buff, "true", 4)
: append_string(buff, "false", 5);
? buffer_append(buff, "true", 4)
: buffer_append(buff, "false", 5);
case XJ_INT:
{
@@ -1474,7 +1608,7 @@ static _Bool encode_value(xj_value *val, buffer_t *buff)
int k = snprintf(temp, sizeof(temp),
"%lld", val->as_int);
assert(k >= 0 && k < (int) sizeof(temp));
if(!append_string(buff, temp, k))
if(!buffer_append(buff, temp, k))
return 0;
return 1;
}
@@ -1485,14 +1619,14 @@ static _Bool encode_value(xj_value *val, buffer_t *buff)
int k = snprintf(temp, sizeof(temp),
"%g", val->as_float);
assert(k >= 0 && k < (int) sizeof(temp));
if(!append_string(buff, temp, k))
if(!buffer_append(buff, temp, k))
return 0;
return 1;
}
case XJ_ARRAY:
{
if(!append_string(buff, "[", 1))
if(!buffer_append(buff, "[", 1))
return 0;
xj_value *child = val->as_object;
@@ -1504,18 +1638,18 @@ static _Bool encode_value(xj_value *val, buffer_t *buff)
child = child->next;
if(child != NULL)
if(!append_string(buff, ", ", 2))
if(!buffer_append(buff, ", ", 2))
return 0;
}
if(!append_string(buff, "]", 1))
if(!buffer_append(buff, "]", 1))
return 0;
return 1;
}
case XJ_OBJECT:
{
if(!append_string(buff, "{", 1))
if(!buffer_append(buff, "{", 1))
return 0;
xj_value *child = val->as_object;
@@ -1524,7 +1658,7 @@ static _Bool encode_value(xj_value *val, buffer_t *buff)
if(!encode_string(child->key, strlen(child->key), buff))
return 0;
if(!append_string(buff, ": ", 2))
if(!buffer_append(buff, ": ", 2))
return 0;
if(!encode_value(child, buff))
@@ -1533,11 +1667,11 @@ static _Bool encode_value(xj_value *val, buffer_t *buff)
child = child->next;
if(child != NULL)
if(!append_string(buff, ", ", 2))
if(!buffer_append(buff, ", ", 2))
return 0;
}
if(!append_string(buff, "}", 1))
if(!buffer_append(buff, "}", 1))
return 0;
return 1;
}
@@ -1548,6 +1682,27 @@ static _Bool encode_value(xj_value *val, buffer_t *buff)
return 0;
}
/* Symbol:
* xj_encode
*
* Description:
* Transforms an [xj_value] to a string.
*
* Arguments:
* value: The object to be converted to a string.
*
* len: An output argument that returns the length
* of the generated string. It's optional, so
* it can be NULL.
*
* Returns:
* The pointer to a zero-terminated string if all went
* well or NULL.
*
* Notes:
* The returned pointer, if not NULL, must be
* deallocated using [free].
*/
char *xj_encode(xj_value *value, int *len)
{
buffer_t buff;