Move json_value and obj_t to same file
 - Rename json_obj.* -> json_value.*
 - Move `print_json_value(...)` to json_value.*

Move `err_ctx(...)` to util.*

parse.h now only exposes `parse_json_value(...)`
This commit is contained in:
olemorud
2023-04-23 22:34:24 +02:00
parent 4b91c62750
commit 9a88d1cbdc
9 changed files with 313 additions and 302 deletions

228
src/json_value.c Normal file
View File

@@ -0,0 +1,228 @@
#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 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);
void* obj_at(obj_t m, char* const key);
/*
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 = strdup(key);
cur->val = value;
cur->next = m[i];
/* insert newest entry as head */
m[i] = cur;
return true;
}
/*
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++) {
if (m[i] == NULL)
continue;
struct obj_entry *e = m[i], *tmp;
while (e != NULL) {
tmp = e;
free((char*)e->key);
free(e->val);
e = e->next;
free(tmp);
}
}
}
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);
}