diff --git a/TODO.txt b/TODO.txt
index 3afe1ce..6ae1e11 100644
--- a/TODO.txt
+++ b/TODO.txt
@@ -35,7 +35,7 @@ Due: January 1, 2023
[x] HashMap
[ ] Http
[ ] HttpServer
- [ ] Json
+ [x] Json
[x] Log
[ ] Matrix
[x] Queue
diff --git a/man/man3/Json.3 b/man/man3/Json.3
new file mode 100644
index 0000000..0f37c6e
--- /dev/null
+++ b/man/man3/Json.3
@@ -0,0 +1,228 @@
+.Dd $Mdocdate: November 30 2022 $
+.Dt JSON 3
+.Os Telodendria Project
+.Sh NAME
+.Nm Json
+.Nd A fully-featured JSON API.
+.Sh SYNOPSIS
+.In Json.h
+.Ft JsonType
+.Fn JsonValueType "JsonValue *"
+.Ft JsonValue *
+.Fn JsonValueObject "HashMap *"
+.Ft HashMap *
+.Fn JsonValueAsObject "JsonValue *"
+.Ft JsonValue *
+.Fn JsonValueArray "Array *"
+.Ft Array *
+.Fn JsonValueAsArray "
+.Ft JsonValue *
+.Fn JsonValueString "char *"
+.Ft char *
+.Fn JsonValueAsString "JsonValue *"
+.Ft JsonValue *
+.Fn JsonValueInteger "long"
+.Ft long
+.Fn JsonValueAsInteger "JsonValue *"
+.Ft JsonValue *
+.Fn JsonValueFloat "double"
+.Ft double
+.Fn JsonValueAsFloat "JsonValue *"
+.Ft JsonValue *
+.Fn JsonValueBoolean "int"
+.Ft int
+.Fn JsonValueAsBoolean "JsonValue *"
+.Ft JsonValue *
+.Fn JsonValueNull "void"
+.Ft void
+.Fn JsonValueFree "JsonValue *"
+.Ft void
+.Fn JsonFree "HashMap *"
+.Ft void
+.Fn JsonEncodeString "const char *" "FILE *"
+.Ft void
+.Fn JsonEncodeValue "JsonValue *" "FILE *"
+.Ft int
+.Fn JsonEncode "HashMap *" "FILE *"
+.Ft HashMap *
+.Fn JsonDecode "FILE *"
+.Sh DESCRIPTION
+.Nm
+is a fully-featured JSON API for C using
+.Xr Array 3
+and
+.Xr HashMap 3
+that can parse JSON, and serialize an in-memory structure
+to JSON.
+It builds on the foundation set up by those APIs because
+that's all JSON really is, just maps and arrays.
+.Nm
+also provides a structure for encapsulating an arbitrary
+value and identifying its type, making it easy for a program
+to work with JSON data.
+.Nm
+is very strict and tries to adhere as closely as possible to
+the proper defintion of JSON. It will fail on syntax errors
+of any kind, which is fine for a Matrix homeserver because we can
+just return M_BAD_JSON if anything here fails, but it may not
+be suitable for other purposes.
+.Pp
+This JSON implementation focuses primarily on serialization and
+deserialization to and from streams. It does not provide facilities
+for handling JSON strings; it only writes JSON to output streams, and
+reads them from input streams. If course, you can use the POSIX
+.Xr fmemopen 3
+and
+.Xr open_memstream 3
+if you want to deal with JSON strings, but JSON is intended to be an
+exchange format. Data should be converted to JSON right before it is
+leaving the program, and converted from JSON as soon as it is coming
+in.
+.Pp
+The
+.Nm
+header defines the following enumeration for identifying types of
+values:
+.Bd -literal -offset indent
+typedef enum JsonType
+{
+ JSON_NULL, /* Maps to C NULL */
+ JSON_OBJECT, /* Maps to a HashMap of JsonValues */
+ JSON_ARRAY, /* Maps to an Array of JsonValues */
+ JSON_STRING, /* Maps to a NULL-terminated C string */
+ JSON_INTEGER, /* Maps to a C long */
+ JSON_FLOAT, /* Maps to a C double */
+ JSON_BOOLEAN /* Maps to a C boolean, 1 or 0 */
+} JsonType;
+.Ed
+.Pp
+A JsonValue encapsulates all the possible types that can be stored in
+JSON. It is an opaque structure that can be managed entirely by the
+functions defined in this API. It is important to note that in the case
+of objects, arrays, and strings, this structure only stores pointers to
+the allocated data, it doesn't store the data itself, but the data IS
+freed when using
+.Fn JsonFree .
+.Pp
+Objects are represented as hash maps consisting entirely of JsonValue
+structures, and arrays are represented as arrays consisting entirely
+of JsonValue structures. When generating a JSON object, any
+attempt to stuff a value into a hash map or array without encoding it
+as a JsonValue first will result in undefined behavior.
+.Pp
+.Fn JsonValueType
+determines the type of a given JsonValue.
+.Pp
+The
+.Fn JsonValue*
+functions wrap their input in a JsonValue that represents the given
+value. The
+.Fn JsonValueAs*
+functions do the opposite; they unwrap a JsonValue and return the
+actual usable value itself. They all closely resemble each other and
+they all behave the same way, so to save on time and effort, they're
+not explicitly documented individually. If something is unclear about
+how these functions work, consult the source code, and feel free
+to write the documentation yourself for clarification. Otherwise,
+reach out to the official Matrix rooms, and someone should be able
+to help you.
+.Pp
+.Fn JsonValueNull
+is a special case that represents a JSON null. Because
+.Xr Array 3
+and
+.Xr HashMap 3
+do not accept NULL values, this function should be used to represent
+NULL in JSON. Even though a small amount of memory is allocated just
+to point to NULL, this keeps the APIs clean.
+.Pp
+.Fn JsonValueFree
+frees the memory being used by a JSON value. Note that this will
+recursively free all Arrays, HashMaps, and other JsonValues that
+are reachable from the given value. It also invokes
+.Fn Free
+(documented in
+.Xr Memory )
+on all strings, so make sure passed string pointers point to strings
+on the heap, not the stack. This will be the case for all strings
+returned by
+.Fn JsonDecode ,
+but if you are manually creating JSON objects and stitching them
+together, you should be aware that calling this function on a value
+that contains a pointer to a stack string will result in undefined
+behavior.
+.Pp
+.Fn JsonFree
+recursively frees a JSON object, iterating over all the values and
+freeing them using
+.Fn JsonValueFree .
+.Pp
+.Fn JsonEncodeString
+encodes the given string in such a way that it can be embedded in a
+JSON stream. This entails:
+.Bl -bullet -offset indent
+.It
+Escaping quotes, backslashes, and other special characters using
+their backslash escape.
+.It
+Encoding bytes that are not UTF-8 using escapes.
+.It
+Wrapping the entire string in double quotes.
+.El
+.Pp
+This function is provided via the public
+.Nm
+API so that it is accessible to custom JSON encoders, such as
+.Xr CanonicalJson 3 .
+This will typically be used for encoding JSON keys; for encoding
+values, just use
+.Fn JsonEncodeValue .
+.Pp
+.Fn JsonEncodeValue
+serializes a JsonValue as it would appear in JSON output. This is
+a recursive function that also encodes all child values reachable
+from the given value. This function is exposed via the public
+.Nm
+API so that it is accessible to custom JSON encoders. Normal users
+that are not writing custom encoders should in most cases just use
+.Fn JsonEncode
+to encode an entire object.
+.Pp
+.Fn JsonEncode
+encodes a JSON object as minimized JSON and writes it to the given
+output stream. This function is recursive; it will serialize
+everything accessible from the passed object.
+.Fn JsonDecode
+does the opposite; it reads from a JSON stream and decodes it
+into a hash map of JsonValues.
+.Sh RETURN VALUES
+.Pp
+.Fn JsonValueType
+returns a JsonType, documented above, that tells what the given
+value actually is, or JSON_NULL if the passed value is NULL.
+Note that even a fully valid JsonValue may actually be of type
+JSON_NULL, so this function should not be used to determine
+whether or not a given value is valid.
+.Pp
+The
+.Fn JsonValue*
+functions return a JsonValue that holds a pointer to the passed
+value, or NULL if there was an error allocating memory. The
+.Fn JsonValueAs*
+functions return the actual value represented by the given
+JsonValue so that it can be manipulated by the program, or
+NULL if no value was provided, or the value is not of the
+correct type expected by the function.
+.Pp
+.Fn JsonEncode
+returns whether or not the encoding operation was successful.
+This function will fail if any passed parameters are NULL,
+otherwise it will assume all pointers are valid and return a
+success value.
+.Pp
+.Fn JsonDecode
+returns a hash map of JsonValues that can be manipulated by
+this API, or NULL if there was an error parsing the JSON.
+.Sh SEE ALSO
+.Xr HashMap 3 ,
+.Xr Array 3
diff --git a/site/index.html b/site/index.html
index 82ae7a3..820a655 100644
--- a/site/index.html
+++ b/site/index.html
@@ -229,6 +229,12 @@ cache.
Smart memory management API.
+
+Json(3) |
+
+A feature-complete API for reading and writing JSON.
+ |
+
Resources
diff --git a/src/include/Json.h b/src/include/Json.h
index 68e5993..c72757b 100644
--- a/src/include/Json.h
+++ b/src/include/Json.h
@@ -22,29 +22,6 @@
* SOFTWARE.
*/
-/*
- * Json.h: A fully-featured JSON API for C using Arrays and HashMaps.
- * This API builds on the foundations of Arrays and HashMaps, because
- * that's all a JSON object really is. It provides a JsonValue, which
- * is used to encapsulate arbitrary values while being able to identify
- * them in the future, so that JSON can be effectively handled.
- *
- * This implementation is just to get the job done in parsing and
- * generating JSON. It is extremely strict; it will fail on syntax
- * errors. This is fine for Matrix, because we can just return
- * M_BAD_JSON anything in here fails.
- *
- * One thing to note about this implementation is that it focuses
- * primarily on serialization and deserialization to and from streams.
- * What this means is that it does not provide facilities for handling
- * JSON strings; it only writes JSON to output streams, and reading
- * them from input streams. Of course, you could use the POSIX
- * fmemopen() and open_memstream() functions if you really want to deal
- * with JSON strings, but JSON is intended to be an exchange format.
- * Data should be converted to JSON when it is leaving, and converted
- * from JSON when it is coming in. Ideally, most of the program would
- * have no idea what JSON actually is.
- */
#ifndef TELODENDRIA_JSON_H
#define TELODENDRIA_JSON_H
@@ -54,223 +31,76 @@
#include
#include
-/*
- * All the possible JSON types. This enumeration is used to identify
- * the type of the value stored in a JsonValue.
- */
typedef enum JsonType
{
- JSON_NULL, /* Maps to nothing. */
- JSON_OBJECT, /* Maps to a HashMap of JsonValues */
- JSON_ARRAY, /* Maps to an Array of JsonValues */
- JSON_STRING, /* Maps to a C string */
- JSON_INTEGER, /* Maps to a C long */
- JSON_FLOAT, /* Maps to a C double */
- JSON_BOOLEAN /* Maps to a C 1 or 0 */
+ JSON_NULL,
+ JSON_OBJECT,
+ JSON_ARRAY,
+ JSON_STRING,
+ JSON_INTEGER,
+ JSON_FLOAT,
+ JSON_BOOLEAN
} JsonType;
-/*
- * A JsonValue encapsulates all the possible values that can be stored
- * in a JSON object as a single type, so as to provide a consistent
- * API for accessing and setting them. It is an opaque structure that
- * can be managed entirely by the functions defined in this API.
- *
- * Note that in the case of objects, arrays, and strings, this structure
- * only stores pointers to allocated data, it doesn't store the data
- * itself. JsonValues only store integers, floats, booleans, and NULL
- * in their memory. Anything else must be freed separately.
- */
typedef struct JsonValue JsonValue;
-/*
- * Get the type of a JsonValue.
- *
- * Params:
- *
- * (JsonValue *) The value to get the type of.
- *
- * Return: A JsonType that tells what the provided value is, or
- * JSON_NULL if the passed value is NULL. Note that even a fully
- * valid JsonValue may still be of type JSON_NULL, so this function
- * should not be used to check whether or not the JSON value is valid.
- */
extern JsonType
JsonValueType(JsonValue *);
-/*
- * Wrap a HashMap into a JsonValue that represents a JSON object. Note
- * that the HashMap should contain only JsonValues. Any other contents
- * are not supported and will lead to undefined behavior.
- *
- * Params:
- *
- * (HashMap *) The hash map of JsonValues to wrap in a JsonValue.
- *
- * Return: A JsonValue that holds a pointer to the given object, or
- * NULL if there was an error allocating memory.
- */
extern JsonValue *
JsonValueObject(HashMap *);
-/*
- * Get a HashMap from a JsonValue that represents a JSON object.
- *
- * Params:
- *
- * (JsonValue *) The value to extract the object from.
- *
- * Return: A HashMap of JsonValues, or NULL if no value was provided,
- * or the value is not of type JSON_OBJECT.
- */
extern HashMap *
JsonValueAsObject(JsonValue *);
-/*
- * The following methods very closely resemble the ones above, and
- * behave pretty much the exact same. To save on time and effort,
- * I'm choosing not to explicitly document all of these. If something
- * is unclear about how these functions work, consult the source code,
- * and then feel free to write the documentation yourself.
- *
- * Otherwise, reach out to the official Matrix room, and someone will
- * be able to help you.
- */
-
extern JsonValue *
- JsonValueArray(Array * array);
+ JsonValueArray(Array *);
extern Array *
- JsonValueAsArray(JsonValue * value);
+ JsonValueAsArray(JsonValue *);
extern JsonValue *
- JsonValueString(char *string);
+ JsonValueString(char *);
extern char *
JsonValueAsString(JsonValue *);
extern JsonValue *
- JsonValueInteger(long integer);
+ JsonValueInteger(long);
extern long
JsonValueAsInteger(JsonValue *);
extern JsonValue *
- JsonValueFloat(double floating);
+ JsonValueFloat(double);
+
+extern double
+JsonValueAsFloat(JsonValue *);
extern JsonValue *
- JsonValueBoolean(int boolean);
+ JsonValueBoolean(int);
+
+extern int
+JsonValueAsBoolean(JsonValue *);
-/*
- * Create a JsonValue that represents a JSON null. Because Arrays and
- * HashMaps should not contain NULL values, I thought it appropriate
- * to provide support for JSON nulls. Yes, a small amount of memory is
- * allocated just to point to a NULL, but this keeps all the APIs
- * clean.
- *
- * Return: A JsonValue that represents a JSON null, or NULL if memory
- * could not be allocated.
- */
extern JsonValue *
JsonValueNull(void);
-/*
- * Free the memory being used by a JSON value. Note that this will
- * recursively free all Arrays, HashMaps, and other JsonValues that
- * are reachable from this one. It will invoke free() on strings as
- * well, so make sure passed string pointers point to strings on the
- * heap, not the stack. This will be the case for all strings returned
- * by JsonDecode(), which is why this assumption is made. However, if
- * you are manually creating JsonObjects and stitching them together,
- * you'll have to manually free them as well. Calling this on a
- * JsonValue that contains a pointer to a stack string is undefined.
- *
- * Params:
- *
- * (JsonValue *) The JsonValue to recursively free.
- */
extern void
JsonValueFree(JsonValue *);
-/*
- * Recursively free a HashMap of JsonValues. This iterates over all
- * the JsonValues in a HashMap and frees them using JsonValueFree(),
- * which will in turn call JsonFree() on values of type JSON_OBJECT.
- *
- * Params:
- *
- * (HashMap *) The hash map of JsonValues to recursively free.
- */
extern void
JsonFree(HashMap *);
-/*
- * Encode the given string in such a way that it can be embedded in a
- * JSON stream. This entails:
- *
- * - Escaping quotes, backslashes, and other special characters using
- * their backslash escape
- * - Encoding bytes that are not UTF-8 using \u escapes.
- * - Wrapping the entire string in double quotes.
- *
- * This function is provided via the public API so it is accessible to
- * custom JSON encoders, such as the CanonicalJson API. This will
- * typically be used for encoding JSON keys; for values, just use
- * JsonEncodeValue().
- *
- * Params:
- *
- * (const char *) The C string to serialize as a JSON string.
- * (FILE *) The output stream to write the encoded string to.
- */
extern void
JsonEncodeString(const char *, FILE *);
-/*
- * Serialize a JsonValue as it would appear in JSON output. This is
- * a recursive function that will also encode all child values
- * reachable from the given JsonValue.
- *
- * This is exposed via the public API so that custom JSON encoders
- * such as CanonicalJson can take advantage of it. Normal users that
- * are writing custom encoders should just use JsonEncode() to encode
- * an entire object.
- *
- * Params:
- *
- * (JsonValue *) The value to encode.
- * (FILE *) The output stream to write the given value to.
- */
extern void
JsonEncodeValue(JsonValue * value, FILE * out);
-/*
- * Encode a HashMap of JsonValues into a fully-valid, minimized JSON
- * object. This function is recursive; it will serialize everything
- * accessible from the passed object into JSON.
- *
- * Params:
- *
- * (HashMap *) The HashMap of JsonValues to encode and write to the
- * output stream.
- * (FILE *) The output stream to write the given HashMap to.
- *
- * Return: Whether or not the operation was successful. This function
- * will fail if either the passed HashMap or file stream are NULL. In
- * all other cases, this function succeeds.
- */
extern int
JsonEncode(HashMap *, FILE *);
-/*
- * Decode the given input stream into a HashMap of JsonValues.
- *
- * Params:
- *
- * (FILE *) The input stream to parse JSON from.
- *
- * Return: A HashMap of JsonValues, or NULL if there was an error
- * parsing the JSON.
- */
extern HashMap *
JsonDecode(FILE *);