Start documenting the headers.

This commit is contained in:
Jordan Bancino 2022-07-29 12:32:52 -04:00
parent 1087069416
commit 8e8ac04505
7 changed files with 817 additions and 65 deletions

View file

@ -21,6 +21,25 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE. * SOFTWARE.
*/ */
/*
* Array.h: A simple array data structure that is automatically
* resized when new values are added. This implementation does not
* actually store the values of the items it; it only stores pointers
* to the data. As such, you will still have to manually maintain all
* your data. The advantage of this is that this array implementation
* doesn't have to copy items, and thus doesn't have to know how big
* they are.
*
* This array is optimized for storage space and appending. Deletions
* are expensive in that all the items of the list are moved down
* to fill the hole where the deletion occurred. Insertions are
* also expensive in that all the elements are shifted to make room
* for the new element.
*
* Due to these, this array implementation is really intended to be
* used primarily for linear writing and linear or random reading.
*/
#ifndef TELODENDRIA_ARRAY_H #ifndef TELODENDRIA_ARRAY_H
#define TELODENDRIA_ARRAY_H #define TELODENDRIA_ARRAY_H
@ -28,31 +47,171 @@
typedef struct Array Array; typedef struct Array Array;
/*
* Create a new, empty array on the heap.
*
* Params: none
*
* Return: A pointer to an Array, or NULL if there was an error
* allocating memory for the Array.
*/
extern Array * extern Array *
ArrayCreate(void); ArrayCreate(void);
/*
* Get the size of the provided array. Note that this is the number
* of elements in the array, not how much memory has been allocated
* for it.
*
* This is an extremely cheap operation, because the size does not
* need to be computed; rather it is just pulled right out of the
* Array and returned to the caller.
*
* Params:
*
* (Array *) The array to check the size of.
*
* Return: The number of elements in the provided array, or 0 if the
* provided array is NULL.
*/
extern size_t extern size_t
ArraySize(Array * array); ArraySize(Array *);
/*
* Get an element out of the provided array at the provided index.
*
* Params:
*
* (Array *) The array to get an element from.
* (size_t) The index of the array where the desired element is
* located.
*
* Return: A pointer to the data located at the given index, or NULL
* if no array was provided, or the index is greater than or equal
* to the size of the array.
*/
extern void * extern void *
ArrayGet(Array * array, size_t index); ArrayGet(Array *, size_t);
/*
* Insert an element into the array at the given index.
*
* Params:
*
* (Array *) The array to get the element from.
* (void *) The value to insert into the array.
* (size_t) The index at which the given value.
*
* Return: A boolean value that indicates whether or not the insert
* was successful. A return value of 0 indicates that the insert was
* NOT successful, and a return value of anything else indicates that
* the insert was successful.
*/
extern int extern int
ArrayInsert(Array *, void *value, size_t index); ArrayInsert(Array *, void *, size_t);
/*
* Append an element to the end of the array. This function actually
* uses ArrayInsert() under the hood, but it makes appending to an
* array more convenient because you don't necessarily have to keep
* track of the array's size.
*
* Params:
*
* (Array *) The array to append to.
* (void *) The value to append to the array.
*
* Return: The result of appending the element, which is the same as
* a call to ArrayInsert().
*/
extern int extern int
ArrayAdd(Array * array, void *value); ArrayAdd(Array *, void *);
/*
* Delete an element from an array by shifting all the elements that
* come after it down one index.
*
* Params:
*
* (Array *) The array to delete a value from.
* (size_t) The desired index to delete. All elements above this
* index are then shifted down to fill the gap.
*
* Return: A pointer to the deleted element, so that it can be freed
* or otherwise dealt with, or NULL if the array is NULL or the index
* is out of bounds.
*/
extern void * extern void *
ArrayDelete(Array * array, size_t index); ArrayDelete(Array *, size_t);
/*
* Sort the array using a simple quick-sort algorithm. This function
* works by taking a caller-specified compare function, so that no
* assumptions about the data stored in the array need to be made by
* this code.
*
* Params:
*
* (Array *) The array to sort. Note that the sort will be done
* in-place.
* (int (*)(void *, void *)) A function that takes in two void
* pointers and returns an integer. This function is
* responsible for comparing the passed items, and
* returning a code that indicates how they should be
* ordered. A return value of 0 indicates that the two
* items are identical. A return value greater than 0
* indicates that the first item is "bigger" than the
* second item and should thus appear after it in the
* array, and a return value less than zero indicates
* the opposite: that the second element should appear
* after the first in the array.
*
*/
extern void extern void
ArraySort(Array *, int (*compare) (void *, void *)); ArraySort(Array *, int (*) (void *, void *));
/*
* Free all the memory associated with the given array. Note that this
* does not free any of the values themselves; you should explicitly
* iterate over the array and free all the values stored inside it
* before calling this function, otherwise you may lose all the
* pointers the array contains, and thus have a memory leak.
*
* Params:
*
* (Array *) The array to free.
*
*/
extern void extern void
ArrayFree(Array * array); ArrayFree(Array *);
/*
* "Trim" the array by reallocating it with only the memory it needs
* to hold the items it currently has. This function might be beneficial
* to call on long-lived arrays that will be read-only, because it
* frees any memory on the end of the array that isn't being used. The
* array resizing algorithm will most likely allocate too much memory
* for most arrays as elements are added, because there's no way to
* know exactly how many elements will be stored.
*
* For example, a library that generates an Array to return to the
* user may wish to call this function on it right before returning to
* the caller if it is not expected that the caller will be modifying
* the array and may hang on to it for a long time.
*
* Params:
*
* (Array *) The array to trim extra memory (if any) off the end.
* Note that the array will still be fully-functional; if
* you add more elements, then more memory will be
* allocated like normal.
*
* Return: Whether or not the trim was successful. The trim may fail if
* realloc() fails, or NULL was passed for the array. If realloc()
* fails, this function is careful not to clobber the array. Upon
* failure of this function, the array is guaranteed to be unaltered.
*/
extern int extern int
ArrayTrim(Array * array); ArrayTrim(Array *);
#endif #endif

View file

@ -21,27 +21,135 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE. * SOFTWARE.
*/ */
/*
* Base64.h: An efficient base64 encoder and decoder that supports
* both regular base64, and the Matrix spec's "unpadded base64."
*/
#ifndef TELODENDRIA_BASE64_H #ifndef TELODENDRIA_BASE64_H
#define TELODENDRIA_BASE64_H #define TELODENDRIA_BASE64_H
#include <stddef.h> #include <stddef.h>
/*
* Compute the encoded size, including padding, of an input with the
* provided size.
*
* Params:
*
* (size_t) The size of the input data to be encoded.
*
* Return: The size of the string needed to hold the data in its
* encoded form. Note that base64 is not compression; base64 strings
* are actually 25% larger than the unencoded input.
*/
extern size_t extern size_t
Base64EncodedSize(size_t inputSize); Base64EncodedSize(size_t);
/*
* Compute the decoded size of the provide base64 input and length.
*
* Note that both the size and the actual base64 string itself is
* needed for this computation, unlike Base64EncodedSize(). This is
* because base64 strings are padded, and that padding is used in the
* calculations.
*
* Params:
*
* (const char *) A padded base64 string. If you are dealing with
* potentially user-provided base64, you should call
* Base64Pad() on it to normalize it before computing
* the decoded size.
* (size_t) The length of the base64 string. Instead of scanning the
* string for a null terminator, and then working backwards,
* the length of the string must be passed here.
*
* Return: The number of bytes that can be decoded from this base64
* string. Note that this will be smaller than the length of the base64
* string because base64 is larger than the unencoded form.
*/
extern size_t extern size_t
Base64DecodedSize(const char *base64, size_t len); Base64DecodedSize(const char *, size_t);
/*
* Copy the given input string to a new string, base64 encoding it in
* the process. This function will produce standard padded base64. If
* you want unpadded base64, call Base64Unpad() on the return value
* of this function.
*
* Params:
*
* (const char *) The raw, unencoded input to be encoded as base64.
* (size_t) The length of the unencoded input string.
*
* Return: A new string, allocated on the heap, that holds the base64
* representation of the input. This string must be free()-ed when it
* is no longer needed. If the allocation of the proper size fails,
* or the input is inaccessible, then this function will return NULL.
*/
extern char * extern char *
Base64Encode(const char *input, size_t len); Base64Encode(const char *, size_t);
/*
* Decode a standard padded base64 string. This function expects that
* the input will be padded, so if you are recieving untrusted input,
* you should run Base64Pad() on it before attempting to decode it.
*
* Params:
*
* (const char *) The base64 string to decode.
* (size_t) The length of the base64 string to decode.
*
* Return: A new string, allocated on the heap, that contains the
* decoded string, or NULL if a decoding error occurred.
*/
extern char * extern char *
Base64Decode(const char *input, size_t len); Base64Decode(const char *, size_t);
/*
* Remove the padding from a base64 string. This is to implement the
* Matrix spec's "unpadded base64" functionality. When base64 strings
* are sent to other servers and clients, their padding must be
* stripped.
*
* Params:
*
* (char *) The base64 string to remove padding from. Note that
* this string is modified in place.
* (size_t) The length of the provided base64 string.
*
*/
extern void extern void
Base64Unpad(char *base64, size_t length); Base64Unpad(char *, size_t);
/*
* Pad a base64 string in place. This is to implement the Matrix spec's
* "unpadded base64." As we will most likely be getting unpadded base64
* from clients and other servers, we should pad it before attempting
* to decode it.
*
* I technically could have just had the decoder accept unpadded as
* well as padded strings, but Matrix is the only thing I know of that
* actually makes "unpadded" base64 a thing, so I thought it best to
* make it clear in this library that unpadded base64 is an extension,
* not the norm.
*
* Params:
*
* (char **) A pointer to a base64 string pointer. The reason we
* take a pointer pointer is because the string may need
* to be reallocated, as characters may be added to the
* end of it. If the string is reallocated, then the
* passed pointer must be updated. If the string is not
* reallocated, the original pointer is not touched.
* (size_t) The length of the given base64 string.
*
* Return: Whether or not the pad operation was successful. This
* function will fail if a larger string cannot be allocated when it
* is needed. Note that not all cases require the string to be
* reallocated.
*/
extern int extern int
Base64Pad(char **base64Ptr, size_t length); Base64Pad(char **, size_t);
#endif #endif

View file

@ -21,13 +21,66 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE. * SOFTWARE.
*/ */
/*
* CanonicalJson.h: An expansion of the JSON encoding functionality
* that is specifically designed to produce the Matrix spec's
* "canonical" JSON.
*
* Canonical JSON is defined as JSON that:
*
* - Does not have any unecessary whitespace.
* - Has all object keys lexicographically sorted.
* - Does not contain any float values.
*
* The regular JSON encoder has no such rules, because normally they
* are not needed. However, Canonical JSON is needed to be able to
* sign JSON objects in a consistent way.
*/
#ifndef TELODENDRIA_CANONICALJSON_H #ifndef TELODENDRIA_CANONICALJSON_H
#define TELODENDRIA_CANONICALJSON_H #define TELODENDRIA_CANONICALJSON_H
#include <stdio.h> #include <stdio.h>
#include <HashMap.h> #include <HashMap.h>
/*
* Encode a JSON object following the rules of canonical JSON. See
* JsonEncode() for more details on how JSON encoding operates.
*
* This function exists as an alternative to JsonEncode(), but should
* not be preferred to JsonEncode() in normal circumstances. It is
* a lot more costly, as it must lexicographically sort all keys and
* strip out float values. If at all possible, use JsonEncode(),
* because it is much cheaper in terms of memory and CPU time.
*
* Params:
*
* (HashMap *) The JSON object to encode. Note that all values must
* be JsonValues.
* (FILE *) The output stream to write the JSON object to.
*
* Return: Whether or not the JSON encoding was successful. This
* function may fail if NULL was given for any parameter.
*/
extern int extern int
CanonicalJsonEncode(HashMap * object, FILE * out); CanonicalJsonEncode(HashMap *, FILE *);
/*
* Encode the JSON object to a string. The regular JSON encoding
* library doesn't have a way to send JSON to strings, because there's
* absolutely no reason to handle JSON strings. However, the sole
* reason canonical JSON exists is so that JSON objects can be signed.
* Thus, you need a string to pass to the signing function.
*
* Params:
*
* (HashMap *) The JSON object to encode. Note that all values must
* be JsonValues.
*
* Return: A string containing the canonical JSON representation of
* the given object, or NULL if the encoding failed.
*/
extern char *
CanonicalJsonEncodeToString(HashMap *);
#endif #endif

View file

@ -21,6 +21,22 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE. * SOFTWARE.
*/ */
/*
* Config.h: A heavily-modified version of Conifer2, a configuration
* file format specification and C parsing library written by Jordan
* Bancino. This library differs from Conifer2 in that the function
* naming convention has been updated to be consistent with Telodendria,
* and the underlying data structures have been overhauled to use the
* data structure libraries provided by Telodendria.
*
* Conifer2 was originally a learning project. It was very thoroughly
* debugged, however, and the configuration syntax was elegant,
* certainly more elegant than using JSON for a configuration file,
* so it was chosen to be the format for Telodendria's configuration
* file. The original Conifer2 project is now dead; Conifer2 lives on
* only as Telodendria's Config parsing library.
*/
#ifndef TELODENDRIA_CONFIG_H #ifndef TELODENDRIA_CONFIG_H
#define TELODENDRIA_CONFIG_H #define TELODENDRIA_CONFIG_H
@ -29,32 +45,164 @@
#include <HashMap.h> #include <HashMap.h>
#include <Array.h> #include <Array.h>
/*
* A configuration directive is a single key that may have at least one
* value, and any number of children.
*/
typedef struct ConfigDirective ConfigDirective; typedef struct ConfigDirective ConfigDirective;
/*
* The parser returns a parse result object. This stores whether or
* not the parse was successful, and then also additional information
* about the parse, such as the line number on which parsing failed,
* or the collection of directives if the parsing succeeded.
*
* There are a number of ConfigParseResult methods that can be used
* to query the result of parsing.
*/
typedef struct ConfigParseResult ConfigParseResult; typedef struct ConfigParseResult ConfigParseResult;
/*
* Parse a configuration file, and generate the structures needed to
* make it easy to read.
*
* Params:
*
* (FILE *) The input stream to read from.
*
* Return: A ConfigParseResult, which can be used to check whether or
* not the parsing was successful. If the parsing was sucessful, then
* this object contains the root directive, which can be used to
* retrieve configuration values out of. If the parsing failed, then
* this object contains the line number at which the parsing was
* aborted.
*/
extern ConfigParseResult * extern ConfigParseResult *
ConfigParse(FILE * stream); ConfigParse(FILE *);
/*
* Get whether or not a parse result indicates that parsing was
* successful or not. This function should be used to determine what
* to do next. If the parsing failed, your program should terminate
* with an error, otherwise, you can proceed to parse the configuration
* file.
*
* Params:
*
* (ConfigParseResult *) The output of ConfigParse() to check.
*
* Return: 0 if the configuration file is malformed, or otherwise
* could not be parsed. Any non-zero return value indicates that the
* configuration file was successfully parsed.
*/
extern unsigned int extern unsigned int
ConfigParseResultOk(ConfigParseResult * result); ConfigParseResultOk(ConfigParseResult *);
/*
* If, and only if, the configuration file parsing failed, then this
* function can be used to get the line number it failed at. Typically,
* this will be reported to the user and then the program will be
* terminated.
*
* Params:
*
* (ConfigParseResult *) The output of ConfigParse() to get the
* line number from.
*
* Return: The line number on which the configuration file parser
* choked, or 0 if the parsing was actually successful.
*/
extern size_t extern size_t
ConfigParseResultLineNumber(ConfigParseResult * result); ConfigParseResultLineNumber(ConfigParseResult *);
/*
* Convert a ConfigParseResult into a HashMap containing the entire
* configuration file, if, and only if, the parsing was successful.
*
* Params:
*
* (ConfigParseResult *) The output of ConfigParse() to get the
* actual configuration data from.
*
* Return: A HashMap containing all the configuration data, or NULL
* if the parsing was not successful. This HashMap is a map of string
* keys to ConfigDirective objects. Use the standard HashMap methods
* to get ConfigDirectives, and then use the ConfigDirective functions
* to get information out of them.
*/
extern HashMap * extern HashMap *
ConfigParseResultGet(ConfigParseResult * result); ConfigParseResultGet(ConfigParseResult *);
/*
* Free the memory being used by the given ConfigParseResult. Note that
* it is safe to free the ConfigParseResult immediately after you have
* retrieved either the line number or the configuration data from it.
* Freeing the parse result does not free the configuration data.
*
* Params:
*
* (ConfigParseResult *) The output of ConfigParse() to free. This
* object will be invalidated, but pointers to
* the actual configuration data will still be
* valid.
*/
extern void extern void
ConfigParseResultFree(ConfigParseResult * result); ConfigParseResultFree(ConfigParseResult *);
/*
* Get an array of values associated with the given configuration
* directive. Directives can have any number of values, which are
* made accessible via the Array API.
*
* Params:
*
* (ConfigDirective *) The configuration directive to get the values
* for.
*
* Return: An array that contains at least 1 value. Configuration files
* cannot have value-less directives. If the passed directive is NULL,
* or there is an error allocating memory for an array, then NULL is
* returned.
*/
extern Array * extern Array *
ConfigValuesGet(ConfigDirective * directive); ConfigValuesGet(ConfigDirective *);
/*
* Get a map of children associated with the given configuration
* directive. Configuration files can recurse with no practical limit,
* so directives can have any number of child directives.
*
* Params:
*
* (ConfigDirective *) The configuratio ndirective to get the
* children of.
*
* Return: A HashMap containing child directives, or NULL if the passed
* directive is NULL or has no children.
*/
extern HashMap * extern HashMap *
ConfigChildrenGet(ConfigDirective * directive); ConfigChildrenGet(ConfigDirective *);
/*
* Free all the memory associated with the given configuration hash
* map. Note: this will free *everything*. All Arrays, HashMaps,
* ConfigDirectives, and even strings will be invalidated. As such,
* this should be done after you either copy the values you want, or
* are done using them. It is highly recommended to use this function
* near the end of your program's execution during cleanup, otherwise
* copy any values you need into your own buffers.
*
* Note that this should only be run on the root configuration object,
* not any children. Running on children will produce undefined
* behavior. This function is recursive; it will get all the children
* under it.
*
* Params:
*
* (HashMap *) The configuration data to free.
*
*/
extern void extern void
ConfigFree(HashMap * conf); ConfigFree(HashMap *);
#endif /* TELODENDRIA_CONFIG_H */ #endif

View file

@ -21,6 +21,7 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE. * SOFTWARE.
*/ */
/* /*
* HashMap.h: The public interface for Telodendria's hash map * HashMap.h: The public interface for Telodendria's hash map
* implementation. This hash map is designed to be simple, well * implementation. This hash map is designed to be simple, well
@ -31,8 +32,6 @@
* Fundamentally, this is an entirely generic map implementation. It * Fundamentally, this is an entirely generic map implementation. It
* can be used for many general purposes, but it is designed to only * can be used for many general purposes, but it is designed to only
* implement the features that Telodendria needs to function well. * implement the features that Telodendria needs to function well.
*
* Copyright (C) 2022 Jordan Bancino <@jordan:bancino.net>
*/ */
#ifndef TELODENDRIA_HASHMAP_H #ifndef TELODENDRIA_HASHMAP_H
#define TELODENDRIA_HASHMAP_H #define TELODENDRIA_HASHMAP_H
@ -48,61 +47,137 @@
typedef struct HashMap HashMap; typedef struct HashMap HashMap;
/* /*
* HashMapCreate: Create a new HashMap object. * Create a new HashMap object.
* *
* Returns: A HashMap object that is ready to be used by the rest of * Return: A HashMap object that is ready to be used by the rest of
* the HashMap functions, or NULL if memory could not be allocated on * the HashMap functions, or NULL if memory could not be allocated on
* the heap. * the heap.
*/ */
extern HashMap * extern HashMap *
HashMapCreate(void); HashMapCreate(void);
/*
* Set the maximum load of the hash map before it is expanded. When the
* hash map reaches the given capacity, it is grown. You don't want
* to only grow hash maps when their full, because that makes them
* perform very poorly.
*
* The default max load on new HashMap objects is 0.75, which should be
* good enough for most purposes, but if you need finer tuning, feel
* free to play with the max load with this function. The changes take
* effect on the next insert.
*
* Params:
*
* (HashMap *) The HashMap to modify the maximum load for.
* (float) The new maximum load. This should be a value
* between 0 and 1, which specifies the percentange
* of "fullness" at which the HashMap will be
* expanded. If the new max load is out of bounds,
* this function does nothing.
*/
extern void extern void
HashMapMaxLoadSet(HashMap * map, float load); HashMapMaxLoadSet(HashMap *, float);
/* /*
* HashMapSet: Set the given key in the HashMap to the given value. Note * Set the given key in the HashMap to the given value. Note that the
* that the value is not copied into the HashMap's own memory space; * key nor the value is copied into the HashMap's own memory space;
* only the pointer is stored. It is the caller's job to ensure that the * only pointers is stored. It is the caller's job to ensure that the
* value's memory remains valid for the life of the HashMap. * key and value memory remains valid for the life of the HashMap, and
* are freed when they're no longer needed.
* *
* Returns: The previous value at the given key, or NULL if the key did * Params:
*
* (HashMap *) The hash map to set a key in.
* (char *) The key to set.
* (void *) The value to set at the given key.
*
* Return: The previous value at the given key, or NULL if the key did
* not previously exist or any of the parameters provided are NULL. All * not previously exist or any of the parameters provided are NULL. All
* keys must have values; you can't set a key to NULL. To delete a key, * keys must have values; you can't set a key to NULL. To delete a key,
* use HashMapDelete. * use HashMapDelete().
*/ */
extern void * extern void *
HashMapSet(HashMap * map, char *key, void *value); HashMapSet(HashMap *, char *, void *);
/* /*
* HashMapGet: Get the value for the given key. * Get the value for the given key.
* *
* Returns: The value at the given key, or NULL if the key does not * Params:
*
* (HashMap *) The hash map to check.
* (char *) The key to get the value for.
*
* Return: The value at the given key, or NULL if the key does not
* exist, no map was provided, or no key was provided. * exist, no map was provided, or no key was provided.
*/ */
extern void * extern void *
HashMapGet(HashMap * map, const char *key); HashMapGet(HashMap *, const char *);
/* /*
* HashMapDelete: Delete the value for the given key. * Delete the value for the given key.
* *
* Returns: The value at the given key, or NULL if the key does not * Params:
*
* (HashMap *) The map to delete the given key from.
* (const char *) The key to delete.
*
* Return: The value at the given key, or NULL if the key does not
* exist or the map or key was not provided. * exist or the map or key was not provided.
*/ */
extern void * extern void *
HashMapDelete(HashMap * map, const char *key); HashMapDelete(HashMap *, const char *);
extern int
HashMapIterate(HashMap * map, char **key, void **value);
/* /*
* HashMapFree: Free the hash map, returning its memory to the operating * Iterate over all the keys and values of a hash map. This function
* system. Note that this function does not free the values stored in * works similarly to the POSIX getopt(), where calls are repeatedly
* made in a "while" loop until there are no more items to go over.
*
* The difference is that this function does not rely on globals. This
* function takes pointer pointers, and stores necessary state inside
* the hash map structure itself.
*
* This function can be tricky to use in some scenarios, as it
* continues where it left off on each call, until there are no more
* elements. Then it returns 0 and resets the iterator, so that it can
* start over for the next iteration. This means that if you are not
* iterating over the entire map at one, and break the loop, the next
* time you try to iterate the HashMap, you'll start somewhere in the
* middle. Thus, it's recommended to iterate over the entire map. For
* scenarios in which the entire map needs to be iterated, such as
* when freeing all the keys and values, this function does well.
*
* Params:
*
* (HashMap *) The hash map to iterate over.
* (char **) A character pointer that will be set to the current
* key.
* (void **) A void pointer that will be set to the current value.
*
* Return: 1 if there are still elements left in this iteration of the
* hash map, or 0 if no valid hash map was provided, or there are no
* more elements in it for this iteration. Note that after this
* function returns 0 on a hash map, subsequent iterations will start
* from the beginning.
*/
extern int
HashMapIterate(HashMap *, char **, void **);
/*
* Free the hash map, returning its memory to the operating system.
* Note that this function does not free the keys or values stored in
* the map since this hash map implementation has no way of knowing * the map since this hash map implementation has no way of knowing
* what actually is stored in it. You should use HashMapIterate to * what actually is stored in it. You should use HashMapIterate() to
* free the values using your own algorithm. * free the values using your own algorithm.
*
* Params:
*
* (HashMap *) The hash map to free. The pointer can be safely
* discarded when this function returns. In fact,
* accessing it after this function returns is undefined
* behavior.
*/ */
extern void extern void
HashMapFree(HashMap *); HashMapFree(HashMap *);
#endif /* TELODENDRIA_HASHMAP_H */ #endif

View file

@ -21,6 +21,30 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE. * 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 #ifndef TELODENDRIA_JSON_H
#define TELODENDRIA_JSON_H #define TELODENDRIA_JSON_H
@ -30,27 +54,87 @@
#include <stdio.h> #include <stdio.h>
#include <stddef.h> #include <stddef.h>
/*
* All the possible JSON types. This enumeration is used to identify
* the type of the value stored in a JsonValue.
*/
typedef enum JsonType typedef enum JsonType
{ {
JSON_OBJECT, JSON_NULL, /* Maps to nothing. */
JSON_ARRAY, JSON_OBJECT, /* Maps to a HashMap of JsonValues */
JSON_STRING, JSON_ARRAY, /* Maps to an Array of JsonValues */
JSON_INTEGER, JSON_STRING, /* Maps to a C string */
JSON_FLOAT, JSON_INTEGER, /* Maps to a C long */
JSON_BOOLEAN, JSON_FLOAT, /* Maps to a C double */
JSON_NULL JSON_BOOLEAN /* Maps to a C 1 or 0 */
} JsonType; } 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; 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 extern JsonType
JsonValueType(JsonValue * value); 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 * extern JsonValue *
JsonValueObject(HashMap * object); 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 * extern HashMap *
JsonValueAsObject(JsonValue * value); 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 * extern JsonValue *
JsonValueArray(Array * array); JsonValueArray(Array * array);
@ -70,25 +154,118 @@ extern JsonValue *
extern JsonValue * extern JsonValue *
JsonValueBoolean(int boolean); JsonValueBoolean(int boolean);
/*
* 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 * extern JsonValue *
JsonValueNull(void); 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 extern void
JsonValueFree(JsonValue * value); 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 extern void
JsonFree(HashMap * object); 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 extern void
JsonEncodeString(const char *str, FILE * out); 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 extern void
JsonEncodeValue(JsonValue * value, FILE * out); 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 extern int
JsonEncode(HashMap * object, FILE * out); 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 * extern HashMap *
JsonDecode(FILE * in); JsonDecode(FILE *);
#endif #endif

View file

@ -21,12 +21,35 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE. * SOFTWARE.
*/ */
/*
* Log.h: A heavily-modified version of Shlog, a simple C logging
* facility that allows for colorful output, timestamps, and custom
* log levels. This library differs from Shlog in that the naming
* conventions have been updated to be consistent with Telodendria.
*
* Shlog was originally a learning project. It worked well, however,
* and produced elegant logging output, so it was chosen to be the
* main logging mechanism of Telodendria. The original Shlog project
* is now dead; Shlog lives on now only as Telodendria's logging
* mechanism.
*
* In the name of simplicity and portability, I opted to use an
* in-house logging system instead of syslog(), or other system logging
* mechanisms. However, this API could easily be patched to allow
* logging via other mechanisms that support the same features.
*/
#ifndef TELODENDRIA_LOG_H #ifndef TELODENDRIA_LOG_H
#define TELODENDRIA_LOG_H #define TELODENDRIA_LOG_H
#include <stdio.h> #include <stdio.h>
#include <stddef.h> #include <stddef.h>
/*
* There are five log "levels," each one showing more information than
* the previous one. A level of LOG_ERROR shows only errors, while a
* level of LOG_DEBUG shows all output possible.
*/
typedef enum LogLevel typedef enum LogLevel
{ {
LOG_ERROR, LOG_ERROR,
@ -36,11 +59,20 @@ typedef enum LogLevel
LOG_DEBUG LOG_DEBUG
} LogLevel; } LogLevel;
/*
* The possible flags that can be applied to alter the behavior of
* the logger
*/
typedef enum LogFlag typedef enum LogFlag
{ {
LOG_FLAG_COLOR = (1 << 0) LOG_FLAG_COLOR = (1 << 0) /* Enable color output on TTYs */
} LogFlag; } LogFlag;
/*
* The log configurations structure in which all settings exist.
* It's not super elegant to pass around a pointer to the logging
* configuration
*/
typedef struct LogConfig LogConfig; typedef struct LogConfig LogConfig;
extern LogConfig * extern LogConfig *