Fix CanonicalJson and ArraySort.

This commit is contained in:
Jordan Bancino 2023-06-21 02:37:56 +00:00
parent 1fa07d2d3c
commit 539fde773f
7 changed files with 81 additions and 106 deletions

View file

@ -259,7 +259,7 @@ ArrayQuickSort(Array * array, size_t low, size_t high, int (*compare) (void *, v
{ {
size_t pi = ArrayPartition(array, low, high, compare); size_t pi = ArrayPartition(array, low, high, compare);
ArrayQuickSort(array, low, pi - 1, compare); ArrayQuickSort(array, low, pi ? pi - 1 : 0, compare);
ArrayQuickSort(array, pi + 1, high, compare); ArrayQuickSort(array, pi + 1, high, compare);
} }
} }
@ -271,7 +271,7 @@ ArraySort(Array * array, int (*compare) (void *, void *))
{ {
return; return;
} }
ArrayQuickSort(array, 0, array->size, compare); ArrayQuickSort(array, 0, array->size - 1, compare);
} }
/* Even though the following operations could be done using only the /* Even though the following operations could be done using only the

View file

@ -16,8 +16,9 @@ Milestone: v0.4.0
[~] Client-Server API [~] Client-Server API
[~] 6: Filtering [~] 6: Filtering
[ ] Finish validating filters before saving them.
[~] 7: Events [~] 7: Events
[ ] Compute size of JSON object in Canonical JSON [x] Compute size of JSON object in Canonical JSON
[x] Rename Sha2.h to just Sha; add Sha1() function [x] Rename Sha2.h to just Sha; add Sha1() function
[x] Make Sha256() return raw bytes; add function to [x] Make Sha256() return raw bytes; add function to
convert to hex string. convert to hex string.
@ -44,6 +45,7 @@ Milestone: v0.4.0
use the default. This will make debugging a lot easier. use the default. This will make debugging a lot easier.
[ ] Make sure admin registration token is printed to log, not stdout. [ ] Make sure admin registration token is printed to log, not stdout.
Unless they are the same, of course. Unless they are the same, of course.
[ ] Fix Json UTF-8 handling.
Milestone: v0.5.0 Milestone: v0.5.0
----------------- -----------------

View file

@ -30,29 +30,32 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
int static int
CanonicalJsonKeyCompare(void *k1, void *k2) CanonicalJsonKeyCompare(void *k1, void *k2)
{ {
return strcmp((char *) k1, (char *) k2); return strcmp((char *) k1, (char *) k2);
} }
static void int
CanonicalJsonEncodeValue(JsonValue * value, Stream * out) CanonicalJsonEncodeValue(JsonValue * value, Stream * out)
{ {
Array *arr; Array *arr;
size_t i, len; size_t i, len;
int length = 0;
/* Override object type to encode using the canonical functions */ /* Override object type to encode using the canonical functions */
switch (JsonValueType(value)) switch (JsonValueType(value))
{ {
case JSON_OBJECT: case JSON_OBJECT:
CanonicalJsonEncode(JsonValueAsObject(value), out); length += CanonicalJsonEncode(JsonValueAsObject(value), out);
break; break;
case JSON_ARRAY: case JSON_ARRAY:
arr = JsonValueAsArray(value); arr = JsonValueAsArray(value);
len = ArraySize(arr); len = ArraySize(arr);
StreamPutc(out, '['); StreamPutc(out, '[');
length++;
for (i = 0; i < len; i++) for (i = 0; i < len; i++)
{ {
@ -64,19 +67,23 @@ CanonicalJsonEncodeValue(JsonValue * value, Stream * out)
continue; continue;
} }
CanonicalJsonEncodeValue(aVal, out); length += CanonicalJsonEncodeValue(aVal, out);
if (i < len - 1) if (i < len - 1)
{ {
StreamPutc(out, ','); StreamPutc(out, ',');
length++;
} }
} }
StreamPutc(out, ']'); StreamPutc(out, ']');
length++;
break; break;
default: default:
JsonEncodeValue(value, out, JSON_DEFAULT); length += JsonEncodeValue(value, out, JSON_DEFAULT);
break; break;
} }
return length;
} }
int int
@ -87,16 +94,17 @@ CanonicalJsonEncode(HashMap * object, Stream * out)
Array *keys; Array *keys;
size_t i; size_t i;
size_t keyCount; size_t keyCount;
int length;
if (!object || !out) if (!object)
{ {
return 0; return -1;
} }
keys = ArrayCreate(); keys = ArrayCreate();
if (!keys) if (!keys)
{ {
return 0; return -1;
} }
while (HashMapIterate(object, &key, (void **) &value)) while (HashMapIterate(object, &key, (void **) &value))
@ -106,7 +114,11 @@ CanonicalJsonEncode(HashMap * object, Stream * out)
ArraySort(keys, CanonicalJsonKeyCompare); ArraySort(keys, CanonicalJsonKeyCompare);
/* The total number of bytes written */
length = 0;
StreamPutc(out, '{'); StreamPutc(out, '{');
length++;
keyCount = ArraySize(keys); keyCount = ArraySize(keys);
for (i = 0; i < keyCount; i++) for (i = 0; i < keyCount; i++)
@ -128,18 +140,21 @@ CanonicalJsonEncode(HashMap * object, Stream * out)
continue; continue;
} }
JsonEncodeString(key, out); length += JsonEncodeString(key, out);
StreamPutc(out, ':'); StreamPutc(out, ':');
CanonicalJsonEncodeValue(value, out); length++;
length += CanonicalJsonEncodeValue(value, out);
if (i < keyCount - 1) if (i < keyCount - 1)
{ {
StreamPutc(out, ','); StreamPutc(out, ',');
length++;
} }
} }
StreamPutc(out, '}'); StreamPutc(out, '}');
length++;
ArrayFree(keys); ArrayFree(keys);
return 1; return length;
} }

View file

@ -55,6 +55,7 @@
#include <HashMap.h> #include <HashMap.h>
#include <Stream.h> #include <Stream.h>
#include <Json.h>
/** /**
* Encode a JSON object following the rules of Canonical JSON. See the * Encode a JSON object following the rules of Canonical JSON. See the
@ -71,25 +72,19 @@
* .Fn JsonEncode , * .Fn JsonEncode ,
* because it is much cheaper both in terms of memory and CPU time. * because it is much cheaper both in terms of memory and CPU time.
* .Pp * .Pp
* This function returns a boolean value indicating whether or not the * This function returns the number of bytes written to the
* operation was sucessful. This function will fail only if NULL was * stream, just like
* given for any parameter. Otherwise, if an invalid pointer is given, * .Fn JsonEncode .
* undefined behavior results.
*/ */
extern int CanonicalJsonEncode(HashMap *, Stream *); extern int CanonicalJsonEncode(HashMap *, Stream *);
/** /**
* This function encodes a JSON object to a string. * Encode a JSON value following the rules of Canonical JSON.
* .Xr Json 3 * See the documentation for
* doesn't have any way to send JSON to a string, because there's * .Fn JsonEncodeValue ,
* absolutely no reason to handle JSON strings in most cases. However, * documented in
* the sole reason Canonical JSON exists is so that JSON objects can * .Xr Json 3 .
* be signed in a consisten way. Thus, it is likely you need a string
* to pass to the signing function.
* .Pp
* This function returns a C string containing the canonical JSON
* representation of the given object, or NULL if the encoding failed.
*/ */
extern char * CanonicalJsonEncodeToString(HashMap *); extern int CanonicalJsonEncodeValue(JsonValue *, Stream *);
#endif /* TELODENDRIA_CANONICALJSON_H */ #endif /* TELODENDRIA_CANONICALJSON_H */

View file

@ -183,7 +183,7 @@ recipe_build() {
if [ $(mod_time "$src") -ge $(mod_time "$out") ] || [ $do_rebuild -eq 1 ]; then if [ $(mod_time "$src") -ge $(mod_time "$out") ] || [ $do_rebuild -eq 1 ]; then
echo "CC $(basename $out)" echo "CC $(basename $out)"
mkdir -p "$(dirname $out)" mkdir -p "$(dirname $out)"
if ! $CC $CFLAGS -o "$out" "$src" ${CYTOPLASM_FLAGS} ${LDFLAGS} ${STATIC}; then if ! $CC $CFLAGS -o "$out" "$src" $objs ${CYTOPLASM_FLAGS} ${LDFLAGS} ${STATIC}; then
exit 1 exit 1
fi fi
fi fi

View file

@ -34,74 +34,6 @@
#include <Db.h> #include <Db.h>
#include <Json.h> #include <Json.h>
static void
HexDump(size_t off, char *hexBuf, char *asciiBuf, void *args)
{
char *fmt;
(void) args;
if (hexBuf && asciiBuf)
{
fmt = "%04lx: | %s | %s |";
}
else
{
fmt = "%04lx";
}
Log(LOG_DEBUG, fmt, off, hexBuf, asciiBuf);
}
void
TelodendriaMemoryHook(MemoryAction a, MemoryInfo * i, void *args)
{
char *action;
int err = 0;
(void) args;
switch (a)
{
case MEMORY_ALLOCATE:
action = "Allocated";
break;
case MEMORY_REALLOCATE:
action = "Re-allocated";
break;
case MEMORY_FREE:
action = "Freed";
break;
case MEMORY_BAD_POINTER:
err = 1;
action = "Bad pointer to";
break;
case MEMORY_CORRUPTED:
err = 1;
action = "Corrupted block of";
break;
default:
action = "Unknown operation on";
break;
}
Log(err ? LOG_ERR : LOG_DEBUG,
"%s:%d: %s %lu bytes of memory at %p.",
MemoryInfoGetFile(i), MemoryInfoGetLine(i),
action, MemoryInfoGetSize(i),
MemoryInfoGetPointer(i));
if (a != MEMORY_ALLOCATE && a != MEMORY_REALLOCATE)
{
MemoryHexDump(i, HexDump, NULL);
}
if (err)
{
raise(SIGINT);
}
}
static HttpServer *server = NULL; static HttpServer *server = NULL;
static void static void

View file

@ -30,7 +30,9 @@
#include <HashMap.h> #include <HashMap.h>
#include <Str.h> #include <Str.h>
#include <Memory.h> #include <Memory.h>
#include <Json.h> #include <Json.h>
#include <CanonicalJson.h>
#define FLAG_ENCODE (1 << 0) #define FLAG_ENCODE (1 << 0)
#define FLAG_SELECT (1 << 1) #define FLAG_SELECT (1 << 1)
@ -42,7 +44,7 @@ usage(char *prog)
} }
static void static void
query(char *select, HashMap * json) query(char *select, HashMap * json, int canonical)
{ {
char *key; char *key;
JsonValue *rootVal = JsonValueObject(json); JsonValue *rootVal = JsonValueObject(json);
@ -169,8 +171,16 @@ query(char *select, HashMap * json)
} }
if (val) if (val)
{
if (canonical)
{
CanonicalJsonEncodeValue(val, StreamStdout());
}
else
{ {
JsonEncodeValue(val, StreamStdout(), JSON_PRETTY); JsonEncodeValue(val, StreamStdout(), JSON_PRETTY);
}
StreamPutc(StreamStdout(), '\n'); StreamPutc(StreamStdout(), '\n');
} }
@ -182,11 +192,19 @@ query(char *select, HashMap * json)
} }
static void static void
encode(char *str) encode(char *str, int canonical)
{ {
JsonValue *val = JsonValueString(str); JsonValue *val = JsonValueString(str);
if (canonical)
{
CanonicalJsonEncodeValue(val, StreamStdout());
}
else
{
JsonEncodeValue(val, StreamStdout(), JSON_DEFAULT); JsonEncodeValue(val, StreamStdout(), JSON_DEFAULT);
}
JsonValueFree(val); JsonValueFree(val);
StreamPutc(StreamStdout(), '\n'); StreamPutc(StreamStdout(), '\n');
} }
@ -200,8 +218,10 @@ Main(Array * args)
char *input = NULL; char *input = NULL;
ArgParseState arg; ArgParseState arg;
int canonical = 0;
ArgParseStateInit(&arg); ArgParseStateInit(&arg);
while ((ch = ArgParse(&arg, args, "s:e:")) != -1) while ((ch = ArgParse(&arg, args, "cs:e:")) != -1)
{ {
switch (ch) switch (ch)
{ {
@ -213,6 +233,9 @@ Main(Array * args)
flag = FLAG_ENCODE; flag = FLAG_ENCODE;
input = arg.optArg; input = arg.optArg;
break; break;
case 'c':
canonical = 1;
break;
default: default:
usage(ArrayGet(args, 0)); usage(ArrayGet(args, 0));
return 1; return 1;
@ -233,13 +256,21 @@ Main(Array * args)
switch (flag) switch (flag)
{ {
case FLAG_SELECT: case FLAG_SELECT:
query(input, json); /* This will implicitly free json */ query(input, json, canonical); /* This will implicitly free json */
break; break;
case FLAG_ENCODE: case FLAG_ENCODE:
encode(input); encode(input, canonical);
break; break;
default: default:
if (canonical)
{
CanonicalJsonEncode(json, StreamStdout());
}
else
{
JsonEncode(json, StreamStdout(), JSON_PRETTY); JsonEncode(json, StreamStdout(), JSON_PRETTY);
}
StreamPutc(StreamStdout(), '\n'); StreamPutc(StreamStdout(), '\n');
JsonFree(json); JsonFree(json);
break; break;