Files
json-parser/src/json_value.c
2023-04-24 23:44:45 +02:00

255 lines
5.5 KiB
C

#include <err.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "json_value.h"
#include "util.h"
bool obj_insert(obj_t m, char* const key, struct json_value* value);
size_t obj_hash(char const* str);
void obj_delete(obj_t* m);
void* obj_at(obj_t m, char* const key);
void print_array(struct json_value** arr, int cur_indent, int indent_amount);
void print_json_value(struct json_value val, int cur_indent, int indent_amount);
void print_object(obj_t obj, int cur_indent, int indent_amount);
/*
djb2 string hash
credits: Daniel J. Bernstein
Returns a hash of the string `str`.
It seems to work well because 33 is coprime to
2^32 and 2^64 (any odd number except 1 is),
which probably improves the distribution (I'm
not a mathematician so I can't prove it this).
Multiplying a number n by 33 is the same as
bitshifting by 5 and adding n, which is faster
than multiplying by, say, 37. Maybe any 2^x±1
are equally good.
5381 is a large-ish prime number which are used
as multipliers in most hash algorithms.
*/
size_t obj_hash(char const* str)
{
size_t hash = 5381;
unsigned int c;
while ((c = *str++) != '\0')
hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
return hash % OBJ_SIZE;
}
/*
Value at index `key`
m - obj to retrieve from
key - index to read
returns value of index if exists, or NULL
if key is not in obj
*/
void* obj_at(obj_t m, char* const key)
{
struct obj_entry* hit = m[obj_hash(key)];
/* traverse linked list to end or until key is found */
while (hit != NULL && strcmp(hit->key, key))
hit = hit->next;
return hit ? hit->val : NULL;
}
/*
Insert `value` at index `key`
m - obj to insert to
key - key to insert at
val - value to insert
val_size - size of value in bytes
returns true if successful
returns false if key already exists
*/
bool obj_insert(obj_t m, char* const key, struct json_value* value)
{
size_t i = obj_hash(key);
struct obj_entry* cur = m[i];
if (value == NULL)
err(EINVAL, "value cannot be NULL");
if (key == NULL)
err(EINVAL, "key cannot be NULL");
/* traverse linked list to end or until key is found */
while (cur != NULL) {
if (cur->key == NULL)
err(EXIT_FAILURE, "entry without key");
if (strncmp(cur->key, key, strlen(key)) == 0)
break;
cur = cur->next;
}
/* fail if key already exists */
if (cur != NULL)
return false;
/* populate new entry */
cur = malloc_or_die(sizeof(struct obj_entry));
cur->key = key;
cur->val = value;
cur->next = m[i];
/* insert newest entry as head */
m[i] = cur;
return true;
}
void json_value_delete(struct json_value val)
{
switch (val.type) {
case array:
for (size_t i = 0; val.array[i] != NULL; i++) {
json_value_delete(*(val.array[i]));
free(val.array[i]);
}
free(val.array);
break;
case object:
obj_delete(val.object);
break;
case string:
free(val.string);
break;
default:
break;
}
}
/*
Free memory allocated for obj
TODO: recurively delete children objects
*/
void obj_delete(obj_t* m)
{
for (size_t i = 0; i < OBJ_SIZE; i++) {
struct obj_entry *e = (*m)[i], *tmp;
while (e != NULL) {
json_value_delete(*(e->val));
free((char*)e->key);
free(e->val);
tmp = e;
e = e->next;
free(tmp);
}
}
free(m);
}
void add_indent(int n)
{
for (int i = 0; i < n; i++)
putchar(' ');
}
void print_object(obj_t obj, int cur_indent, int indent_amount)
{
putchar('{');
bool first = true;
for (size_t i = 0; i < OBJ_SIZE; i++) {
struct obj_entry* e = obj[i];
while (e != NULL) {
if (!first)
putchar(',');
first = false;
putchar('\n');
add_indent(cur_indent);
printf("\"%s\": ", e->key);
print_json_value(*(e->val), cur_indent + indent_amount, indent_amount);
e = e->next;
}
}
putchar('\n');
add_indent(cur_indent - indent_amount * 2);
putchar('}');
}
void print_array(struct json_value** arr, int cur_indent, int indent_amount)
{
putchar('[');
if (arr[0] == NULL) {
putchar(']');
return;
}
for (size_t i = 0; arr[i + 1] != NULL; i++) {
putchar('\n');
add_indent(cur_indent);
print_json_value(*arr[i], cur_indent + indent_amount, indent_amount);
if (arr[i + 1] != NULL)
putchar(',');
}
putchar('\n');
add_indent(cur_indent - indent_amount * 2);
putchar(']');
}
void print_json_value(struct json_value val, int cur_indent,
int indent_amount)
{
switch (val.type) {
case string:
printf("\"%s\"", val.string);
break;
case number:
printf("%lf", val.number);
break;
case boolean:
printf("%s", val.boolean ? "true" : "false");
break;
case null:
printf("null");
break;
case object:
print_object(*val.object, cur_indent + indent_amount, indent_amount);
break;
case array:
print_array(val.array, cur_indent + indent_amount, indent_amount);
break;
default:
printf("<unknown>");
}
}
void print_json(struct json_value val, int indent)
{
print_json_value(val, 0, indent);
}