Cytoplasm/src/include/Json.h

333 lines
12 KiB
C

/*
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net>
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef CYTOPLASM_JSON_H
#define CYTOPLASM_JSON_H
/***
* @Nm Json
* @Nd A fully-featured JSON API.
* @Dd March 12 2023
* @Xr HashMap Array Stream
*
* .Nm
* is a fully-featured JSON API for C using the array and hash map
* APIs. It can parse JSON, ans serialize an in-memory structure to
* JSON. It build on the foundation of Array and HashMap because that's
* all JSON really is, just arrays and maps.
* .Nm
* also provides a structure for encapsulating an arbitrary value and
* identifying its type, making it easy for a strictly-typed language
* like C to work with loosely-typed JSON data.
* .Nm
* is very strict and tries to adhere as closely as possible to the
* proper definition of JSON. It will fail on syntax errors of any
* kind, which is fine for a Matrix homeserver that can just return
* M_BAD_JSON if anything in here fails, but this behavior 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. Of course, you can use the
* POSIX
* .Xr fmemopen 3
* and
* .Xr open_memstream 3
* functions if you want to deal with JSON strings, but JSON is
* intended to be an exchange format. Data should be converted to JSON
* right when it is leaving the program, and converted from JSON to the
* in-memory format as soon as it is coming in.
* .Pp
* JSON 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 first encoding it as a JsonValue will result in undefined
* behavior.
*/
#include "HashMap.h"
#include "Array.h"
#include "Stream.h"
#include <stdio.h>
#include <stddef.h>
#include <stdbool.h>
#define JSON_DEFAULT -1
#define JSON_PRETTY 0
/**
* This opaque structure encapsulates all the possible types that can
* be stored in JSON. It is managed entirely by the functions defined
* in this API. It is important to note that strings, integers, floats,
* booleans, and the NULL value are all stored by value, but objects
* and arrays are stored by reference. That is, it doesn't store these
* itself, just pointers to them, however, the data
* .Em is
* freed when using
* .Fn JsonFree .
*/
typedef struct JsonValue JsonValue;
/**
* These are the types that can be used to identify a JsonValue
* and act on it accordingly.
*/
typedef enum JsonType
{
JSON_NULL, /* Maps to a 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 an Int64 */
JSON_FLOAT, /* Maps to a C double */
JSON_BOOLEAN /* Maps to a C integer of either 0 or 1 */
} JsonType;
/**
* Determine the type of the specified JSON value.
*/
extern JsonType JsonValueType(JsonValue *);
/**
* Encode a JSON object as a JSON value that can be added to another
* object, or an array.
*/
extern JsonValue * JsonValueObject(HashMap *);
/**
* Unwrap a JSON value that represents an object. This function will
* return NULL if the value is not actually an object.
*/
extern HashMap * JsonValueAsObject(JsonValue *);
/**
* Encode a JSON array as a JSON value that can be added to an object
* or another array.
*/
extern JsonValue * JsonValueArray(Array *);
/**
* Unwrap a JSON value that represents an array. This function will
* return NULL if the value is not actually an array.
*/
extern Array * JsonValueAsArray(JsonValue *);
/**
* Encode a C string as a JSON value that can be added to an object or
* an array.
*/
extern JsonValue * JsonValueString(char *);
/**
* Unwrap a JSON value that represents a string. This function will
* return NULL if the value is not actually a string.
*/
extern char * JsonValueAsString(JsonValue *);
/**
* Encode a number as a JSON value that can be added to an object or
* an array.
*/
extern JsonValue * JsonValueInteger(uint64_t);
/**
* Unwrap a JSON value that represents a number. This function will
* return 0 if the value is not actually a number, which may be
* misleading. Check the type of the value before making assumptions
* about its value.
*/
extern uint64_t JsonValueAsInteger(JsonValue *);
/**
* Encode a floating point number as a JSON value that can be added
* to an object or an array.
*/
extern JsonValue * JsonValueFloat(double);
/**
* Unwrap a JSON value that represents a floating point number. This
* function will return 0 if the value is not actually a floating
* point number, which may be misleading. Check the type of the value
* before making assumptions about its type.
*/
extern double JsonValueAsFloat(JsonValue *);
/**
* Encode a C integer according to the way C treats integers in boolean
* expressions as a JSON value that can be added to an object or an
* array.
*/
extern JsonValue * JsonValueBoolean(bool);
/**
* Unwrap a JSON value that represents a boolean. This function will
* return 0 if the value is not actually a boolean, which may be
* misleading. Check the type of the value before making assumptions
* about its type.
*/
extern bool JsonValueAsBoolean(JsonValue *);
/**
* This is a special case that represents a JSON null. Because the
* Array and HashMap APIs 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 be a placeholder for nothing,
* this keeps the APIs clean.
*/
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 the given value, including any strings attached to
* this value.
*/
extern void JsonValueFree(JsonValue *);
/**
* Recursively duplicate the given JSON value. This returns a new
* JSON value that is completely identical to the specified value, but
* in no way connected to it.
*/
extern JsonValue * JsonValueDuplicate(JsonValue *);
/**
* Recursively duplicate the given JSON object. This returns a new
* JSON object that is completely identical to the specified object,
* but in no way connect to it.
*/
extern HashMap * JsonDuplicate(HashMap *);
/**
* Recursively free a JSON object by iterating over all of its values
* and freeing them using
* .Fn JsonValueFree .
*/
extern void JsonFree(HashMap *);
/**
* Encode the given string in such a way that it can be safely
* 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 only provided via the public
* .Nm
* API so that it is accessible to custom JSON encoders, such as the
* CanonicalJson encoder. This will typically be used for encoding
* object keys; to encode values, just use
* .Fn JsonEncodeValue .
* .Pp
* This function returns the number of bytes written to the stream,
* or if the stream is NULL, the number of bytes that would have
* been written.
*/
extern size_t JsonEncodeString(const char *, Stream *);
/**
* Serialize a JSON value 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
* The third parameter is an integer that represents the indent level
* of the value to be printed, or a negative number if pretty-printing
* should be disabled and JSON should be printed as minimized as
* possible. To pretty-print a JSON object, set this to
* .Va JSON_PRETTY .
* To get minified output, set it to
* .Va JSON_DEFAULT .
* .Pp
* This function returns the number of bytes written to the stream,
* or if the stream is NULL, the number of bytes that would have
* been written.
*/
extern size_t JsonEncodeValue(JsonValue *, Stream *, int);
/**
* Encode a JSON object as it would appear in JSON output, writing it
* to the given output stream. This function is recursive; it will
* serialize everything accessible from the passed object. The third
* parameter has the same behavior as described above.
* .Pp
* This function returns the number of bytes written to the stream,
* or if the stream is NULL, the number of bytes that would have
* been written.
*/
extern size_t JsonEncode(HashMap *, Stream *, int);
/**
* Decode a JSON object from the given input stream and parse it into
* a hash map of JSON values.
*/
extern HashMap * JsonDecode(Stream *);
/**
* A convenience function that allows the caller to retrieve and
* arbitrarily deep keys within a JSON object. It takes a root JSON
* object, the number of levels deep to go, and then that number of
* keys as a varargs list. All keys must have objects as values, with
* the exception of the last one, which is the value that will be
* returned. Otherwise, NULL indicates the specified path doas not
* exist.
*/
extern JsonValue * JsonGet(HashMap *, size_t,...);
/**
* A convenience function that allows the caller to set arbitrarily
* deep keys within a JSON object. It takes a root JSON object, the
* number of levels deep to go, and then that number of keys as a
* varargs list. All keys must have object as values, with the
* exception of the last one, which is the value that will be set.
* The value currently at that key, if any, will be returned.
* This function will create any intermediate objects as necessary to
* set the proper key.
*/
extern JsonValue * JsonSet(HashMap *, JsonValue *, size_t,...);
/**
* Recursively merge two JSON objects. The second object is merged
* on top of the first; any keys present in the first object that are
* also present in the second object are replaced with those in the
* second object, and any keys present in the second object that are
* not present in the first object are copied to the first object.
*/
extern void JsonMerge(HashMap *, HashMap *);
#endif /* CYTOPLASM_JSON_H */