diff --git a/src/CanonicalJson.c b/src/CanonicalJson.c new file mode 100644 index 0000000..1352405 --- /dev/null +++ b/src/CanonicalJson.c @@ -0,0 +1,122 @@ +#include + +#include +#include +#include + +#include +#include + +int +CanonicalJsonKeyCompare(void *k1, void *k2) +{ + return strcmp((char *) k1, (char *) k2); +} + +static void +CanonicalJsonEncodeValue(JsonValue * value, FILE * out) +{ + Array *arr; + size_t i, len; + + /* Override object type to encode using the canonical functions */ + switch (JsonValueType(value)) + { + case JSON_OBJECT: + CanonicalJsonEncode(JsonValueAsObject(value), out); + break; + case JSON_ARRAY: + arr = JsonValueAsArray(value); + len = ArraySize(arr); + + fputc('[', out); + + for (i = 0; i < len; i++) + { + JsonValue *aVal = ArrayGet(arr, i); + + if (JsonValueType(aVal) == JSON_FLOAT) + { + /* See comment in CanonicalJsonEncode() */ + continue; + } + + CanonicalJsonEncodeValue(aVal, out); + if (i < len - 1) + { + fputc(',', out); + } + } + + fputc(']', out); + break; + default: + JsonEncodeValue(value, out); + break; + } +} + +int +CanonicalJsonEncode(HashMap * object, FILE * out) +{ + char *key; + JsonValue *value; + Array *keys; + size_t i; + size_t keyCount; + + if (!object || !out) + { + return 0; + } + + keys = ArrayCreate(); + if (!keys) + { + return 0; + } + + while (HashMapIterate(object, &key, (void **) &value)) + { + ArrayAdd(keys, key); + } + + ArraySort(keys, CanonicalJsonKeyCompare); + + fputc('{', out); + + keyCount = ArraySize(keys); + for (i = 0; i < keyCount; i++) + { + key = (char *) ArrayGet(keys, i); + value = (JsonValue *) HashMapGet(object, key); + + if (JsonValueType(value) == JSON_FLOAT) + { + /* + * "INFO: Float values are not permitted by this encoding." + * + * The spec doesn't say how a canonical JSON generator should + * handle float values, but given that it is highly unlikely + * that we will ever be using float values, it shouldn't be + * an issue if we just skip keys that have float values + * altogether. + */ + continue; + } + + JsonEncodeString(key, out); + fputc(':', out); + CanonicalJsonEncodeValue(value, out); + + if (i < keyCount - 1) + { + fputc(',', out); + } + } + + fputc('}', out); + + ArrayFree(keys); + return 1; +} diff --git a/src/include/CanonicalJson.h b/src/include/CanonicalJson.h new file mode 100644 index 0000000..60a5905 --- /dev/null +++ b/src/include/CanonicalJson.h @@ -0,0 +1,11 @@ +#ifndef TELODENDRIA_CANONICALJSON_H +#define TELODENDRIA_CANONICALJSON_H + +#include +#include + +extern int + CanonicalJsonEncode(HashMap * object, FILE * out); + +#endif +