From cdf4430a9e933277a8ca2f7c76a4050b7e8d7dd5 Mon Sep 17 00:00:00 2001 From: lda Date: Sun, 26 May 2024 22:01:06 +0200 Subject: [PATCH 1/2] [FIX] Fix issue with const strings which may be invalidated --- src/Int64.c | 399 +++++++++++++++++++++++++++++++++++++ src/Memory.c | 14 +- src/UInt64.c | 265 ++++++++++++++++++++++++ src/include/Args.h | 82 ++++++++ src/include/Array.h | 187 +++++++++++++++++ src/include/Base64.h | 99 +++++++++ src/include/Cron.h | 140 +++++++++++++ src/include/Db.h | 169 ++++++++++++++++ src/include/Graph.h | 175 ++++++++++++++++ src/include/HashMap.h | 185 +++++++++++++++++ src/include/HeaderParser.h | 125 ++++++++++++ src/include/Http.h | 211 ++++++++++++++++++++ src/include/HttpClient.h | 104 ++++++++++ src/include/HttpRouter.h | 91 +++++++++ src/include/HttpServer.h | 234 ++++++++++++++++++++++ src/include/Int.h | 122 ++++++++++++ src/include/Int64.h | 252 +++++++++++++++++++++++ src/include/Io.h | 222 +++++++++++++++++++++ src/include/Json.h | 323 ++++++++++++++++++++++++++++++ src/include/Log.h | 196 ++++++++++++++++++ src/include/Memory.h | 235 ++++++++++++++++++++++ src/include/Queue.h | 105 ++++++++++ src/include/Rand.h | 81 ++++++++ src/include/Runtime.h | 53 +++++ src/include/Sha.h | 76 +++++++ src/include/Str.h | 129 ++++++++++++ src/include/Stream.h | 223 +++++++++++++++++++++ src/include/Tls.h | 109 ++++++++++ src/include/UInt64.h | 252 +++++++++++++++++++++++ src/include/Uri.h | 69 +++++++ src/include/Util.h | 117 +++++++++++ tools/int64.c | 145 ++++++++++++++ tools/uint64.c | 119 +++++++++++ 33 files changed, 5302 insertions(+), 6 deletions(-) create mode 100644 src/Int64.c create mode 100644 src/UInt64.c create mode 100644 src/include/Args.h create mode 100644 src/include/Array.h create mode 100644 src/include/Base64.h create mode 100644 src/include/Cron.h create mode 100644 src/include/Db.h create mode 100644 src/include/Graph.h create mode 100644 src/include/HashMap.h create mode 100644 src/include/HeaderParser.h create mode 100644 src/include/Http.h create mode 100644 src/include/HttpClient.h create mode 100644 src/include/HttpRouter.h create mode 100644 src/include/HttpServer.h create mode 100644 src/include/Int.h create mode 100644 src/include/Int64.h create mode 100644 src/include/Io.h create mode 100644 src/include/Json.h create mode 100644 src/include/Log.h create mode 100644 src/include/Memory.h create mode 100644 src/include/Queue.h create mode 100644 src/include/Rand.h create mode 100644 src/include/Runtime.h create mode 100644 src/include/Sha.h create mode 100644 src/include/Str.h create mode 100644 src/include/Stream.h create mode 100644 src/include/Tls.h create mode 100644 src/include/UInt64.h create mode 100644 src/include/Uri.h create mode 100644 src/include/Util.h create mode 100644 tools/int64.c create mode 100644 tools/uint64.c diff --git a/src/Int64.c b/src/Int64.c new file mode 100644 index 0000000..21f07f5 --- /dev/null +++ b/src/Int64.c @@ -0,0 +1,399 @@ +/* + * Copyright (C) 2022-2023 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. + */ +#include + +#include +#include + +#include + +#ifdef INT64_NATIVE +#define Int64Sign(x) ((int) (((UInt64) (x)) >> 63)) +#else +#define Int64Sign(x) ((int) ((x).i[1] >> 31)) +#endif + +size_t +Int64Str(Int64 x, int base, char *out, size_t len) +{ + static const char symbols[] = "0123456789ABCDEF"; + + size_t i = len - 1; + size_t j = 0; + + int neg = Int64Sign(x); + + Int64 base64 = Int64Create(0, base); + + /* We only have symbols up to base 16 */ + if (base < 2 || base > 16) + { + return 0; + } + + /* + * This algorithm doesn't work on INT64_MIN. + * + * But it works on all other integers in the range, so we + * just scoot the range in by one for now. It's a hack and + * I'm not a huge fan of it, but this function is mostly + * used in Json, which shouldn't have a range this large + * anyway (Json is limited to -2^53 -> 2^53-1). + * + * Proper fixes are always welcome. + */ + if (Int64Eq(x, Int64Create(0x80000000, 0x00000000))) + { + x = Int64Add(x, Int64Create(0, 1)); + } +#if 0 + else if (Int64Eq(x, Int64Create(0x7FFFFFFF, 0xFFFFFFFF))) + { + x = Int64Sub(x, Int64Create(0, 1)); + } +#endif + + if (base != 2 && base != 8 && base != 16 && neg) + { + x = Int64Neg(x); + } + + do + { + Int64 mod = Int64Rem(x, base64); + Int32 low = Int64Low(mod); + + out[i] = symbols[low]; + i--; + x = Int64Div(x, base64); + } while (Int64Gt(x, Int64Create(0, 0))); + + if (base != 2 && base != 8 && base != 16) + { + /* + * Binary, octal, and hexadecimal are known to + * be bit representations. Everything else (notably + * decimal) should include the negative sign. + */ + if (neg) + { + out[i] = '-'; + i--; + } + } + + while (++i < len) + { + out[j++] = out[i]; + } + + out[j] = '\0'; + + return j; +} + +#ifndef INT64_NATIVE + +/* No native 64-bit support, add our own */ + +Int64 +Int64Create(UInt32 high, UInt32 low) +{ + Int64 x; + + x.i[0] = low; + x.i[1] = high; + + return x; +} + +Int64 +Int64Add(Int64 x, Int64 y) +{ + Int64 z = Int64Create(0, 0); + int carry; + + z.i[0] = x.i[0] + y.i[0]; + carry = z.i[0] < x.i[0]; + z.i[1] = x.i[1] + y.i[1] + carry; + + return z; +} + +Int64 +Int64Sub(Int64 x, Int64 y) +{ + return Int64Add(x, Int64Neg(y)); +} + +Int64 +Int64Mul(Int64 x, Int64 y) +{ + Int64 z = Int64Create(0, 0); + + int xneg = Int64Sign(x); + int yneg = Int64Sign(y); + + if (xneg) + { + x = Int64Neg(x); + } + + if (yneg) + { + y = Int64Neg(y); + } + + /* while (y > 0) */ + while (Int64Gt(y, Int64Create(0, 0))) + { + /* if (y & 1 != 0) */ + if (Int64Neq(Int64And(y, Int64Create(0, 1)), Int64Create(0, 0))) + { + z = Int64Add(z, x); + } + + x = Int64Sll(x, 1); + y = Int64Sra(y, 1); + } + + if (xneg != yneg) + { + z = Int64Neg(z); + } + + return z; +} + +typedef struct +{ + Int64 q; + Int64 r; +} Int64Ldiv; + +static Int64Ldiv +Int64LongDivision(Int64 n, Int64 d) +{ + Int64Ldiv o; + + int i; + + int nneg = Int64Sign(n); + int dneg = Int64Sign(d); + + o.q = Int64Create(0, 0); + o.r = Int64Create(0, 0); + + if (Int64Eq(d, Int64Create(0, 0))) + { + raise(SIGFPE); + return o; + } + + if (nneg) + { + n = Int64Neg(n); + } + + if (dneg) + { + d = Int64Neg(d); + } + + for (i = 63; i >= 0; i--) + { + Int64 bit = Int64And(Int64Sra(n, i), Int64Create(0, 1)); + + o.r = Int64Sll(o.r, 1); + o.r = Int64Or(o.r, bit); + + if (Int64Geq(o.r, d)) + { + o.r = Int64Sub(o.r, d); + o.q = Int64Or(o.q, Int64Sll(Int64Create(0, 1), i)); + } + } + + if (nneg != dneg) + { + o.r = Int64Neg(o.r); + o.q = Int64Neg(o.q); + } + + return o; +} + +Int64 +Int64Div(Int64 x, Int64 y) +{ + return Int64LongDivision(x, y).q; +} + +Int64 +Int64Rem(Int64 x, Int64 y) +{ + return Int64LongDivision(x, y).r; +} + +Int64 +Int64Sll(Int64 x, int y) +{ + Int64 z; + + if (!y) + { + return x; + } + + z = Int64Create(0, 0); + + if (y < 32) + { + z.i[1] = (x.i[0] >> (32 - y)) | (x.i[1] << y); + z.i[0] = x.i[0] << y; + } + else + { + z.i[1] = x.i[0] << (y - 32); + } + + return z; +} + +Int64 +Int64Sra(Int64 x, int y) +{ + Int64 z; + + int neg = Int64Sign(x); + + if (!y) + { + return x; + } + + z = Int64Create(0, 0); + + if (y < 32) + { + z.i[0] = (x.i[1] << (32 - y)) | (x.i[0] >> y); + z.i[1] = x.i[1] >> y; + } + else + { + z.i[0] = x.i[1] >> (y - 32); + } + + if (neg) + { + Int64 mask = Int64Create(0xFFFFFFFF, 0xFFFFFFFF); + + z = Int64Or(Int64Sll(mask, (64 - y)), z); + } + + return z; +} + +Int64 +Int64And(Int64 x, Int64 y) +{ + return Int64Create(x.i[1] & y.i[1], x.i[0] & y.i[0]); +} + +Int64 +Int64Or(Int64 x, Int64 y) +{ + return Int64Create(x.i[1] | y.i[1], x.i[0] | y.i[0]); +} + +Int64 +Int64Xor(Int64 x, Int64 y) +{ + return Int64Create(x.i[1] ^ y.i[1], x.i[0] ^ y.i[0]); +} + +Int64 +Int64Not(Int64 x) +{ + return Int64Create(~(x.i[1]), ~(x.i[0])); +} + +int +Int64Eq(Int64 x, Int64 y) +{ + return x.i[0] == y.i[0] && x.i[1] == y.i[1]; +} + +int +Int64Lt(Int64 x, Int64 y) +{ + int xneg = Int64Sign(x); + int yneg = Int64Sign(y); + + if (xneg != yneg) + { + return xneg > yneg; + } + else + { + if (xneg) + { + /* Both negative */ + return x.i[1] > y.i[1] || (x.i[1] == y.i[1] && x.i[0] > y.i[0]); + } + else + { + /* Both positive */ + return x.i[1] < y.i[1] || (x.i[1] == y.i[1] && x.i[0] < y.i[0]); + } + } +} + +int +Int64Gt(Int64 x, Int64 y) +{ + int xneg = Int64Sign(x); + int yneg = Int64Sign(y); + + if (xneg != yneg) + { + return xneg < yneg; + } + else + { + if (xneg) + { + /* Both negative */ + return x.i[1] < y.i[1] || (x.i[1] == y.i[1] && x.i[0] < y.i[0]); + } + else + { + /* Both positive */ + return x.i[1] > y.i[1] || (x.i[1] == y.i[1] && x.i[0] > y.i[0]); + } + } + +} + +#endif diff --git a/src/Memory.c b/src/Memory.c index 4083143..01c98c5 100644 --- a/src/Memory.c +++ b/src/Memory.c @@ -40,10 +40,12 @@ #define MEMORY_HEXDUMP_WIDTH 16 #endif +#define MEMORY_FILE_SIZE 256 + struct MemoryInfo { size_t size; - const char *file; + char file[MEMORY_FILE_SIZE]; int line; void *pointer; }; @@ -227,7 +229,7 @@ MemoryAllocate(size_t size, const char *file, int line) MEM_BOUND_UPPER(p, size) = MEM_BOUND; a->size = MEM_SIZE_ACTUAL(size); - a->file = file; + strncpy(a->file, file, MEMORY_FILE_SIZE - 1); a->line = line; a->pointer = p; @@ -270,7 +272,7 @@ MemoryReallocate(void *p, size_t size, const char *file, int line) { MemoryDelete(a); a->size = MEM_SIZE_ACTUAL(size); - a->file = file; + strncpy(a->file, file, MEMORY_FILE_SIZE - 1); a->line = line; a->pointer = new; @@ -293,7 +295,7 @@ MemoryReallocate(void *p, size_t size, const char *file, int line) if (a) { a->size = 0; - a->file = file; + strncpy(a->file, file, MEMORY_FILE_SIZE - 1); a->line = line; a->pointer = p; hook(MEMORY_BAD_POINTER, a, hookArgs); @@ -322,7 +324,7 @@ MemoryFree(void *p, const char *file, int line) pthread_mutex_lock(&lock); if (hook) { - a->file = file; + strncpy(a->file, file, MEMORY_FILE_SIZE - 1); a->line = line; hook(MEMORY_FREE, a, hookArgs); } @@ -337,7 +339,7 @@ MemoryFree(void *p, const char *file, int line) a = malloc(sizeof(MemoryInfo)); if (a) { - a->file = file; + strncpy(a->file, file, MEMORY_FILE_SIZE - 1); a->line = line; a->size = 0; a->pointer = p; diff --git a/src/UInt64.c b/src/UInt64.c new file mode 100644 index 0000000..ae8eff7 --- /dev/null +++ b/src/UInt64.c @@ -0,0 +1,265 @@ +/* + * Copyright (C) 2022-2023 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. + */ +#include + +#include +#include + +size_t +UInt64Str(UInt64 x, int base, char *out, size_t len) +{ + static const char symbols[] = "0123456789ABCDEF"; + + size_t i = len - 1; + size_t j = 0; + + UInt64 base64 = UInt64Create(0, base); + + /* We only have symbols up to base 16 */ + if (base < 2 || base > 16) + { + return 0; + } + + do + { + UInt64 mod = UInt64Rem(x, base64); + UInt32 low = UInt64Low(mod); + + out[i] = symbols[low]; + i--; + x = UInt64Div(x, base64); + } while (UInt64Gt(x, UInt64Create(0, 0))); + + while (++i < len) + { + out[j++] = out[i]; + } + + out[j] = '\0'; + + return j; +} + +#ifndef UINT64_NATIVE + +/* No native 64-bit support, add our own */ + +UInt64 +UInt64Create(UInt32 high, UInt32 low) +{ + UInt64 x; + + x.i[0] = low; + x.i[1] = high; + + return x; +} + +UInt64 +UInt64Add(UInt64 x, UInt64 y) +{ + UInt64 z = UInt64Create(0, 0); + int carry; + + z.i[0] = x.i[0] + y.i[0]; + carry = z.i[0] < x.i[0]; + z.i[1] = x.i[1] + y.i[1] + carry; + + return z; +} + +UInt64 +UInt64Sub(UInt64 x, UInt64 y) +{ + UInt64 twosCompl = UInt64Add(UInt64Not(y), UInt64Create(0, 1)); + + return UInt64Add(x, twosCompl); +} + +UInt64 +UInt64Mul(UInt64 x, UInt64 y) +{ + UInt64 z = UInt64Create(0, 0); + + /* while (y > 0) */ + while (UInt64Gt(y, UInt64Create(0, 0))) + { + /* if (y & 1 != 0) */ + if (UInt64Neq(UInt64And(y, UInt64Create(0, 1)), UInt64Create(0, 0))) + { + z = UInt64Add(z, x); + } + + x = UInt64Sll(x, 1); + y = UInt64Srl(y, 1); + } + + return z; +} + +typedef struct +{ + UInt64 q; + UInt64 r; +} UInt64Ldiv; + +static UInt64Ldiv +UInt64LongDivision(UInt64 n, UInt64 d) +{ + UInt64Ldiv o; + + int i; + + o.q = UInt64Create(0, 0); + o.r = UInt64Create(0, 0); + + if (UInt64Eq(d, UInt64Create(0, 0))) + { + raise(SIGFPE); + return o; + } + + for (i = 63; i >= 0; i--) + { + UInt64 bit = UInt64And(UInt64Srl(n, i), UInt64Create(0, 1)); + + o.r = UInt64Sll(o.r, 1); + o.r = UInt64Or(o.r, bit); + + if (UInt64Geq(o.r, d)) + { + o.r = UInt64Sub(o.r, d); + o.q = UInt64Or(o.q, UInt64Sll(UInt64Create(0, 1), i)); + } + } + + return o; +} + +UInt64 +UInt64Div(UInt64 x, UInt64 y) +{ + return UInt64LongDivision(x, y).q; +} + +UInt64 +UInt64Rem(UInt64 x, UInt64 y) +{ + return UInt64LongDivision(x, y).r; +} + +UInt64 +UInt64Sll(UInt64 x, int y) +{ + UInt64 z; + + if (!y) + { + return x; + } + + z = UInt64Create(0, 0); + + if (y < 32) + { + z.i[1] = (x.i[0] >> (32 - y)) | (x.i[1] << y); + z.i[0] = x.i[0] << y; + } + else + { + z.i[1] = x.i[0] << (y - 32); + } + + return z; +} + +UInt64 +UInt64Srl(UInt64 x, int y) +{ + UInt64 z; + + if (!y) + { + return x; + } + + z = UInt64Create(0, 0); + + if (y < 32) + { + z.i[0] = (x.i[1] << (32 - y)) | (x.i[0] >> y); + z.i[1] = x.i[1] >> y; + } + else + { + z.i[0] = x.i[1] >> (y - 32); + } + + return z; +} + +UInt64 +UInt64And(UInt64 x, UInt64 y) +{ + return UInt64Create(x.i[1] & y.i[1], x.i[0] & y.i[0]); +} + +UInt64 +UInt64Or(UInt64 x, UInt64 y) +{ + return UInt64Create(x.i[1] | y.i[1], x.i[0] | y.i[0]); +} + +UInt64 +UInt64Xor(UInt64 x, UInt64 y) +{ + return UInt64Create(x.i[1] ^ y.i[1], x.i[0] ^ y.i[0]); +} + +UInt64 +UInt64Not(UInt64 x) +{ + return UInt64Create(~(x.i[1]), ~(x.i[0])); +} + +int +UInt64Eq(UInt64 x, UInt64 y) +{ + return x.i[0] == y.i[0] && x.i[1] == y.i[1]; +} + +int +UInt64Lt(UInt64 x, UInt64 y) +{ + return x.i[1] < y.i[1] || (x.i[1] == y.i[1] && x.i[0] < y.i[0]); +} + +int +UInt64Gt(UInt64 x, UInt64 y) +{ + return x.i[1] > y.i[1] || (x.i[1] == y.i[1] && x.i[0] > y.i[0]); +} + +#endif diff --git a/src/include/Args.h b/src/include/Args.h new file mode 100644 index 0000000..3108de3 --- /dev/null +++ b/src/include/Args.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2022-2023 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_ARGS_H +#define CYTOPLASM_ARGS_H + +/*** + * @Nm Args + * @Nd Getopt-style argument parser that operates on arrays. + * @Dd May 12 2023 + * @Xr Array + * + * .Nm + * provides a simple argument parser in the style of + * .Xr getopt 3 . + * It exists because the runtime passes the program arguments as + * an Array, and it is often useful to parse the arguments to + * provide the standard command line interface. + */ + +#include + +/** + * All state is stored in this structure, instead of global + * variables. This makes + * .Nm + * thread-safe and easy to reset. + */ +typedef struct ArgParseState +{ + int optInd; + int optErr; + int optOpt; + char *optArg; + + int optPos; +} ArgParseState; + +/** + * Initialize the variables in the given parser state structure + * to their default values. This should be called before + * .Fn ArgParse + * is called with the parser state. It should also be called if + * .Fn ArgParse + * will be used again on a different array, or the same array all + * over again. + */ +extern void ArgParseStateInit(ArgParseState *); + +/** + * Parse command line arguments stored in the given array, using + * the given state and option string. This function behaves + * identically to the POSIX + * .Fn getopt + * function, and should be used in exactly the same way. Refer to + * your system's + * .Xr getopt 3 + * page for details. + */ +extern int ArgParse(ArgParseState *, Array *, const char *); + +#endif /* CYTOPLASM_ARGS_H */ diff --git a/src/include/Array.h b/src/include/Array.h new file mode 100644 index 0000000..0ba1335 --- /dev/null +++ b/src/include/Array.h @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2022-2023 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_ARRAY_H +#define CYTOPLASM_ARRAY_H + +/*** + * @Nm Array + * @Nd A simple dynamic array data structure. + * @Dd November 24 2022 + * @Xr HashMap Queue + * + * These functions implement a simple array data structure that is + * automatically resized as necessary when new values are added. This + * implementation does not actually store the values of the items in 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 these + * functions don't have to copy data, and thus don't care how big the + * data is. Furthermore, arbitrary data can be stored in the array. + * .Pp + * This array implementation is optimized for storage space and + * appending. Deletions are expensive in that all the items of the list + * above a deletion are moved down to fill the hole where the deletion + * occurred. Insertions are also expensive in that all the elements + * above the given index must be shifted up to make room for the new + * element. + * .Pp + * Due to these design choices, this array implementation is best + * suited to linear writing, and then linear or random reading. + */ + +#include +#include + +/** + * The functions in this API operate on an array structure which is + * opaque to the caller. + */ +typedef struct Array Array; + +/** + * Allocate a new array. This function returns a pointer that can be + * used with the other functions in this API, or NULL if there was an + * error allocating memory for the array. + */ +extern Array *ArrayCreate(void); + +/** + * Deallocate an array. Note that this function does not free any of + * the values stored in the array; it is the caller's job to manage the + * memory for each item. Typically, the caller would iterate over all + * the items in the array and free them before freeing the array. + */ +extern void ArrayFree(Array *); + +/** + * Get the size, in number of elements, of the given array. + */ +extern size_t ArraySize(Array *); + +/** + * Get the element at the specified index from the specified array. + * This function will return NULL if the array is NULL, or the index + * is out of bounds. Otherwise, it will return a pointer to a value + * put into the array using + * .Fn ArrayInsert + * or + * .Fn ArraySet . + */ +extern void *ArrayGet(Array *, size_t); + +/** + * Insert the specified element at the specified index in the specified + * array. This function will shift the element currently at that index, + * and any elements after it before inserting the given element. + * .Pp + * This function returns a boolean value indicating whether or not it + * suceeded. + */ +extern int ArrayInsert(Array *, size_t, void *); + +/** + * Set the value at the specified index in the specified array to the + * specified value. This function will return the old value at that + * index, if any. + */ +extern void *ArraySet(Array *, size_t, void *); + +/** + * Append the specified element to the end of the specified array. This + * function uses + * .Fn ArrayInsert + * under the hood to insert an element at the end. It thus has the same + * return value as + * .Fn ArrayInsert . + */ +extern int ArrayAdd(Array *, void *); + +/** + * Remove the element at the specified index from the specified array. + * This function returns the element removed, if any. + */ +extern void *ArrayDelete(Array *, size_t); + +/** + * Sort the specified array using the specified sort function. The + * sort function compares two elements. It takes two void pointers as + * parameters, and returns an integer. The return value indicates to + * the sorting algorithm how the elements relate to each other. A + * return value of 0 indicates that the elements 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. A return value less than 0 indicates the opposite: the + * second element should appear after the first in the array. + */ +extern void ArraySort(Array *, int (*) (void *, void *)); + +/** + * Remove all duplicates from an array by using the given comparison + * function to sort the array, then remove matching values. This + * function returns a new array with all duplicates removed. The + * original array is unaffected. Note that arrays only store pointers + * to values, usually values on the heap. Thus, it is possible to lose + * pointers to duplicate values and have them leak. + * .P + * This is a relatively expensive operation. The array must first be + * duplicated. Then it is sorted, then it is iterated from beginning + * to end to remove duplicate entires. Note that the comparison + * function is executed on each element at least twice. + */ +extern Array *ArrayUnique(Array *, int (*) (void *, void *)); + + +/** + * Reverses the order of all elements in the array into a new array on + * the heap, to be freed using + * .Fn ArrayFree . + */ +extern Array *ArrayReverse(Array *); + +/** + * If possible, reduce the amount of memory allocated to this array + * by calling + * .Fn Realloc + * on the internal structure to perfectly fit the elements in the + * array. This function is intended to be used by functions that return + * relatively read-only arrays that will be long-lived. + */ +extern int ArrayTrim(Array *); + +/** + * Convert a variadic arguments list into an Array. In most cases, the + * Array API is much easier to work with than + * .Fn va_arg + * and friends. + */ +extern Array *ArrayFromVarArgs(size_t, va_list); + +/** + * Duplicate an existing array. Note that arrays only hold pointers to + * their data, not the data itself, so the duplicated array will point + * to the same places in memory as the original array. + */ +extern Array *ArrayDuplicate(Array *); + +#endif /* CYTOPLASM_ARRAY_H */ diff --git a/src/include/Base64.h b/src/include/Base64.h new file mode 100644 index 0000000..b36a48c --- /dev/null +++ b/src/include/Base64.h @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2022-2023 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_BASE64_H +#define CYTOPLASM_BASE64_H + +/*** + * @Nm Base64 + * @Nd A simple base64 encoder/decoder with unpadded base64 support. + * @Dd September 30 2022 + * @Xr Sha2 + * + * This is an efficient yet simple base64 encoding and decoding API + * that supports regular base64, as well as the Matrix specification's + * extension to base64, called ``unpadded base64.'' This API provides + * the ability to convert between the two, instead of just implementing + * unpadded base64. + */ + +#include + +/** + * This function computes the amount of bytes needed to store a message + * of the specified number of bytes as base64. + */ +extern size_t + Base64EncodedSize(size_t); + +/** + * This function computes the amount of bytes needed to store a decoded + * representation of the encoded message. It takes a pointer to the + * encoded string because it must read a few bytes off the end in order + * to accurately compute the size. + */ +extern size_t + Base64DecodedSize(const char *, size_t); + +/** + * Encode the specified number of bytes from the specified buffer as + * base64. This function returns a string on the heap that should be + * freed with + * .Fn Free , + * or NULL if a memory allocation error ocurred. + */ +extern char * + Base64Encode(const char *, size_t); + +/** + * Decode the specified number of bytes from the specified buffer of + * base64. This function returns a string on the heap that should be + * freed with + * .Fn Free , + * or NULL if a memory allocation error occured. + */ +extern char * + Base64Decode(const char *, size_t); + +/** + * Remove the padding from a specified base64 string. This function + * modifies the specified string in place. It thus has no return value + * because it cannot fail. If the passed pointer is invalid, the + * behavior is undefined. + */ +extern void + Base64Unpad(char *, size_t); + +/** + * Add padding to an unpadded base64 string. This function takes a + * pointer to a pointer because it may be necessary to grow the memory + * allocated to the string. This function returns a boolean value + * indicating whether the pad operation was successful. In practice, + * this means it will only fail if a bigger string is necessary, but it + * could not be automatically allocated on the heap. + */ +extern int + Base64Pad(char **, size_t); + +#endif /* CYTOPLASM_BASE64_H */ diff --git a/src/include/Cron.h b/src/include/Cron.h new file mode 100644 index 0000000..e78900d --- /dev/null +++ b/src/include/Cron.h @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2022-2023 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_CRON_H +#define CYTOPLASM_CRON_H + +/*** + * @Nm Cron + * @Nd Basic periodic job scheduler. + * @Dd December 24 2022 + * + * This is an extremely basic job scheduler. So basic, in fact, that + * it currently runs all jobs on a single thread, which means that it + * is intended for short-lived jobs. In the future, it might be + * extended to support a one-thread-per-job model, but for now, jobs + * should take into consideration the fact that they are sharing their + * thread, so they should not be long-running. + * .Pp + * .Nm + * works by ``ticking'' at an interval defined by the caller of + * .Fn CronCreate . + * At each tick, all the registered jobs are queried, and if they are + * due to run again, their function is executed. As much as possible, + * .Nm + * tries to tick at constant intervals, however it is possible that one + * or more jobs may overrun the tick duration. If this happens, + * .Nm + * ticks again immediately after all the jobs for the previous tick + * have completed. This is in an effort to compensate for lost time, + * however it is important to note that when jobs overrun the tick + * interval, the interval is pushed back by the amount that it was + * overrun. Because of this, + * .Nm + * is best suited for scheduling jobs that should happen + * ``aproximately'' every so often; it is not a real-time scheduler + * by any means. + */ + +#include + +/** + * All functions defined here operate on a structure opaque to the + * caller. + */ +typedef struct Cron Cron; + +/** + * A job function is a function that takes a void pointer and returns + * nothing. The pointer is passed when the job is scheduled, and + * is expected to remain valid until the job is no longer registered. + * The pointer is passed each time the job executes. + */ +typedef void (JobFunc) (void *); + +/** + * Create a new + * .Nm + * object that all other functions operate on. Like most of the other + * APIs in this project, it must be freed with + * .Fn CronFree + * when it is no longer needed. + * .Pp + * This function takes the tick interval in milliseconds. + */ +extern Cron * CronCreate(UInt32); + +/** + * Schedule a one-off job to be executed only at the next tick, and + * then immediately discarded. This is useful for scheduling tasks that + * only have to happen once, or very infrequently depending on + * conditions other than the current time, but don't have to happen + * immediately. The caller simply indicates that it wishes for the task + * to execute at some time in the future. How far into the future this + * practically ends up being is determined by how long it takes for + * other registered jobs to finish, and what the tick interval is. + * .Pp + * This function takes a job function and a pointer to pass to that + * function when it is executed. + */ +extern void + CronOnce(Cron *, JobFunc *, void *); + +/** + * Schedule a repetitive task to be executed at aproximately the given + * interval. As stated above, this is as fuzzy interval; depending on + * the jobs being run and the tick interval, tasks may not execute at + * exactly the scheduled time, but they will eventually execute. + * .Pp + * This function takes an interval in milliseconds, a job function, + * and a pointer to pass to that function when it is executed. + */ +extern void + CronEvery(Cron *, unsigned long, JobFunc *, void *); + +/** + * Start ticking the clock and executing registered jobs. + */ +extern void + CronStart(Cron *); + +/** + * Stop ticking the clock. Jobs that are already executing will + * continue to execute, but when they are finished, no new jobs will + * be executed until + * .Fn CronStart + * is called. + */ +extern void + CronStop(Cron *); + +/** + * Discard all job references and free all memory associated with the + * given + * .Nm Cron + * instance. + */ +extern void + CronFree(Cron *); + +#endif /* CYTOPLASM_CRON_H */ diff --git a/src/include/Db.h b/src/include/Db.h new file mode 100644 index 0000000..618a2e2 --- /dev/null +++ b/src/include/Db.h @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2022-2023 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_DB_H +#define CYTOPLASM_DB_H + +/*** + * @Nm Db + * @Nd A minimal flat-file database with mutex locking and cache. + * @Dd April 27 2023 + * @Xr Json + * + * Cytoplasm operates on a flat-file database instead of a + * traditional relational database. This greatly simplifies the + * persistent storage code, and creates a relatively basic API, + * described here. + */ + +#include + +#include +#include + +/** + * All functions in this API operate on a database structure that is + * opaque to the caller. + */ +typedef struct Db Db; + +/** + * When an object is locked, a reference is returned. This reference + * is owned by the current thread, and the database is inaccessible to + * other threads until all references have been returned to the + * database. + * .Pp + * This reference is opaque, but can be manipulated by the functions + * defined here. + */ +typedef struct DbRef DbRef; + +/** + * Open a data directory. This function takes a path to open, and a + * cache size in bytes. If the cache size is 0, then caching is + * disabled and objects are loaded off the disk every time they are + * locked. Otherwise, objects are stored in the cache, and they are + * evicted in a least-recently-used manner. + */ +extern Db * DbOpen(char *, size_t); + +/** + * Close the database. This function will flush anything in the cache + * to the disk, and then close the data directory. It assumes that + * all references have been unlocked. If a reference has not been + * unlocked, undefined behavior results. + */ +extern void DbClose(Db *); + +/** + * Set the maximum cache size allowed before + * .Nm + * starts evicting old objects. If this is set to 0, everything in the + * cache is immediately evicted and caching is disabled. If the + * database was opened with a cache size of 0, setting this will + * initialize the cache, and subsequent calls to + * .Fn DbLock + * will begin caching objects. + */ +extern void DbMaxCacheSet(Db *, size_t); + +/** + * Create a new object in the database with the specified name. This + * function will fail if the object already exists in the database. It + * takes a variable number of C strings, with the exact number being + * specified by the second parameter. These C strings are used to + * generate a filesystem path at which to store the object. These paths + * ensure each object is uniquely identifiable, and provides semantic + * meaning to an object. + */ +extern DbRef * DbCreate(Db *, size_t,...); + +/** + * Lock an existing object in the database. This function will fail + * if the object does not exist. It takes a variable number of C + * strings, with the exact number being specified by the second + * parameter. These C strings are used to generate the filesystem path + * at which to load the object. These paths ensure each object is + * uniquely identifiable, and provides semantic meaning to an object. + */ +extern DbRef * DbLock(Db *, size_t,...); + +/** + * Immediately and permanently remove an object from the database. + * This function assumes the object is not locked, otherwise undefined + * behavior will result. + */ +extern int DbDelete(Db *, size_t,...); + +/** + * Unlock an object and return it back to the database. This function + * immediately syncs the object to the filesystem. The cache is a + * read cache; writes are always immediate to ensure data integrity in + * the event of a system failure. + */ +extern int DbUnlock(Db *, DbRef *); + +/** + * Check the existence of the given database object in a more efficient + * manner than attempting to lock it with + * .Fn DbLock . + * This function does not lock the object, nor does it load it into + * memory if it exists. + */ +extern int DbExists(Db *, size_t,...); + +/** + * List all of the objects at a given path. Unlike the other varargs + * functions, this one does not take a path to a specific object; it + * takes a directory to be iterated, where each path part is its own + * C string. Note that the resulting list only contains the objects + * in the specified directory, it does not list any subdirectories. + * .Pp + * The array returned is an array of C strings containing the object + * name. + */ +extern Array * DbList(Db *, size_t,...); + +/** + * Free the list returned by + * .Fn DbListFree . + */ +extern void DbListFree(Array *); + +/** + * Convert a database reference into JSON that can be manipulated. + * At this time, the database actually stores objects as JSON on the + * disk, so this function just returns an internal pointer, but in the + * future it may have to be generated by decompressing a binary blob, + * or something of that nature. + */ +extern HashMap * DbJson(DbRef *); + +/** + * Free the existing JSON associated with the given reference, and + * replace it with new JSON. This is more efficient than duplicating + * a separate object into the database reference. + */ +extern int DbJsonSet(DbRef *, HashMap *); + +#endif diff --git a/src/include/Graph.h b/src/include/Graph.h new file mode 100644 index 0000000..02e4e02 --- /dev/null +++ b/src/include/Graph.h @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2022-2023 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_GRAPH_H +#define CYTOPLASM_GRAPH_H + +/*** + * @Nm Graph + * @Nd Extremely simple graph, implemented as an adjacency matrix. + * @Dd July 15 2023 + * + * .Nm + * is a basic graph data structure originally written for a computer + * science class on data structures and algorithms, in which it + * received full credit. This is an adaptation of the original + * implementation that follows the Cytoplasm style and uses Cytoplasm + * APIs when convenient. + * .P + * .Nm + * stores data in an adjacency matrix, which means the storage + * complexity is O(N^2), where N is the number of vertices (called + * Nodes in this implementation) in the graph. However, this makes the + * algorithms fast and efficient. + * .P + * Nodes are identified by index, so the first node is 0, the second + * is 1, and so on. This data structure does not support storing + * arbitrary data as nodes; rather, the intended use case is to add + * all your node data to an Array, thus giving each node an index, + * and then manipulating the graph with that index. This allows access + * to node data in O(1) time in call cases, and is the most memory + * efficient. + * .P + * .Nm + * can be used to store a variety of types of graphs, although it is + * primarily suited to directed and weighted graphs. + */ + +#include + +/** + * The functions provided here operate on an opaque graph structure. + * This structure really just stores a matrix in a contiguous block of + * memory, as well as the number of nodes in the graph, but the + * structure is kept opaque so that it remains internally consistent. + * It also maintains the style of the Cytoplasm library. + */ +typedef struct Graph Graph; + +/** + * An Edge is really just a weight, which is easily represented by an + * integer. However, it makes sense to alias this to Edge for clarity, + * both in the documentation and in the implementation. + */ +typedef int Edge; + +/** + * A Node is really just a row or column in the matrix, which is easily + * represented by an unsigned integer. However, it makes sense to alias + * this to Node for clarity, both in the documentation and the + * implementation. + */ +typedef size_t Node; + +/** + * Create a new graph structure with the given number of vertices. + */ +extern Graph *GraphCreate(size_t); + +/** + * Create a new graph data structure with the given number of vertices + * and the given adjacency matrix. The adjacency matrix is copied + * verbatim into the graph data structure without any validation. + */ +extern Graph *GraphCreateWithEdges(size_t, Edge *); + +/** + * Free all memory associated with the given graph. Since graphs are + * just a collection of numbers, they do not depend on each other in + * any way. + */ +extern void GraphFree(Graph *); + +/** + * Get the weight of the edge connecting the node specified first to + * the node specified second. If this is a directed graph, it does not + * necessarily follow that there is an edge from the node specified + * second to the node specified first. It also does not follow that + * such an edge, if it exists, has the same weight. + * .P + * This function will return -1 if the graph is invalid or either node + * is out of bounds. It will return 0 if there is no such edge from the + * node specified first to the node specified second. + */ +extern Edge GraphEdgeGet(Graph *, Node, Node); + +/** + * Set the weight of the edge connecting the node specified first to + * the node specified second. If this is not a directed graph, this + * function will have to be called twice, the second time reversing the + * order of the nodes. To remove the edge, specify a weight of 0. + */ +extern Edge GraphEdgeSet(Graph *, Node, Node, Edge); + +/** + * Get the number of nodes in the given graph. This operation is a + * simple memory access that happens in O(1) time. + */ +extern size_t GraphCountNodes(Graph *); + +/** + * Perform a breadth-first search on the given graph, starting at the + * specified node. This function returns a list of nodes in the order + * they were touched. The size of the list is stored in the unsigned + * integer pointed to by the last argument. + * .P + * If an error occurs, NULL will be returned. Otherwise, the returned + * pointer should be freed with the Memory API when it is no longer + * needed. + */ +extern Node * GraphBreadthFirstSearch(Graph *, Node, size_t *); + +/** + * Perform a depth-first search on the given graph, starting at the + * specified node. This function returns a list of nodes in the order + * they were touched. The size of the list is stored in the unsigned + * integer pointed to by the last argument. + * .P + * If an error occurs, NULL will be returned. Otherwise the returned + * pointer should be freed with the Memory API when it is no longer + * needed. + */ +extern Node *GraphDepthFirstSearch(Graph *, Node, size_t *); + +/** + * Perform a topological sort on the given graph. This function returns + * a list of nodes in topological ordering, though note that this is + * probably not the only topological ordering that exists for the + * graph. The size of the list is stored in the unsigned integer + * pointed to by the last argument. It should always be the number of + * nodes in the graph, but is provided for consistency and convenience. + * .P + * If an error occurs, NULL will be returned. Otherwise the returned + * pointer should be freed with the Memory API when it is no longer + * needed. + */ +extern Node *GraphTopologicalSort(Graph *, size_t *); + +/** + * Transpose the given graph, returning a brand new graph that is the + * result of the transposition. + */ +extern Graph * GraphTranspose(Graph *); + +#endif /* CYTOPLASM_GRAPH_H */ diff --git a/src/include/HashMap.h b/src/include/HashMap.h new file mode 100644 index 0000000..1359400 --- /dev/null +++ b/src/include/HashMap.h @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2022-2023 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_HASHMAP_H +#define CYTOPLASM_HASHMAP_H + +/*** + * @Nm HashMap + * @Nd A simple hash map implementation. + * @Dd October 11 2022 + * @Xr Array Queue + * + * This is the public interface for Cytoplasm's hash map + * implementation. This hash map is designed to be simple, + * well-documented, and generally readable and understandable, yet also + * performant enough to be useful, because it is used extensively + * throughout the project. + * .Pp + * Fundamentally, this is an entirely generic map implementation. It + * can be used for many general purposes, but it is designed only to + * implement the features Cytoplasm needs to be functional. One + * example of a Cytoplasm-specific feature is that keys cannot be + * arbitrary data; they are NULL-terminated C strings. + */ + +#include + +#include + +/** + * These functions operate on an opaque structure, which the caller + * has no knowledge about. + */ +typedef struct HashMap HashMap; + +/** + * Create a new hash map that is ready to be used with the rest of the + * functions defined here. + */ +extern HashMap * HashMapCreate(void); + +/** + * Free the specified hash map such that it becomes invalid and any + * future use results in undefined behavior. Note that this function + * does not free the values stored in the hash map, but since it stores + * the keys internally, it will free the keys. You should use + * .Fn HashMapIterate + * to free the values stored in this map appropriately before calling + * this function. + */ +extern void HashMapFree(HashMap *); + +/** + * Control 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 they are full, because that + * makes them perform very poorly. The maximum load value is a + * percentage of how full the hash map is, and it should be between + * 0 and 1, where 0 means that no elements will cause the map to be + * expanded, and 1 means that the hash map must be completely full + * before it is expanded. The default maximum load on a new hash map + * is 0.75, which should be good enough for most purposes, however, + * this function exists specifically so that the maximum load can be + * fine-tuned. + */ +extern void HashMapMaxLoadSet(HashMap *, float); + +/** + * Use a custom hashing function with the given hash map. New hash + * maps have a sane hashing function that should work okay for most + * use cases, but if you have a better hashing function, it can be + * specified this way. Do not change the hash function after keys have + * been added; doing so results in undefined behavior. Only set a new + * hash function immediately after constructing a new hash map, before + * anything has been added to it. + * .Pp + * The hash function takes a pointer to a C string, and is expected + * to return a fairly unique numerical hash value which will be + * converted into an array index. + */ +extern void +HashMapFunctionSet(HashMap *, unsigned long (*) (const char *)); + +/** + * Set the given string key to the given value. Note that the key is + * copied into the hash map's own memory space, but the value is not. + * It is the caller's job to ensure that the value pointer remains + * valid for the life of the hash map, and are freed when no longer + * needed. + */ +extern void * HashMapSet(HashMap *, char *, void *); + +/** + * Retrieve the value for the given key, or return NULL if no such + * key exists in the hash map. + */ +extern void * HashMapGet(HashMap *, const char *); + +/** + * Remove a value specified by the given key from the hash map, and + * return it to the caller to deal with. This function returns NULL + * if no such key exists. + */ +extern void * HashMapDelete(HashMap *, const char *); + +/** + * Iterate over all the keys and values of a hash map. This function + * works very similarly to + * .Xr getopt 3 , + * 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; it takes pointer pointers, and stores all + * necessary state inside the hash map itself. + * .Pp + * Note that this function is not thread-safe; two threads cannot be + * iterating over any given hash map at the same time, though they + * can each be iterating over different hash maps. + * .Pp + * 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 to go through in the hash map. If you are not iterating + * over the entire map in one go, and happen to break the loop, then + * the next time you attempt to iterate the hash map, you'll start + * somewhere in the middle, which is most likely not the intended + * behavior. Thus, it is always recommended to iterate over the entire + * hash map if you're going to use this function. + * .Pp + * Also note that the behavior of this function is undefined if + * insertions or deletions occur during the iteration. This + * functionality has not been tested, and will likely not work. + */ +extern int HashMapIterate(HashMap *, char **, void **); + +/** + * A reentrant version of + * .Fn HashMapIterate + * that allows the caller to overcome the flaws of that function by + * storing the cursor outside of the hash map structure itself. This + * allows multiple threads to iterate over the same hash map at the + * same time, and it allows the iteration to be halted midway through + * without causing any unintended side effects. + * .Pp + * The cursor should be initialized to 0 at the start of iteration. + */ +extern int +HashMapIterateReentrant(HashMap *, char **, void **, size_t *); + +/** + * Collect the string keys of a hash map and return them as an array. + * The returned array holds pointers to the strings stored in the + * hash map, so the strings should NOT be freed; it is sufficient to + * free the array itself. Likewise, once the hash map is freed, the + * array elements are invalid and the array should be freed. + */ +extern Array * HashMapKeys(HashMap *); + +/** + * Collect the values of a hash map and return them as an array. The + * returned array holds the same pointers to the values as the hash + * map. + */ +extern Array * HashMapValues(HashMap *); + +#endif /* CYTOPLASM_HASHMAP_H */ diff --git a/src/include/HeaderParser.h b/src/include/HeaderParser.h new file mode 100644 index 0000000..ffb3d0b --- /dev/null +++ b/src/include/HeaderParser.h @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2022-2023 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_HEADERPARSER_H +#define CYTOPLASM_HEADERPARSER_H + +/*** + * @Nm HeaderParser + * @Nd Parse simple C header files. + * @Dd April 29 2023 + * + * .Nm + * is an extremely simple parser that lacks most of the functionality + * one would expect from a C code parser. It simply maps a stream + * of tokens into a few categories, parsing major ``milestones'' in + * a header, without actually understanding any of the details. + * .Pp + * This exists because it is used to generate man pages from headers. + * See + * .Xr hdoc 1 + * for example usage of this parser. + */ + +#include +#include + +#define HEADER_EXPR_MAX 4096 + +/** + * Headers are parsed as expressions. These are the expressions that + * this parser recognizes. + */ +typedef enum HeaderExprType +{ + HP_COMMENT, + HP_PREPROCESSOR_DIRECTIVE, + HP_TYPEDEF, + HP_DECLARATION, + HP_GLOBAL, + HP_UNKNOWN, + HP_SYNTAX_ERROR, + HP_PARSE_ERROR, + HP_EOF +} HeaderExprType; + +/** + * A representation of a function declaration. + */ +typedef struct HeaderDeclaration +{ + char returnType[64]; + char name[32]; /* Enforced by ANSI C */ + Array *args; +} HeaderDeclaration; + +/** + * A global variable declaration. The type must be of the same size + * as the function declaration's return type due to the way parsing + * them is implemented. + */ +typedef struct HeaderGlobal +{ + char type[64]; + char name[HEADER_EXPR_MAX - 64]; +} HeaderGlobal; + +/** + * A representation of a single header expression. Note that that state + * structure is entirely internally managed, so it should not be + * accessed or manipulated by functions outside the functions defined + * here. + * .Pp + * The type field should be used to determine which field in the data + * union is valid. + */ +typedef struct HeaderExpr +{ + HeaderExprType type; + union + { + char text[HEADER_EXPR_MAX]; + HeaderDeclaration declaration; + HeaderGlobal global; + struct + { + int lineNo; + char *msg; + } error; + } data; + + struct + { + Stream *stream; + int lineNo; + } state; +} HeaderExpr; + +/** + * Parse the next expression into the given header expression structure. + * To parse an entire C header, this function should be called in a + * loop until the type of the expression is HP_EOF. + */ +extern void HeaderParse(Stream *, HeaderExpr *); + +#endif /* CYTOPLASM_HEADERPARSER_H */ diff --git a/src/include/Http.h b/src/include/Http.h new file mode 100644 index 0000000..d2de020 --- /dev/null +++ b/src/include/Http.h @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2022-2023 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_HTTP_H +#define CYTOPLASM_HTTP_H + +/*** + * @Nm Http + * @Nd Encode and decode various parts of the HTTP protocol. + * @Dd March 12 2023 + * @Xr HttpClient HttpServer HashMap Queue Memory + * + * .Nm + * is a collection of utility functions and type definitions that are + * useful for dealing with HTTP. HTTP is not a complex protocol, but + * this API makes it a lot easier to work with. + * .Pp + * Note that this API doesn't target any particular HTTP version, but + * it is currently used with HTTP 1.0 clients and servers, and + * therefore may be lacking functionality added in later HTTP versions. + */ + +#include + +#include +#include + +#define HTTP_FLAG_NONE 0 +#define HTTP_FLAG_TLS (1 << 0) + +/** + * The request methods defined by the HTTP standard. These numeric + * constants should be preferred to strings when building HTTP APIs + * because they are more efficient. + */ +typedef enum HttpRequestMethod +{ + HTTP_METHOD_UNKNOWN, + HTTP_GET, + HTTP_HEAD, + HTTP_POST, + HTTP_PUT, + HTTP_DELETE, + HTTP_CONNECT, + HTTP_OPTIONS, + HTTP_TRACE, + HTTP_PATCH +} HttpRequestMethod; + +/** + * An enumeration that corresponds to the actual integer values of the + * valid HTTP response codes. + */ +typedef enum HttpStatus +{ + HTTP_STATUS_UNKNOWN = 0, + + /* Informational responses */ + HTTP_CONTINUE = 100, + HTTP_SWITCHING_PROTOCOLS = 101, + HTTP_EARLY_HINTS = 103, + + /* Successful responses */ + HTTP_OK = 200, + HTTP_CREATED = 201, + HTTP_ACCEPTED = 202, + HTTP_NON_AUTHORITATIVE_INFORMATION = 203, + HTTP_NO_CONTENT = 204, + HTTP_RESET_CONTENT = 205, + HTTP_PARTIAL_CONTENT = 206, + + /* Redirection messages */ + HTTP_MULTIPLE_CHOICES = 300, + HTTP_MOVED_PERMANENTLY = 301, + HTTP_FOUND = 302, + HTTP_SEE_OTHER = 303, + HTTP_NOT_MODIFIED = 304, + HTTP_TEMPORARY_REDIRECT = 307, + HTTP_PERMANENT_REDIRECT = 308, + + /* Client error messages */ + HTTP_BAD_REQUEST = 400, + HTTP_UNAUTHORIZED = 401, + HTTP_FORBIDDEN = 403, + HTTP_NOT_FOUND = 404, + HTTP_METHOD_NOT_ALLOWED = 405, + HTTP_NOT_ACCEPTABLE = 406, + HTTP_PROXY_AUTH_REQUIRED = 407, + HTTP_REQUEST_TIMEOUT = 408, + HTTP_CONFLICT = 409, + HTTP_GONE = 410, + HTTP_LENGTH_REQUIRED = 411, + HTTP_PRECONDITION_FAILED = 412, + HTTP_PAYLOAD_TOO_LARGE = 413, + HTTP_URI_TOO_LONG = 414, + HTTP_UNSUPPORTED_MEDIA_TYPE = 415, + HTTP_RANGE_NOT_SATISFIABLE = 416, + HTTP_EXPECTATION_FAILED = 417, + HTTP_TEAPOT = 418, + HTTP_UPGRADE_REQUIRED = 426, + HTTP_PRECONDITION_REQUIRED = 428, + HTTP_TOO_MANY_REQUESTS = 429, + HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE = 431, + HTTP_UNAVAILABLE_FOR_LEGAL_REASONS = 451, + + /* Server error responses */ + HTTP_INTERNAL_SERVER_ERROR = 500, + HTTP_NOT_IMPLEMENTED = 501, + HTTP_BAD_GATEWAY = 502, + HTTP_SERVICE_UNAVAILABLE = 503, + HTTP_GATEWAY_TIMEOUT = 504, + HTTP_VERSION_NOT_SUPPORTED = 505, + HTTP_VARIANT_ALSO_NEGOTIATES = 506, + HTTP_NOT_EXTENDED = 510, + HTTP_NETWORK_AUTH_REQUIRED = 511 +} HttpStatus; + +/** + * Convert an HTTP status enumeration value into a string description + * of the status, which is to be used in server response to a client, + * or a client response to a user. For example, calling + * .Fn HttpStatusToString "HTTP_GATEWAY_TIMEOUT" + * (or + * .Fn HttpStatusToString "504" ) + * produces the string "Gateway Timeout". Note that the returned + * pointers point to static space, so their manipulation is forbidden. + */ +extern const char * HttpStatusToString(const HttpStatus); + +/** + * Convert a string into a numeric code that can be used throughout + * the code of a program in an efficient manner. See the definition + * of HttpRequestMethod. This function does case-sensitive matching, + * and does not trim or otherwise process the input string. + */ +extern HttpRequestMethod HttpRequestMethodFromString(const char *); + +/** + * Convert a numeric code as defined by HttpRequestMethod into a + * string that can be sent to a server. Note that the returned pointers + * point to static space, so their manipulation is forbidden. + */ +extern const char * HttpRequestMethodToString(const HttpRequestMethod); + +/** + * Encode a C string such that it can safely appear in a URL by + * performing the necessary percent escaping. A new string on the + * heap is returned. It should be freed with + * .Fn Free , + * defined in the + * .Xr Memory 3 + * API. + */ +extern char * HttpUrlEncode(char *); + +/** + * Decode a percent-encoded string into a C string, ignoring encoded + * null characters entirely, because those would do nothing but cause + * problems. + */ +extern char * HttpUrlDecode(char *); + +/** + * Decode an encoded parameter string in the form of + * ``key=val&key2=val2'' into a hash map whose values are C strings. + * This function properly decodes keys and values using the functions + * defined above. + */ +extern HashMap * HttpParamDecode(char *); + +/** + * Encode a hash map whose values are strings as an HTTP parameter + * string suitable for GET or POST requests. + */ +extern char * HttpParamEncode(HashMap *); + +/** + * Read HTTP headers from a stream and return a hash map whose values + * are strings. All keys are lowercased to make querying them + * consistent and not dependent on the case that was read from the + * stream. This is useful for both client and server code, since the + * headers are in the same format. This function should be used after + * parsing the HTTP status line, because it does not parse that line. + * It will stop when it encounters the first blank line, which + * indicates that the body is beginning. After this function completes, + * the body may be immediately read from the stream without any + * additional processing. + */ +extern HashMap * HttpParseHeaders(Stream *); + +#endif diff --git a/src/include/HttpClient.h b/src/include/HttpClient.h new file mode 100644 index 0000000..f757041 --- /dev/null +++ b/src/include/HttpClient.h @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2022-2023 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_HTTPCLIENT_H +#define CYTOPLASM_HTTPCLIENT_H + +/*** + * @Nm HttpClient + * @Nd Extremely simple HTTP client. + * @Dd April 29 2023 + * @Xr Http HttpServer Tls + * + * .Nm + * HttpClient + * builds on the HTTP API to provide a simple yet functional HTTP + * client. It aims at being easy to use and minimal, yet also + * efficient. + */ + +#include + +#include +#include + +/** + * A server response is represented by a client context. It is + * opaque, so the functions defined in this API should be used to + * fetch data from and manipulate it. + */ +typedef struct HttpClientContext HttpClientContext; + +/** + * Make an HTTP request. This function takes the request method, + * any flags defined in the HTTP API, the port, hostname, and path, + * all in that order. It returns NULL if there was an error making + * the request. Otherwise it returns a client context. Note that this + * function does not actually send any data, it simply makes the + * connection. Use + * .Fn HttpRequestHeader + * to add headers to the request. Then, send headers with + * .Fn HttpRequestSendHeaders . + * Finally, the request body, if any, can be written to the output + * stream, and then the request can be fully sent using + * .Fn HttpRequestSend . + */ +extern HttpClientContext * + HttpRequest(HttpRequestMethod, int, unsigned short, char *, char *); + +/** + * Set a request header to send to the server when making the + * request. + */ +extern void HttpRequestHeader(HttpClientContext *, char *, char *); + +/** + * Send the request headers to the server. This must be called before + * the request body can be written or a response body can be read. + */ +extern void HttpRequestSendHeaders(HttpClientContext *); + +/** + * Flush the request stream to the server. This function should be + * called before the response body is read. + */ +extern HttpStatus HttpRequestSend(HttpClientContext *); + +/** + * Get the headers returned by the server. + */ +extern HashMap * HttpResponseHeaders(HttpClientContext *); + +/** + * Get the stream used to write the request body and read the + * response body. + */ +extern Stream * HttpClientStream(HttpClientContext *); + +/** + * Free all memory associated with the client context. This closes the + * connection, if it was still open. + */ +extern void HttpClientContextFree(HttpClientContext *); + +#endif /* CYTOPLASM_HTTPCLIENT_H */ diff --git a/src/include/HttpRouter.h b/src/include/HttpRouter.h new file mode 100644 index 0000000..6816178 --- /dev/null +++ b/src/include/HttpRouter.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2022-2023 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_HTTPROUTER_H +#define CYTOPLASM_HTTPROUTER_H + +/*** + * @Nm HttpRouter + * @Nd Simple HTTP request router with regular expression support. + * @Dd April 29 2023 + * @Xr HttpServer Http + * + * .Nm + * provides a simple mechanism for assigning functions to an HTTP + * request path. It is a simple tree data structure that parses the + * registered request paths and maps functions onto each part of the + * path. Then, requests can be easily routed to their appropriate + * handler functions. + */ + +#include + +/** + * The router structure is opaque and thus managed entirely by the + * functions defined in this API. + */ +typedef struct HttpRouter HttpRouter; + +/** + * A function written to handle an HTTP request takes an array + * consisting of the matched path parts in the order they appear in + * the path, and a pointer to caller-provided arguments, if any. + * It returns a pointer that the caller is assumed to know how to + * handle. + */ +typedef void *(HttpRouteFunc) (Array *, void *); + +/** + * Create a new empty routing tree. + */ +extern HttpRouter * HttpRouterCreate(void); + +/** + * Free all the memory associated with the given routing tree. + */ +extern void HttpRouterFree(HttpRouter *); + +/** + * Register the specified route function to be executed upon requests + * for the specified HTTP path. The path is parsed by splitting at + * each path separator. Each part of the path is a regular expression + * that matches the entire path part. A regular expression cannot + * match more than one path part. This allows for paths like + * .Pa /some/path/(.*)/parts + * to work as one would expect. + */ +extern int HttpRouterAdd(HttpRouter *, char *, HttpRouteFunc *); + +/** + * Route the specified request path using the specified routing + * tree. This function will parse the path and match it to the + * appropriate route handler function. The return value is a boolean + * value that indicates whether or not an appropriate route function + * was found. If an appropriate function was found, then the void + * pointer is passed to it as arguments that it is expected to know + * how to handle, and the pointer to a void pointer is where the + * route function's response will be placed. + */ +extern int HttpRouterRoute(HttpRouter *, char *, void *, void **); + +#endif /* CYTOPLASM_HTTPROUTER_H */ diff --git a/src/include/HttpServer.h b/src/include/HttpServer.h new file mode 100644 index 0000000..f8e449a --- /dev/null +++ b/src/include/HttpServer.h @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2022-2023 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_HTTPSERVER_H +#define CYTOPLASM_HTTPSERVER_H + +/*** + * @Nm HttpServer + * @Nd Extremely simple HTTP server. + * @Dd December 13 2022 + * @Xr Http HttpClient + * + * .Nm + * builds on the + * .Xr Http 3 + * API, and provides a very simple, yet very functional API for + * creating an HTTP server. It aims at being easy to use and minimal, + * yet also efficient. It uses non-blocking I/O, is fully + * multi-threaded, and is very configurable. It can be set up in just + * two function calls and minimal supporting code. + * .Pp + * This API should be familar to those that have dealt with the HTTP + * server libraries of other programming languages, particularly Java. + * In fact, much of the terminology used in this API came from Java, + * and you'll notice that the way responses are sent and received very + * closely resembles Java. + */ + +#include + +#include + +#include +#include + +/** + * The functions on this API operate on an opaque structure. + */ +typedef struct HttpServer HttpServer; + +/** + * Each request receives a context structure. It is opaque, so the + * functions defined in this API should be used to fetch data from + * it. These functions allow the handler to figure out the context of + * the request, which includes the path requested, any parameters, + * and the headers and method used to make the request. The context + * also provides the means by which the handler responds to the + * request, allowing it to set the status code, headers, and body. + */ +typedef struct HttpServerContext HttpServerContext; + +/** + * The request handler function is executed when an HTTP request is + * received. It takes a request context, and a pointer as specified + * in the server configuration. + */ +typedef void (HttpHandler) (HttpServerContext *, void *); + +/** + * The number of arguments to + * .Fn HttpServerCreate + * has grown so large that arguments are now stuffed into a + * configuration structure, which is in turn passed to + * .Fn HttpServerCreate . + * This configuration is copied by value into the internal + * structures of the server. It is copied with very minimal + * validation, so ensure that all values are sensible. It may + * make sense to use + * .Fn memset + * to zero out everything in here before assigning values. + */ +typedef struct HttpServerConfig +{ + unsigned short port; + unsigned int threads; + unsigned int maxConnections; + + int flags; /* Http(3) flags */ + char *tlsCert; /* File path */ + char *tlsKey; /* File path */ + + HttpHandler *handler; + void *handlerArgs; +} HttpServerConfig; + +/** + * Create a new HTTP server using the specified configuration. + * This will set up all internal structures used by the server, + * and bind the socket and start listening for connections. However, + * it will not start accepting connections. + */ +extern HttpServer * HttpServerCreate(HttpServerConfig *); + +/** + * Retrieve the configuration that was used to instantiate the given + * server. Note that this configuration is not necessarily the exact + * one that was provided; even though its values are the same, it + * should be treated as an entirely separate configuration with no + * connection to the original. + */ +extern HttpServerConfig * HttpServerConfigGet(HttpServer *); + +/** + * Free the resources associated with the given HTTP server. Note that + * the server can only be freed after it has been stopped. Calling this + * function while the server is still running results in undefined + * behavior. + */ +extern void HttpServerFree(HttpServer *); + +/** + * Attempt to start the HTTP server, and return immediately with the + * status. This API is fully multi-threaded and asynchronous, so the + * caller can continue working while the HTTP server is running in a + * separate thread and managing a pool of threads to handle responses. + */ +extern int HttpServerStart(HttpServer *); + +/** + * Typically, at some point after calling + * .Fn HttpServerStart , + * the program will have no more work to do, so it will want to wait + * for the HTTP server to finish. This is accomplished via this + * function, which joins the HTTP worker thread to the calling thread, + * pausing the calling thread until the HTTP server has stopped. + */ +extern void HttpServerJoin(HttpServer *); + +/** + * Stop the HTTP server. Only the execution of this function will + * cause the proper shutdown of the HTTP server. If the main program + * is joined to the HTTP thread, then either another thread or a + * signal handler will have to stop the server using this function. + * The typical use case is to install a signal handler that executes + * this function on a global HTTP server. + */ +extern void HttpServerStop(HttpServer *); + +/** + * Get the request headers for the request represented by the given + * context. The data in the returned hash map should be treated as + * read only and should not be freed; it is managed entirely by the + * server. + */ +extern HashMap * HttpRequestHeaders(HttpServerContext *); + +/** + * Get the request method used to make the request represented by + * the given context. + */ +extern HttpRequestMethod HttpRequestMethodGet(HttpServerContext *); + +/** + * Get the request path for the request represented by the given + * context. The return value of this function should be treated as + * read-only, and should not be freed; it is managed entirely by the + * server. + */ +extern char * HttpRequestPath(HttpServerContext *); + +/** + * Retrieve the parsed GET parameters for the request represented by + * the given context. The returned hash map should be treated as + * read-only, and should not be freed; it is managed entirely by the + * server. + */ +extern HashMap * HttpRequestParams(HttpServerContext *); + +/** + * Set a response header to return to the client. The old value for + * the given header is returned, if any, otherwise NULL is returned. + */ +extern char * HttpResponseHeader(HttpServerContext *, char *, char *); + +/** + * Set the response status to return to the client. + */ +extern void HttpResponseStatus(HttpServerContext *, HttpStatus); + +/** + * Get the current response status that will be sent to the client + * making the request represented by the given context. + */ +extern HttpStatus HttpResponseStatusGet(HttpServerContext *); + +/** + * Send the response headers to the client that made the request + * represented by the specified context. This function must be called + * before the response body can be written, otherwise a malformed + * response will be sent. + */ +extern void HttpSendHeaders(HttpServerContext *); + +/** + * Get a stream that is both readable and writable. Reading from the + * stream reads the request body that the client sent, if there is one. + * Note that the rquest headers have already been read, so the stream + * is correctly positioned at the beginning of the body of the request. + * .Fn HttpSendHeaders + * must be called before the stream is written, otherwise a malformed + * HTTP response will be sent. An HTTP handler should properly set all + * the headers it itends to send, send those headers, and then write + * the response body to this stream. + * .Pp + * Note that the stream does not need to be closed by the HTTP + * handler; in fact doing so results in undefined behavior. The stream + * is managed entirely by the server itself, so it will close it when + * necessary. This allows the underlying protocol to differ: for + * instance, an HTTP/1.1 connection may stay for multiple requests and + * responses. + */ +extern Stream * HttpServerStream(HttpServerContext *); + +#endif /* CYTOPLASM_HTTPSERVER_H */ diff --git a/src/include/Int.h b/src/include/Int.h new file mode 100644 index 0000000..7933a69 --- /dev/null +++ b/src/include/Int.h @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2022-2023 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_INT_H +#define CYTOPLASM_INT_H + +/*** + * @Nm Int + * @Nd Fixed-width integer types. + * @Dd April 27 2023 + * + * This header provides cross-platform, fixed-width integer types. + * Specifically, it uses preprocessor magic to define the following + * types: + * .Bl -bullet -offset indent + * .It + * Int8 and UInt8 + * .It + * Int16 and UInt16 + * .It + * Int32 and UInt32 + * .El + * .Pp + * Note that there is no 64-bit integer type, because the ANSI C + * standard makes no guarantee that such a type will exist, even + * though it does on most platforms. + * .Pp + * The reason Cytoplasm provides its own header for this is + * because ANSI C does not define fixed-width types, and while it + * should be safe to rely on C99 fixed-width types in most cases, + * there may be cases where even that is not possible. + * + * @ignore-typedefs + */ + +#include + +#define BIT32_MAX 4294967295UL +#define BIT16_MAX 65535UL +#define BIT8_MAX 255UL + +#ifndef UCHAR_MAX +#error Size of char data type is unknown. Define UCHAR_MAX. +#endif + +#ifndef USHRT_MAX +#error Size of short data type is unknown. Define USHRT_MAX. +#endif + +#ifndef UINT_MAX +#error Size of int data type is unknown. Define UINT_MAX. +#endif + +#ifndef ULONG_MAX +#error Size of long data type is unknown. Define ULONG_MAX. +#endif + +#if UCHAR_MAX == BIT8_MAX +typedef signed char Int8; +typedef unsigned char UInt8; + +#else +#error Unable to determine suitable data type for 8-bit integers. +#endif + +#if UINT_MAX == BIT16_MAX +typedef signed int Int16; +typedef unsigned int UInt16; + +#elif USHRT_MAX == BIT16_MAX +typedef signed short Int16; +typedef unsigned short UInt16; + +#elif UCHAR_MAX == BIT16_MAX +typedef signed char Int16; +typedef unsigned char UInt16; + +#else +#error Unable to determine suitable data type for 16-bit integers. +#endif + +#if ULONG_MAX == BIT32_MAX +typedef signed long Int32; +typedef unsigned long UInt32; + +#elif UINT_MAX == BIT32_MAX +typedef signed int Int32; +typedef unsigned int UInt32; + +#elif USHRT_MAX == BIT32_MAX +typedef signed short Int32; +typedef unsigned short UInt32; + +#elif UCHAR_MAX == BIT32_MAX +typedef signed char Int32; +typedef unsigned char UInt32; + +#else +#error Unable to determine suitable data type for 32-bit integers. +#endif + +#endif /* CYTOPLASM_INT_H */ diff --git a/src/include/Int64.h b/src/include/Int64.h new file mode 100644 index 0000000..08083c1 --- /dev/null +++ b/src/include/Int64.h @@ -0,0 +1,252 @@ +/* + * Copyright (C) 2022-2023 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_INT64_H +#define CYTOPLASM_INT64_H + +/*** + * @Nm Int64 + * @Nd Fixed-width 64 bit integers. + * @Dd August 11, 2023 + * + * .Pp + * ANSI C89 (or C99 for that matter) provides no required mechanism + * for 64 bit integers. Nevertheless, many compilers provide them as + * extensions. However, since it is not a gaurantee, and to be fully + * standards-compliant and thus portable, a platform-agnostic interface + * is required. This header provides such an interface. If the platform + * has a 64 bit integer type, that is used, and native operations are + * performed by C preprocessor macro expansion. Otherwise, a + * compatibility layer is provided, which implements 64-bit + * arithmetic on an array of 2 32-bit numbers which are provided by + * .Xr Int 3 . + * .Pp + * Note that 64-bit emulation is certainly not as performant as using + * native 64-bit operations, so whenever possible, the native + * operations should be preferred. However, since C provides no required + * 64 bit integer on 32-bit and less platforms, this API can be used as + * a "good enough" fallback mechanism. + * .Pp + * Also note that this implementation, both in the native and + * non-native forms, makes some assumptions: + * .Bl -bullet -width Ds + * .It + * When a cast from a larger integer to a smaller integer is performed, + * the upper bits are truncated, not the lower bits. + * .It + * Negative numbers are represented in memory and in registers in two's + * compliment form. + * .El + * .Pp + * This API may provide unexpected output if these assumptions are + * false for a given platform. + * + * @ignore-typedefs + */ + +#include +#include + +#include + +#ifndef INT64_FORCE_EMULATED + +#define BIT64_MAX 18446744073709551615UL + +#if UINT_MAX == BIT64_MAX +typedef signed int Int64; + +#define INT64_NATIVE + +#elif ULONG_MAX == BIT64_MAX +typedef signed long Int64; + +#define INT64_NATIVE + +#endif + +#endif /* ifndef INT64_FORCE_EMULATED */ + +#ifdef INT64_NATIVE + +#define Int64Create(high, low) ((Int64) (((UInt64) (high) << 32) | (low))) +#define Int64Neg(x) (-(x)) + +#define Int64Low(a) ((UInt32) (a)) +#define Int64High(a) ((UInt32) ((a) >> 32)) + +#define Int64Add(a, b) ((a) + (b)) +#define Int64Sub(a, b) ((a) - (b)) +#define Int64Mul(a, b) ((a) * (b)) +#define Int64Div(a, b) ((a) / (b)) +#define Int64Rem(a, b) ((a) % (b)) + +#define Int64Sll(a, b) ((a) << (b)) +#define Int64Sra(a, b) ((a) >> (b)) + +#define Int64And(a, b) ((a) & (b)) +#define Int64Or(a, b) ((a) | (b)) +#define Int64Xor(a, b) ((a) ^ (b)) +#define Int64Not(a) (~(a)) + +#define Int64Eq(a, b) ((a) == (b)) +#define Int64Lt(a, b) ((a) < (b)) +#define Int64Gt(a, b) ((a) > (b)) + +#define Int64Neq(a, b) ((a) != (b)) +#define Int64Leq(a, b) ((a) <= (b)) +#define Int64Geq(a, b) ((a) >= (b)) + +#else + +#define Int64Neg(x) (Int64Add(Int64Not(x), Int64Create(0, 1))) + +/** + * The internal bit representation of a signed integer is identical + * to an unsigned integer, the difference is in the algorithms and + * the way the bits are interpreted. + */ +typedef UInt64 Int64; + +/** + * Create a new signed 64 bit integer using the given high and low + * bits. + */ +extern Int64 Int64Create(UInt32, UInt32); + +/** + * Add two signed 64 bit integers together. + */ +extern Int64 Int64Add(Int64, Int64); + +/** + * Subtract the second 64 bit integer from the first. + */ +extern Int64 Int64Sub(Int64, Int64); + +/** + * Multiply two 64 bit integers together. The non-native version of + * this function uses the Russian Peasant method of multiplication, + * which should afford more performance than a naive multiplication by + * addition, but it is still rather slow and depends on the size of + * the integers being multiplied. + */ +extern Int64 Int64Mul(Int64, Int64); + +/** + * Divide the first 64 bit integer by the second and return the + * quotient. The non-native version of this function uses naive binary + * long division, which is slow, but gauranteed to finish in constant + * time. + */ +extern Int64 Int64Div(Int64, Int64); + +/** + * Divide the first 64 bit integer by the second and return the + * remainder. The non-native version of this function uses naive binary + * long division, which is slow, but gauranteed to finish in constant + * time. + */ +extern Int64 Int64Rem(Int64, Int64); + +/** + * Perform a left logical bit shift of a 64 bit integer. The second + * parameter is how many places to shift, and is declared as a regular + * integer because anything more than 64 does not make sense. + */ +extern Int64 Int64Sll(Int64, int); + +/** + * Perform a right arithmetic bit shift of a 64 bit integer. The second + * parameter is how many places to shift, and is declared as a regular + * integer because anything more than 64 does not make sense. + * .Pp + * Note that on platforms that use the native 64-bit implementation, + * this is technically implementation-defined, and may in fact be a + * logical shift instead of an arithmetic shift. Note that typically + * this operation is not performed on signed integers. + */ +extern Int64 Int64Sra(Int64, int); + +/** + * Perform a bitwise AND (&) of the provided 64 bit integers. + */ +extern Int64 Int64And(Int64, Int64); + +/** + * Perform a bitwise OR (|) of the provided 64 bit integers. + */ +extern Int64 Int64Or(Int64, Int64); + +/** + * Perform a bitwise XOR (^) of the provided 64 bit integers. + */ +extern Int64 Int64Xor(Int64, Int64); + +/** + * Perform a bitwise NOT (~) of the provided 64 bit integer. + */ +extern Int64 Int64Not(Int64); + +/** + * Perform a comparison of the provided 64 bit integers and return a C + * boolean that is true if and only if they are equal. + */ +extern int Int64Eq(Int64, Int64); + +/** + * Perform a comparison of the provided 64 bit integers and return a C + * boolean that is true if and only if the second operand is strictly + * less than the first. + */ +extern int Int64Lt(Int64, Int64); + +/** + * Perform a comparison of the provided 64 bit integers and return a C + * boolean that is true if and only if the second operand is strictly + * greater than the first. + */ +extern int Int64Gt(Int64, Int64); + +#define Int64Low(a) ((a).i[0]) +#define Int64High(a) ((a).i[1]) + +#define Int64Neq(a, b) (!Int64Eq(a, b)) +#define Int64Leq(a, b) (Int64Eq(a, b) || Int64Lt(a, b)) +#define Int64Geq(a, b) (Int64Eq(a, b) || Int64Gt(a, b)) + +#endif + +#define INT64_STRBUF 65 /* Base 2 representation with '\0' */ + +/** + * Convert a 64 bit integer to a string in an arbitrary base + * representation specified by the second parameter, using the provided + * buffer and length specified by the third and fourth parameters. To + * guarantee that the string will fit in the buffer, allocate it of + * size INT64_STRBUF or larger. Note that a buffer size smaller than + * INT64_STRBUF will invoke undefined behavior. + */ +extern size_t Int64Str(Int64, int, char *, size_t); + +#endif /* CYTOPLASM_INT64_H */ diff --git a/src/include/Io.h b/src/include/Io.h new file mode 100644 index 0000000..06afb77 --- /dev/null +++ b/src/include/Io.h @@ -0,0 +1,222 @@ +/* + * Copyright (C) 2022-2023 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_IO_H +#define CYTOPLASM_IO_H + +/*** + * @Nm Io + * @Nd Source/sink-agnostic I/O for implementing custom streams. + * @Dd April 29 2023 + * @Xr Stream Tls + * + * Many systems provide platform-specific means of implementing custom + * streams using file pointers. However, POSIX does not define a way + * of programmatically creating custom streams. + * .Nm + * therefore fills this gap in POSIX by mimicking all of the + * functionality of these platform-specific functions, but in pure + * POSIX C. It defines a number of callback funtions to be executed + * in place of the standard POSIX I/O functions, which are used to + * implement arbitrary streams that may not be to a file or socket. + * Additionally, streams can now be pipelined; the sink of one stream + * may be the source of another lower-level stream. Additionally, all + * streams, regardless of their source or sink, share the same API, so + * streams can be handled in a much more generic manner. This allows + * the HTTP client and server libraries to seemlessly support TLS and + * plain connections without having to handle each separately. + * .Pp + * .Nm + * was heavily inspired by GNU's + * .Fn fopencookie + * and BSD's + * .Fn funopen . + * It aims to combine the best of both of these functions into a single + * API that is intuitive and easy to use. + */ + +#include +#include +#include +#include + +#ifndef IO_BUFFER +#define IO_BUFFER 4096 +#endif + +/** + * An opaque structure analogous to a POSIX file descriptor. + */ +typedef struct Io Io; + +/** + * Read input from the source of a stream. This function should + * attempt to read the specified number of bytes of data from the + * given cookie into the given buffer. It should behave identically + * to the POSIX + * .Xr read 2 + * system call, except instead of using an integer descriptor as the + * first parameter, a pointer to an implementation-defined cookie + * stores any information the function needs to read from the source. + */ +typedef ssize_t (IoReadFunc) (void *, void *, size_t); + +/** + * Write output to a sink. This function should attempt to write the + * specified number of bytes of data from the given buffer into the + * stream described by the given cookie. It should behave identically + * to the POSIX + * .Xr write 2 + * system call, except instead of using an integer descriptor as the + * first parameter, a pointer to an implementation-defined cookie + * stores any information the function needs to write to the sink. + */ +typedef ssize_t (IoWriteFunc) (void *, void *, size_t); + +/** + * Repositions the offset of the stream described by the specified + * cookie. This function should behave identically to the POSIX + * .Xr lseek 2 + * system call, except instead of using an integer descriptor as the + * first parameter, a pointer to an implementation-defined cookie + * stores any information the function needs to seek the stream. + */ +typedef off_t (IoSeekFunc) (void *, off_t, int); + +/** + * Close the given stream, making future reads or writes result in + * undefined behavior. This function should also free all memory + * associated with the cookie. It should behave identically to the + * .Xr close 2 + * system call, except instead of using an integer descriptor for the + * parameter, a pointer to an implementation-defined cookie stores any + * information the function needs to close the stream. + */ +typedef int (IoCloseFunc) (void *); + +/** + * A simple mechanism for grouping together a set of stream functions, + * to be passed to + * .Fn IoCreate . + */ +typedef struct IoFunctions +{ + IoReadFunc *read; + IoWriteFunc *write; + IoSeekFunc *seek; + IoCloseFunc *close; +} IoFunctions; + +/** + * Create a new stream using the specified cookie and the specified + * I/O functions. + */ +extern Io * IoCreate(void *, IoFunctions); + +/** + * Read the specified number of bytes from the specified stream into + * the specified buffer. This calls the stream's underlying IoReadFunc, + * which should behave identically to the POSIX + * .Xr read 2 + * system call. + */ +extern ssize_t IoRead(Io *, void *, size_t); + +/** + * Write the specified number of bytes from the specified stream into + * the specified buffer. This calls the stream's underlying + * IoWriteFunc, which should behave identically to the POSIX + * .Xr write 2 + * system call. + */ +extern ssize_t IoWrite(Io *, void *, size_t); + +/** + * Seek the specified stream using the specified offset and whence + * value. This calls the stream's underlying IoSeekFunc, which should + * behave identically to the POSIX + * .Xr lseek 2 + * system call. + */ +extern off_t IoSeek(Io *, off_t, int); + +/** + * Close the specified stream. This calls the stream's underlying + * IoCloseFunc, which should behave identically to the POSIX + * .Xr close 2 + * system call. + */ +extern int IoClose(Io *); + +/** + * Print a formatted string to the given stream. This is a + * re-implementation of the standard library function + * .Xr vfprintf 3 , + * and behaves identically. + */ +extern int IoVprintf(Io *, const char *, va_list); + +/** + * Print a formatted string to the given stream. This is a + * re-implementation of the standard library function + * .Xr fprintf 3 , + * and behaves identically. + */ +extern int IoPrintf(Io *, const char *,...); + +/** + * Read all the bytes from the first stream and write them to the + * second stream. Neither stream is closed upon the completion of this + * function. This can be used for quick and convenient buffered + * copying of data from one stream into another. + */ +extern ssize_t IoCopy(Io *, Io *); + +/** + * Wrap a POSIX file descriptor to take advantage of this API. The + * common use case for this function is when a regular file descriptor + * needs to be accessed by an application that uses this API to also + * access non-POSIX streams. + */ +extern Io * IoFd(int); + +/** + * Open or create a file for reading or writing. The specified file + * name is opened for reading or writing as specified by the given + * flags and mode. This function is a simple convenience wrapper around + * the POSIX + * .Xr open 2 + * system call that passes the opened file descriptor into + * .Fn IoFd . + */ +extern Io * IoOpen(const char *, int, mode_t); + +/** + * Wrap a standard C file pointer to take advantage of this API. The + * common use case for this function is when a regular C file pointer + * needs to be accessed by an application that uses this API to also + * access custom streams. + */ +extern Io * IoFile(FILE *); + +#endif /* CYTOPLASM_IO_H */ diff --git a/src/include/Json.h b/src/include/Json.h new file mode 100644 index 0000000..4185260 --- /dev/null +++ b/src/include/Json.h @@ -0,0 +1,323 @@ +/* + * Copyright (C) 2022-2023 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 +#include +#include +#include + +#include +#include + +#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(Int64); + +/** + * 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 Int64 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(int); + +/** + * 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 int 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 int 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 int 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 int 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,...); + +#endif /* CYTOPLASM_JSON_H */ diff --git a/src/include/Log.h b/src/include/Log.h new file mode 100644 index 0000000..d0300ca --- /dev/null +++ b/src/include/Log.h @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2022-2023 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_LOG_H +#define CYTOPLASM_LOG_H + +/*** + * @Nm Log + * @Nd A simple logging framework for logging to multiple destinations. + * @Dd April 27 2023 + * @Xr Stream + * + * .Nm + * is a simple C logging library that allows for colorful outputs, + * timestamps, and custom log levels. It also features the ability to + * have multiple logs open at one time, although Cytoplasm primarily + * utilizes the global log. All logs are thread safe. + */ + +#include +#include +#include + +#include + +#define LOG_FLAG_COLOR (1 << 0) +#define LOG_FLAG_SYSLOG (1 << 1) + +/** + * A log is defined as a configuration that describes the properties + * of the log. This opaque structure can be manipulated by the + * functions defined in this API. + */ +typedef struct LogConfig LogConfig; + +/** + * Create a new log configuration with sane defaults that can be used + * immediately with the logging functions. + */ +extern LogConfig * LogConfigCreate(void); + +/** + * Get the global log configuration, creating a new one with + * .Fn LogConfigCreate + * if necessary. + */ +extern LogConfig * LogConfigGlobal(void); + +/** + * Free the given log configuration. Note that this does not close the + * underlying stream associated with the log, if there is one. Also + * note that to avoid memory leaks, the global log configuration must + * also be freed, but it cannot be used after it is freed. + */ +extern void LogConfigFree(LogConfig *); + +/** + * Set the current log level on the specified log configuration. + * This indicates that only messages at or above this level should be + * logged; all others are silently discarded. The passed log level + * should be one of the log levels defined by + * .Xr syslog 3 . + * Refer to that page for a complete list of acceptable log levels, + * and note that passing an invalid log level will result in undefined + * behavior. + */ +extern void LogConfigLevelSet(LogConfig *, int); + +/** + * Cause the log output to be indented two more spaces than it was + * previously. This can be helpful when generating stack traces or + * other hierarchical output. This is a simple convenience wrapper + * around + * .Fn LogConfigIndentSet . + */ +extern void LogConfigIndent(LogConfig *); + +/** + * Cause the log output to be indented two less spaces than it was + * previously. This is a simple convenience wrapper around + * .Fn LogConfigIndentSet . + */ +extern void LogConfigUnindent(LogConfig *); + +/** + * Indent future log output using the specified config by some + * arbitrary amount. + */ +extern void LogConfigIndentSet(LogConfig *, size_t); + +/** + * Set the file stream that logging output should be written to. This + * defaults to standard output, but it can be set to standard error, + * or any other arbitrary stream. Passing a NULL value for the stream + * pointer sets the log output to the standard output. Note that the + * output stream is only used if + * .Va LOG_FLAG_SYSLOG + * is not set. + */ +extern void LogConfigOutputSet(LogConfig *, Stream *); + +/** + * Set a number of boolean options on a log configuration. This + * function uses bitwise operators, so multiple options can be set with + * a single function call using bitwise OR operators. The flags are + * defined as preprocessor macros, and are as follows: + * .Bl -tag -width Ds + * .It LOG_FLAG_COLOR + * When set, enable color-coded output on TTYs. Note that colors are + * implemented as ANSI escape sequences, and are not written to file + * streams that are not actually connected to a TTY, to prevent those + * sequences from being written to a file. + * .Xr isatty 3 + * is checked before writing any terminal sequences. + * .It LOG_FLAG_SYSLOG + * When set, log output to the syslog using + * .Xr syslog 3 , + * instead of logging to the file set by + * .Fn LogConfigOutputSet . + * This flag always overrides the stream set by that function, + * regardless of when it was set, even if it was set after this flag + * was set. + * .El + */ +extern void LogConfigFlagSet(LogConfig *, int); + +/** + * Clear a boolean flag from the specified log format. See above for + * the list of flags. + */ +extern void LogConfigFlagClear(LogConfig *, int); + +/** + * Set a custom timestamp to be prepended to each message if the + * output is not going to the system log. Consult your system's + * documentation for + * .Xr strftime 3 . + * A value of NULL disables the timestamp output before messages. + */ +extern void LogConfigTimeStampFormatSet(LogConfig *, char *); + +/** + * This function does the actual logging of messages using a + * specified configuration. It takes the configuration, the log + * level, a format string, and then a list of arguments, all in that + * order. This function only logs messages if their level is above + * or equal to the currently configured log level, making it easy to + * turn some messages on or off. + * .Pp + * This function has the same usage as + * .Xr vprintf 3 . + * Consult that page for the list of format specifiers and their + * arguments. This function is typically not used directly, see the + * other log functions for the most common use cases. + */ +extern void Logv(LogConfig *, int, const char *, va_list); + +/** + * Log a message using + * .Fn Logv . + * with the specified configuration. This function has the same usage + * as + * .Xr printf 3 . + */ +extern void LogTo(LogConfig *, int, const char *, ...); + +/** + * Log a message to the global log using + * .Fn Logv . + * This function has the same usage as + * .Xr printf 3 . + */ +extern void Log(int, const char *, ...); + +#endif diff --git a/src/include/Memory.h b/src/include/Memory.h new file mode 100644 index 0000000..5d5ecec --- /dev/null +++ b/src/include/Memory.h @@ -0,0 +1,235 @@ +/* + * Copyright (C) 2022-2023 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_MEMORY_H +#define CYTOPLASM_MEMORY_H + +/*** + * @Nm Memory + * @Nd Smart memory management. + * @Dd January 9 2023 + * + * .Nm + * is an API that allows for smart memory management and profiling. It + * wraps the standard library functions + * .Xr malloc 3 , + * .Xr realloc 3 , + * and + * .Xr free 3 , + * and offers identical semantics, while providing functionality that + * the standard library doesn't have, such as getting statistics on the + * total memory allocated on the heap, and getting the size of a block + * given a pointer. Additionally, thanks to preprocessor macros, the + * exact file and line number at which an allocation, re-allocation, or + * free occured can be obtained given a pointer. Finally, all the + * blocks allocated on the heap can be iterated and evaluated, and a + * callback function can be executed every time a memory operation + * occurs. + * .Pp + * In the future, this API could include a garbage collector that + * automatically frees memory it detects as being no longer in use. + * However, this feature does not yet exist. + * .Pp + * A number of macros are available, which make the + * .Nm + * API much easier to use. They are as follows: + * .Bl -bullet -offset indent + * .It + * .Fn Malloc "x" + * .It + * .Fn Realloc "x" "y" + * .It + * .Fn Free "x" + * .El + * .Pp + * These macros expand to + * .Fn MemoryAllocate , + * .Fn MemoryReallocate , + * and + * .Fn MemoryFree + * with the second and third parameters set to __FILE__ and __LINE__. + * This allows + * .Nm + * to be used exactly how the standard library functions would be + * used. In fact, the functions to which these macros expand are not + * intended to be used directly; for the best results, use these + * macros. + */ +#include + +/** + * These values are passed into the memory hook function to indicate + * the action that just happened. + */ +typedef enum MemoryAction +{ + MEMORY_ALLOCATE, + MEMORY_REALLOCATE, + MEMORY_FREE, + MEMORY_BAD_POINTER, + MEMORY_CORRUPTED +} MemoryAction; + +#define Malloc(x) MemoryAllocate(x, __FILE__, __LINE__) +#define Realloc(x, s) MemoryReallocate(x, s, __FILE__, __LINE__) +#define Free(x) MemoryFree(x, __FILE__, __LINE__) + +/** + * The memory information is opaque, but can be accessed using the + * functions defined by this API. + */ +typedef struct MemoryInfo MemoryInfo; + +/** + * Allocate the specified number of bytes on the heap. This function + * has the same semantics as + * .Xr malloc 3 , + * except that it takes the file name and line number at which the + * allocation occurred. + */ +extern void * MemoryAllocate(size_t, const char *, int); + +/** + * Change the size of the object pointed to by the given pointer + * to the given number of bytes. This function has the same semantics + * as + * .Xr realloc 3 , + * except that it takes the file name and line number at which the + * reallocation occurred. + */ +extern void * MemoryReallocate(void *, size_t, const char *, int); + +/** + * Free the memory at the given pointer. This function has the same + * semantics as + * .Xr free 3 , + * except that it takes the file name and line number at which the + * free occurred. + */ +extern void MemoryFree(void *, const char *, int); + +/** + * Get the total number of bytes that the program has allocated on the + * heap. This operation iterates over all heap allocations made with + * .Fn MemoryAllocate + * and then returns a total count, in bytes. + */ +extern size_t MemoryAllocated(void); + +/** + * Iterate over all heap allocations made with + * .Fn MemoryAllocate + * and call + * .Fn MemoryFree + * on them. This function immediately invalidates all pointers to + * blocks on the heap, and any subsequent attempt to read or write to + * data on the heap will result in undefined behavior. This is + * typically called at the end of the program, just before exit. + */ +extern void MemoryFreeAll(void); + +/** + * Fetch information about an allocation. This function takes a raw + * pointer, and if + * . Nm + * knows about the pointer, it returns a structure that can be used + * to obtain information about the block of memory that the pointer + * points to. + */ +extern MemoryInfo * MemoryInfoGet(void *); + +/** + * Get the size in bytes of the block of memory represented by the + * specified memory info structure. + */ +extern size_t MemoryInfoGetSize(MemoryInfo *); + +/** + * Get the file name in which the block of memory represented by the + * specified memory info structure was allocated. + */ +extern const char * MemoryInfoGetFile(MemoryInfo *); + +/** + * Get the line number on which the block of memory represented by the + * specified memory info structure was allocated. + */ +extern int MemoryInfoGetLine(MemoryInfo *); + +/** + * Get a pointer to the block of memory represented by the specified + * memory info structure. + */ +extern void * MemoryInfoGetPointer(MemoryInfo *); + +/** + * This function takes a pointer to a function that takes the memory + * info structure, as well as a void pointer for caller-provided + * arguments. It iterates over all the heap memory currently allocated + * at the time of calling, executing the function on each allocation. + */ +extern void MemoryIterate(void (*) (MemoryInfo *, void *), void *); + +/** + * Specify a function to be executed whenever a memory operation + * occurs. The MemoryAction argument specifies the operation that + * occurred on the block of memory represented by the memory info + * structure. The function also takes a void pointer to caller-provided + * arguments. + */ +extern void MemoryHook(void (*) (MemoryAction, MemoryInfo *, void *), void *); + +/** + * The default memory hook, which has sane behavior and is installed + * at runtime. This function does not use any memory on the heap, + * except for the MemoryInfo passed to it, which it assumes to be + * valid. Everything else happens on the stack only, to ensure that + * the hook doesn't make any memory problems worse. + */ +extern void MemoryDefaultHook(MemoryAction, MemoryInfo *, void *); + +/** + * Read over the block of memory represented by the given memory info + * structure and generate a hexadecimal and ASCII string for each + * chunk of the block. This function takes a callback function that + * takes the following parameters in order: + * .Bl -bullet -offset indent + * .It + * The current offset from the beginning of the block of memory in + * bytes. + * .It + * A null-terminated string containing the next 16 bytes of the block + * encoded as space-separated hex values. + * .It + * A null-terminated string containing the ASCII representation of the + * same 16 bytes of memory. This ASCII representation is safe to print + * to a terminal or other text device, because non-printable characters + * are encoded as a . (period). + * .It + * Caller-passed pointer. + * .El + */ +extern void +MemoryHexDump(MemoryInfo *, void (*) (size_t, char *, char *, void *), void *); + +#endif diff --git a/src/include/Queue.h b/src/include/Queue.h new file mode 100644 index 0000000..35821d3 --- /dev/null +++ b/src/include/Queue.h @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2022-2023 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_QUEUE_H +#define CYTOPLASM_QUEUE_H + +/*** + * @Nm Queue + * @Nd A simple static queue data structure. + * @Dd November 25 2022 + * @Xr Array HashMap + * + * .Nm + * implements a simple queue data structure that is statically sized. + * This implementation does not actually store the values of the items + * in it; it only stores pointers to the data. As such, you will have + * to manually maintain data and make sure it remains valid as long as + * it is in the queue. The advantage of this is that + * .Nm + * doesn't have to copy data, and thus doesn't care how big the data + * is. Furthermore, any arbitrary data can be stored in the queue. + * .Pp + * This queue implementation operates on the heap. It is a circular + * queue, and it does not grow as it is used. Once the size is set, + * the queue never gets any bigger. + */ + +#include + +/** + * These functions operate on a queue structure that is opaque to the + * caller. + */ +typedef struct Queue Queue; + +/** + * Allocate a new queue that is able to store the specified number of + * items in it. + */ +extern Queue * QueueCreate(size_t); + +/** + * Free the memory associated with the specified queue structure. Note + * that this function does not free any of the values stored in the + * queue; it is the caller's job to manage memory for each item. + * Typically, the caller would dequeue all the items in the queue and + * deal with them before freeing the queue itself. + */ +extern void QueueFree(Queue *); + +/** + * Push an element into the queue. This function returns a boolean + * value indicating whether or not the push succeeded. Pushing items + * into the queue will fail if the queue is full. + */ +extern int QueuePush(Queue *, void *); + +/** + * Pop an element out of the queue. This function returns NULL if the + * queue is empty. Otherwise, it returns a pointer to the item that is + * next up in the queue. + */ +extern void * QueuePop(Queue *); + +/** + * Retrieve a pointer to the item that is next up in the queue without + * actually discarding it, such that the next call to + * .Fn QueuePeek + * or + * .Fn QueuePop + * will return the same pointer. + */ +extern void * QueuePeek(Queue *); + +/** + * Determine whether or not the queue is full. + */ +extern int QueueFull(Queue *); + +/** + * Determine whether or not the queue is empty. + */ +extern int QueueEmpty(Queue *); + +#endif diff --git a/src/include/Rand.h b/src/include/Rand.h new file mode 100644 index 0000000..a5be3ce --- /dev/null +++ b/src/include/Rand.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2022-2023 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_RAND_H +#define CYTOPLASM_RAND_H + +/*** + * @Nm Rand + * @Nd Thread-safe random numbers. + * @Dd February 16 2023 + * @Xr Util + * + * .Nm + * is used for generating random numbers in a thread-safe way. + * Currently, one generator state is shared across all threads, which + * means that only one thread can generate random numbers at a time. + * This state is protected with a mutex to guarantee this behavior. + * In the future, a seed pool may be maintained to allow multiple + * threads to generate random numbers at the same time. + * .Pp + * The generator state is seeded on the first call to a function that + * needs it. The seed is determined by the current timestamp, the ID + * of the process, and the thread ID. These should all be sufficiently + * random sources, so the seed should be secure enough. + * .Pp + * .Nm + * currently uses a simple Mersenne Twister algorithm to generate + * random numbers. This algorithm was chosen because it is extremely + * popular and widespread. While it is likely not cryptographically + * secure, and does suffer some unfortunate pitfalls, this algorithm + * has stood the test of time and is simple enough to implement, so + * it was chosen over the alternatives. + * .Pp + * .Nm + * does not use any random number generator functions from the C + * standard library, since these are often flawed. + */ + +#include + +/** + * Generate a single random integer between 0 and the passed value. + */ +extern int RandInt(unsigned int); + +/** + * Generate the number of integers specified by the second argument + * storing them into the buffer pointed to in the first argument. + * Ensure that each number is between 0 and the third argument. + * .Pp + * This function allows a caller to get multiple random numbers at once + * in a more efficient manner than repeatedly calling + * .Fn RandInt , + * since each call to these functions + * has to lock and unlock a mutex. It is therefore better to obtain + * multiple random numbers in one pass if multiple are needed. + */ +extern void RandIntN(int *, size_t, unsigned int); + +#endif /* CYTOPLASM_RAND_H */ diff --git a/src/include/Runtime.h b/src/include/Runtime.h new file mode 100644 index 0000000..6e13b6a --- /dev/null +++ b/src/include/Runtime.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2022-2023 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_RUNTIME_H +#define CYTOPLASM_RUNTIME_H + +/*** + * @Nm Runtime + * @Nd Supporting functions for the Cytoplasm runtime. + * @Dd May 23 2023 + * @Xr Memory + * + * .Nm + * provides supporting functions for the Cytoplasm runtime. These + * functions are not intended to be called directly by programs, + * but are used internally. They're exposed via a header because + * the runtime stub needs to know their definitions. + */ + +#include + +/** + * Write a memory report to a file in the current directory, using + * the provided program arguments, including the program name that + * executed. This function is to be called after all memory is + * supposed to have been freed. It iterates over all remaining + * memory and generates a text file containing all of the + * recorded information about each block, including a hex dump of + * the data stored in them. + */ +extern void GenerateMemoryReport(int argc, char **argv); + +#endif /* CYTOPLASM_RUNTIME_H */ diff --git a/src/include/Sha.h b/src/include/Sha.h new file mode 100644 index 0000000..25b6247 --- /dev/null +++ b/src/include/Sha.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2022-2023 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_SHA_H +#define CYTOPLASM_SHA_H + +/*** + * @Nm Sha + * @Nd A simple implementation of a few SHA hashing functions. + * @Dd December 19 2022 + * @Xr Memory Base64 + * + * This API defines simple functions for computing SHA hashes. + * At the moment, it only defines + * .Fn Sha256 + * and + * .Fn Sha1 , + * which compute the SHA-256 and SHA-1 hashes of the given C string, + * respectively. It is not trivial to implement SHA-512 in ANSI C + * due to the lack of a 64-bit integer type, so that hash + * function has been omitted. + */ + +/** + * This function takes a pointer to a NULL-terminated C string, and + * returns a NULL-terminated byte buffer allocated on the heap using + * the Memory API, or NULL if there was an error allocating memory. + * The returned byte buffer should be freed when it is no longer + * needed. It is important to note that the returned buffer is not + * a printable string; to get a printable string, use + * .Fn ShaToHex . + */ +extern unsigned char * Sha256(char *); + +/** + * This function takes a pointer to a NULL-terminated C string, and + * returns a NULL-terminated byte buffer allocated on the heap using + * the Memory API, or NULL if there was an error allocating memory. + * The returned byte buffer should be freed when it is no longer + * needed. It is important to note that the returned buffer is not + * a printable string; to get a printable string, use + * .Fn ShaToHex . + */ +extern unsigned char * Sha1(char *); + +/** + * Convert a SHA byte buffer into a hex string. These hex strings + * are typically what is transmitted, stored, and compared, however + * there may be times when it is necessary to work with the raw + * bytes directly, which is why the conversion to a hex string is + * a separate step. + */ +extern char * ShaToHex(unsigned char *); + +#endif /* CYTOPLASM_SHA_H */ diff --git a/src/include/Str.h b/src/include/Str.h new file mode 100644 index 0000000..714b8d8 --- /dev/null +++ b/src/include/Str.h @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2022-2023 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_STR_H +#define CYTOPLASM_STR_H + +/*** + * @Nm Str + * @Nd Functions for creating and manipulating strings. + * @Dd February 15 2023 + * @Xr Memory + * + * .Nm + * provides string-related functions. It is called + * .Nm , + * not String, because some platforms (Windows) do not have + * case-sensitive filesystems, which poses a problem since + * .Pa string.h + * is a standard library header. + */ + +#include + +#include + +/** + * Convert UTF-16 into a Unicode codepoint. + */ +extern UInt32 StrUtf16Decode(UInt16, UInt16); + +/** + * Take a Unicode codepoint and encode it into a string buffer containing + * between 1 and 4 bytes. The string buffer is allocated on the heap, + * so it should be freed when it is no longer needed. + */ +extern char * StrUtf8Encode(UInt32); + +/** + * Duplicate a null-terminated string, returning a new string on the + * heap. This is useful when a function takes in a string that it needs + * to store for long amounts of time, even perhaps after the original + * string is gone. + */ +extern char * StrDuplicate(const char *); + +/** + * Extract part of a null-terminated string, returning a new string on + * the heap containing only the requested subsection. Like the + * substring functions included with most programming languages, the + * starting index is inclusive, and the ending index is exclusive. + */ +extern char * StrSubstr(const char *, size_t, size_t); + +/** + * A varargs function that takes a number of null-terminated strings + * specified by the first argument, and returns a new string that + * contains their concatenation. It works similarly to + * .Xr strcat 3 , + * but it takes care of allocating memory big enough to hold all the + * strings. Any string in the list may be NULL. If a NULL pointer is + * passed, it is treated like an empty string. + */ +extern char * StrConcat(size_t,...); + +/** + * Return a boolean value indicating whether or not the null-terminated + * string consists only of blank characters, as determined by + * .Xr isblank 3 . + */ +extern int StrBlank(const char *str); + +/** + * Generate a string of the specified length, containing random + * lowercase and uppercase letters. + */ +extern char * StrRandom(size_t); + +/** + * Convert the specified integer into a string, returning the string + * on the heap, or NULL if there was a memory allocation error. The + * returned string should be freed by the caller after it is no longer + * needed. + */ +extern char * StrInt(long); + +/** + * Converts a string into a lowercase version of it using + * .Xr tolower 3 , + * returning the lowercase version on the heap, or NULL if there was + * a memory allocation error. The returned string should be freed by + * the caller after it is no longer needed. + */ +extern char * StrLower(char *); + +/** + * Compare two strings and determine whether or not they are equal. + * This is the most common use case of strcmp() in Cytoplasm, but + * strcmp() doesn't like NULL pointers, so these have to be checked + * explicitly and can cause problems if they aren't. This function, + * on the other hand, makes NULL pointers special cases. If both + * arguments are NULL, then they are considered equal. If only one + * argument is NULL, they are considered not equal. Otherwise, if + * no arguments are NULL, a regular strcmp() takes place and this + * function returns a boolean value indicating whether or not + * strcmp() returned 0. + */ +extern int StrEquals(const char *, const char *); + +#endif /* CYTOPLASM_STR_H */ diff --git a/src/include/Stream.h b/src/include/Stream.h new file mode 100644 index 0000000..3ddacc6 --- /dev/null +++ b/src/include/Stream.h @@ -0,0 +1,223 @@ +/* + * Copyright (C) 2022-2023 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_STREAM_H +#define CYTOPLASM_STREAM_H + +/*** + * @Nm Stream + * @Nd An abstraction over the Io API that implements standard C I/O. + * @Dd April 29 2023 + * @Xr Io + * + * .Nm + * implements an abstraction layer over the Io API. This layer buffers + * I/O and makes it much easier to work with, mimicking the standard + * C library and offering some more convenience features. + */ + +#include + +#include + +/** + * An opaque structure analogous to C's FILE pointers. + */ +typedef struct Stream Stream; + +/** + * Create a new stream using the specified Io for underlying I/O + * operations. + */ +extern Stream * StreamIo(Io * io); + +/** + * Create a new stream using the specified POSIX file descriptor. + * This is a convenience function for calling + * .Fn IoFd + * and then passing the result into + * .Fn StreamIo . + */ +extern Stream * StreamFd(int); + +/** + * Create a new stream using the specified C FILE pointer. This is a + * convenience function for calling + * .Fn IoFile + * and then passing the result into + * .Fn StreamIo . + */ +extern Stream * StreamFile(FILE *); + +/** + * Create a new stream using the specified path and mode. This is a + * convenience function for calling + * .Xr fopen 3 + * and then passing the result into + * .Fn StreamFile . + */ +extern Stream * StreamOpen(const char *, const char *); + +/** + * Get a stream that writes to the standard output. + */ +extern Stream * StreamStdout(void); + +/** + * Get a stream that writes to the standard error. + */ +extern Stream * StreamStderr(void); + +/** + * Get a stream that reads from the standard input. + */ +extern Stream * StreamStdin(void); + +/** + * Close the stream. This flushes the buffers and closes the underlying + * Io. It is analogous to the standard + * .Xr fclose 3 + * function. + */ +extern int StreamClose(Stream *); + +/** + * Print a formatted string. This function is analogous to the standard + * .Xr vfprintf 3 + * function. + */ +extern int StreamVprintf(Stream *, const char *, va_list); + +/** + * Print a formatted string. This function is analogous to the + * standard + * .Xr fprintf 3 + * function. + */ +extern int StreamPrintf(Stream *, const char *,...); + +/** + * Get a single character from a stream. This function is analogous to + * the standard + * .Xr fgetc 3 + * function. + */ +extern int StreamGetc(Stream *); + +/** + * Push a character back onto the input stream. This function is + * analogous to the standard + * .Xr ungetc 3 + * function. + */ +extern int StreamUngetc(Stream *, int); + +/** + * Write a single character to the stream. This function is analogous + * to the standard + * .Xr fputc 3 + * function. + */ +extern int StreamPutc(Stream *, int); + +/** + * Write a null-terminated string to the stream. This function is + * analogous to the standard + * .Xr fputs 3 + * function. + */ +extern int StreamPuts(Stream *, char *); + +/** + * Read at most the specified number of characters minus 1 from the + * specified stream and store them at the memory located at the + * specified pointer. This function is analogous to the standard + * .Xr fgets 3 + * function. + */ +extern char * StreamGets(Stream *, char *, int); + +/** + * Set the file position indicator for the specified stream. This + * function is analogous to the standard + * .Xr fseeko + * function. + */ +extern off_t StreamSeek(Stream *, off_t, int); + +/** + * Test the end-of-file indicator for the given stream, returning a + * boolean value indicating whether or not it is set. This is analogous + * to the standard + * .Xr feof 3 + * function. + */ +extern int StreamEof(Stream *); + +/** + * Test the stream for an error condition, returning a boolean value + * indicating whether or not one is present. This is analogous to the + * standard + * .Xr ferror 3 + * function. + */ +extern int StreamError(Stream *); + +/** + * Clear the error condition associated with the given stream, allowing + * future reads or writes to potentially be successful. This functio + * is analogous to the standard + * .Xr clearerr 3 + * function. + */ +extern void StreamClearError(Stream *); + +/** + * Flush all buffered data using the streams underlying write function. + * This function is analogous to the standard + * .Xr fflush 3 + * function. + */ +extern int StreamFlush(Stream *); + +/** + * Read all the bytes from the first stream and write them to the + * second stream. This is analogous to + * .Fn IoCopy , + * but it uses the internal buffers of the streams. It is probably + * less efficient than doing a + * .Fn IoCopy + * instead, but it is more convenient. + */ +extern ssize_t StreamCopy(Stream *, Stream *); + +/** + * Get the file descriptor associated with the given stream, or -1 if + * the stream is not associated with any file descriptor. This function + * is analogous to the standard + * .Xr fileno 3 + * function. + */ +extern int StreamFileno(Stream *); + +#endif /* CYTOPLASM_STREAM_H */ diff --git a/src/include/Tls.h b/src/include/Tls.h new file mode 100644 index 0000000..4407ed0 --- /dev/null +++ b/src/include/Tls.h @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2022-2023 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_TLS_H +#define CYTOPLASM_TLS_H + +/*** + * @Nm Tls + * @Nd Interface to platform-dependent TLS libraries. + * @Dd April 29 2023 + * @Xr Stream Io + * + * .Nm + * provides an interface to platform-dependent TLS libraries. It allows + * Cytoplasm to support any TLS library with no changes to existing + * code. Support for additional TLS libraries is added by creating a + * new compilation unit that implements all the functions here, with + * the exception of a few, which are noted. + * .Pp + * Currently, Cytoplasm has support for the following TLS libraries: + * .Bl -bullet -offset indent + * .It + * LibreSSL + * .It + * OpenSSL + * .El + */ + +#include + +#define TLS_LIBRESSL 2 +#define TLS_OPENSSL 3 + +/** + * Create a new TLS client stream using the given file descriptor and + * the given server hostname. The hostname should be used to verify + * that the server actually is who it says it is. + * .Pp + * This function does not need to be implemented by the individual + * TLS support stubs. + */ +extern Stream * TlsClientStream(int, const char *); + +/** + * Create a new TLS server stream using the given certificate and key + * file, in the format natively supported by the TLS library. + * .Pp + * This function does not need to be implemented by the individual + * TLS support stubs. + */ +extern Stream * TlsServerStream(int, const char *, const char *); + +/** + * Initialize a cookie that stores information about the given client + * connection. This cookie will be passed into the other functions + * defined by this API. + */ +extern void * TlsInitClient(int, const char *); + +/** + * Initialize a cookie that stores information about the given + * server connection. This cookie will be passed into the other + * functions defined by this API. + */ +extern void * TlsInitServer(int, const char *, const char *); + +/** + * Read from a TLS stream, decrypting it and storing the result in the + * specified buffer. This function takes the cookie, buffer, and + * number of decrypted bytes to read into it. See the documentation for + * .Fn IoRead . + */ +extern ssize_t TlsRead(void *, void *, size_t); + +/** + * Write to a TLS stream, encrypting the buffer. This function takes + * the cookie, buffer, and number of unencrypted bytes to write to + * the stream. See the documentation for + * .Fn IoWrite . + */ +extern ssize_t TlsWrite(void *, void *, size_t); + +/** + * Close the TLS stream, also freeing all memory associated with the + * cookie. + */ +extern int TlsClose(void *); + +#endif /* CYTOPLASM_TLS_H */ diff --git a/src/include/UInt64.h b/src/include/UInt64.h new file mode 100644 index 0000000..51cc30c --- /dev/null +++ b/src/include/UInt64.h @@ -0,0 +1,252 @@ +/* + * Copyright (C) 2022-2023 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_UINT64_H +#define CYTOPLASM_UINT64_H + +/*** + * @Nm UInt64 + * @Nd Fixed-width 64 bit integers. + * @Dd August 11, 2023 + * + * .Pp + * ANSI C89 (or C99 for that matter) provides no required mechanism + * for 64 bit integers. Nevertheless, many compilers provide them as + * extensions. However, since it is not a gaurantee, and to be fully + * standards-compliant and thus portable, a platform-agnostic interface + * is required. This header provides such an interface. If the platform + * has a 64 bit integer type, that is used, and native operations are + * performed by C preprocessor macro expansion. Otherwise, a + * compatibility layer is provided, which implements 64-bit + * arithmetic on an array of 2 32-bit numbers which are provided by + * .Xr Int 3 . + * .Pp + * Note that 64-bit emulation is certainly not as performant as using + * native 64-bit operations, so whenever possible, the native + * operations should be preferred. However, since C provides no required + * 64 bit integer on 32-bit and less platforms, this API can be used as + * a "good enough" fallback mechanism. + * .Pp + * Also note that this implementation, both in the native and + * non-native forms, makes some assumptions: + * .Bl -bullet -width Ds + * .It + * When a cast from a larger integer to a smaller integer is performed, + * the upper bits are truncated, not the lower bits. + * .It + * Negative numbers are represented in memory and in registers in two's + * compliment form. + * .El + * .Pp + * This API may provide unexpected output if these assumptions are + * false for a given platform. + * + * @ignore-typedefs + */ + +#include + +#include + +#ifndef INT64_FORCE_EMULATED + +#define BIT64_MAX 18446744073709551615UL + +#if UINT_MAX == BIT64_MAX +/* typedef signed int Int64; */ +typedef unsigned int UInt64; + +#define UINT64_NATIVE + +#elif ULONG_MAX == BIT64_MAX +/* typedef signed int Int64; */ +typedef unsigned long UInt64; + +#define UINT64_NATIVE + +#endif + +#endif /* ifndef INT64_FORCE_EMULATED */ + +#ifdef UINT64_NATIVE + +#define UInt64Create(high, low) (((UInt64) (high) << 32) | (low)) +#define UInt64Low(a) ((UInt32) ((a) & 0x00000000FFFFFFFF)) +#define UInt64High(a) ((UInt32) ((a) >> 32)) + +#define UInt64Add(a, b) ((a) + (b)) +#define UInt64Sub(a, b) ((a) - (b)) +#define UInt64Mul(a, b) ((a) * (b)) +#define UInt64Div(a, b) ((a) / (b)) +#define UInt64Rem(a, b) ((a) % (b)) + +#define UInt64Sll(a, b) ((a) << (b)) +#define UInt64Srl(a, b) ((a) >> (b)) + +#define UInt64And(a, b) ((a) & (b)) +#define UInt64Or(a, b) ((a) | (b)) +#define UInt64Xor(a, b) ((a) ^ (b)) +#define UInt64Not(a) (~(a)) + +#define UInt64Eq(a, b) ((a) == (b)) +#define UInt64Lt(a, b) ((a) < (b)) +#define UInt64Gt(a, b) ((a) > (b)) + +#define UInt64Neq(a, b) ((a) != (b)) +#define UInt64Leq(a, b) ((a) <= (b)) +#define UInt64Geq(a, b) ((a) >= (b)) + +#else + +/** + * For platforms that do not have a native integer large enough to + * store a 64 bit integer, this struct is used. i[0] contains the low + * bits of integer, and i[1] contains the high bits of the integer. + * .Pp + * This struct should not be accessed directly, because UInt64 may not + * actually be this struct, it might be an actual integer type. For + * maximum portability, only use the functions defined here to + * manipulate 64 bit integers. + */ +typedef struct +{ + UInt32 i[2]; +} UInt64; + +/** + * Create a new unsigned 64 bit integer using the given high and low + * bits. + */ +extern UInt64 UInt64Create(UInt32, UInt32); + +/** + * Add two unsigned 64 bit integers together. + */ +extern UInt64 UInt64Add(UInt64, UInt64); + +/** + * Subtract the second 64 bit integer from the first. + */ +extern UInt64 UInt64Sub(UInt64, UInt64); + +/** + * Multiply two 64 bit integers together. The non-native version of + * this function uses the Russian Peasant method of multiplication, + * which should afford more performance than a naive multiplication by + * addition, but it is still rather slow and depends on the size of + * the integers being multiplied. + */ +extern UInt64 UInt64Mul(UInt64, UInt64); + +/** + * Divide the first 64 bit integer by the second and return the + * quotient. The non-native version of this function uses naive binary + * long division, which is slow, but gauranteed to finish in constant + * time. + */ +extern UInt64 UInt64Div(UInt64, UInt64); + +/** + * Divide the first 64 bit integer by the second and return the + * remainder. The non-native version of this function uses naive binary + * long division, which is slow, but gauranteed to finish in constant + * time. + */ +extern UInt64 UInt64Rem(UInt64, UInt64); + +/** + * Perform a left logical bit shift of a 64 bit integer. The second + * parameter is how many places to shift, and is declared as a regular + * integer because anything more than 64 does not make sense. + */ +extern UInt64 UInt64Sll(UInt64, int); + +/** + * Perform a right logical bit shift of a 64 bit integer. The second + * parameter is how many places to shift, and is declared as a regular + * integer because anything more than 64 does not make sense. + */ +extern UInt64 UInt64Srl(UInt64, int); + +/** + * Perform a bitwise AND (&) of the provided 64 bit integers. + */ +extern UInt64 UInt64And(UInt64, UInt64); + +/** + * Perform a bitwise OR (|) of the provided 64 bit integers. + */ +extern UInt64 UInt64Or(UInt64, UInt64); + +/** + * Perform a bitwise XOR (^) of the provided 64 bit integers. + */ +extern UInt64 UInt64Xor(UInt64, UInt64); + +/** + * Perform a bitwise NOT (~) of the provided 64 bit integer. + */ +extern UInt64 UInt64Not(UInt64); + +/** + * Perform a comparison of the provided 64 bit integers and return a C + * boolean that is true if and only if they are equal. + */ +extern int UInt64Eq(UInt64, UInt64); + +/** + * Perform a comparison of the provided 64 bit integers and return a C + * boolean that is true if and only if the second operand is strictly + * less than the first. + */ +extern int UInt64Lt(UInt64, UInt64); + +/** + * Perform a comparison of the provided 64 bit integers and return a C + * boolean that is true if and only if the second operand is strictly + * greater than the first. + */ +extern int UInt64Gt(UInt64, UInt64); + +#define UInt64Low(a) ((a).i[0]) +#define UInt64High(a) ((a).i[1]) + +#define UInt64Neq(a, b) (!UInt64Eq(a, b)) +#define UInt64Leq(a, b) (UInt64Eq(a, b) || UInt64Lt(a, b)) +#define UInt64Geq(a, b) (UInt64Eq(a, b) || UInt64Gt(a, b)) + +#endif + +#define UINT64_STRBUF 65 /* Base 2 representation with '\0' */ + +/** + * Convert a 64 bit integer to a string in an arbitrary base + * representation specified by the second parameter, using the provided + * buffer and length specified by the third and fourth parameters. To + * guarantee that the string will fit in the buffer, allocate it of + * size UINT64_STRBUF or larger. Note that a buffer size smaller than + * UINT64_STRBUF will invoke undefined behavior. + */ +extern size_t UInt64Str(UInt64, int, char *, size_t); + +#endif /* CYTOPLASM_UINT64_H */ diff --git a/src/include/Uri.h b/src/include/Uri.h new file mode 100644 index 0000000..a47bf28 --- /dev/null +++ b/src/include/Uri.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2022-2023 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_URI_H +#define CYTOPLASM_URI_H + +/*** + * @Nm Uri + * @Nd Parse a URI. Typically used to parse HTTP(s) URLs. + * @Dd April 29 2023 + * @Xr Http + * + * .Nm + * provides a simple mechanism for parsing URIs. This is an extremely + * basic parser that (ab)uses + * .Xr sscanf 3 + * to parse URIs, so it may not be the most reliable, but it should + * work in most cases and on reasonable URIs that aren't too long, as + * the _MAX definitions are modest. + */ + +#define URI_PROTO_MAX 8 +#define URI_HOST_MAX 128 +#define URI_PATH_MAX 256 + +/** + * The parsed URI is stored in this structure. + */ +typedef struct Uri +{ + char proto[URI_PROTO_MAX]; + char host[URI_HOST_MAX]; + char path[URI_PATH_MAX]; + unsigned short port; +} Uri; + +/** + * Parse a URI string into the Uri structure as described above, or + * return NULL if there was a parsing error. + */ +extern Uri * UriParse(const char *); + +/** + * Free the memory associated with a Uri structure returned by + * .Fn UriParse . + */ +extern void UriFree(Uri *); + +#endif /* CYTOPLASM_URI_H */ diff --git a/src/include/Util.h b/src/include/Util.h new file mode 100644 index 0000000..d6c3830 --- /dev/null +++ b/src/include/Util.h @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2022-2023 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_UTIL_H +#define CYTOPLASM_UTIL_H + +/*** + * @Nm Util + * @Nd Some misc. helper functions that don't need their own headers. + * @Dd February 15 2023 + * + * This header holds a number of random functions related to strings, + * time, the filesystem, and other simple tasks that don't require a + * full separate API. For the most part, the functions here are + * entirely standalone, depending only on POSIX functions, however + * there are a few that depend explicitly on a few other APIs. Those + * are noted. + */ + +#include +#include +#include + +#include +#include + +/** + * Get the current timestamp in milliseconds since the Unix epoch. This + * uses + * .Xr gettimeofday 2 + * and time_t, and converts it to a single number, which is then + * returned to the caller. + * .Pp + * A note on the 2038 problem: as long as sizeof(time_t) >= 8, that is, + * as long as the time_t type is 64 bits or more, then everything + * should be fine. On most, if not, all, 64-bit systems, time_t is 64 + * bits. time_t is promoted to a 64-bit integer before it is converted + * to milliseconds, so there is no risk of overflue due to the + * multiplication by 1000. However, if time_t is only 32 bits, it will + * overflow before it even gets to this function, which will cause this + * function to produce unexpected results. + */ +extern UInt64 UtilServerTs(void); + +/** + * Use + * .Xr stat 2 + * to get the last modified time of the given file, or zero if there + * was an error getting the last modified time of a file. This is + * primarily useful for caching file data. + */ +extern UInt64 UtilLastModified(char *); + +/** + * This function behaves just like the system call + * .Xr mkdir 2 , + * but it creates any intermediate directories as necessary, unlike + * .Xr mkdir 2 . + */ +extern int UtilMkdir(const char *, const mode_t); + +/** + * Sleep the calling thread for the given number of milliseconds. + * POSIX does not have a very friendly way to sleep, so this wraps + * .Xr nanosleep 2 + * to make its usage much, much simpler. + */ +extern int UtilSleepMillis(UInt64); + +/** + * This function works identically to the POSIX + * .Xr getdelim 3 , + * except that it assumes pointers were allocated with the Memory API + * and it reads from a Stream instead of a file pointer. + */ +extern ssize_t UtilGetDelim(char **, size_t *, int, Stream *); + +/** + * This function is just a special case of + * .Fn UtilGetDelim + * that sets the delimiter to the newline character. + */ +extern ssize_t UtilGetLine(char **, size_t *, Stream *); + +/** + * Get a unique number associated with the current thread. + * Numbers are assigned in the order that threads call this + * function, but are guaranteed to be unique in identifying + * the thread in a more human-readable way than just casting + * the return value of + * .Fn pthread_self + * to a number. + */ +extern UInt32 UtilThreadNo(void); + +#endif /* CYTOPLASM_UTIL_H */ diff --git a/tools/int64.c b/tools/int64.c new file mode 100644 index 0000000..fb9c5cf --- /dev/null +++ b/tools/int64.c @@ -0,0 +1,145 @@ +#include + +#include + +/* AssertEquals(actual, expected) */ +int +AssertEquals(char *msg, Int64 x, Int64 y) +{ + if (!Int64Eq(x, y)) + { + Log(LOG_ERR, "%s: Expected 0x%X 0x%X, got 0x%X 0x%X", msg, + Int64High(y), Int64Low(y), + Int64High(x), Int64Low(x)); + + return 0; + } + + return 1; +} + +int +Main(void) +{ + Int64 x, y; + + Log(LOG_INFO, "sizeof(Int64) = %lu", sizeof(Int64)); + +#ifdef INT64_NATIVE + Log(LOG_INFO, "Using native 64-bit integers."); +#else + Log(LOG_INFO, "Using emulated 64-bit integers."); +#endif + + /* BSR Tests */ + + x = Int64Create(0x000000FF, 0x00000000); + + y = Int64Sra(x, 4); + AssertEquals("x >> 4", y, Int64Create(0x0000000F, 0xF0000000)); + + y = Int64Sra(x, 8); + AssertEquals("x >> 8", y, Int64Create(0x00000000, 0xFF000000)); + + y = Int64Sra(x, 36); + AssertEquals("x >> 36", y, Int64Create(0x00000000, 0x0000000F)); + + x = Int64Create(0xFF000000, 0x00000000); + + y = Int64Sra(x, 4); + AssertEquals("x >> 4", y, Int64Create(0xFFF00000, 0x00000000)); + + y = Int64Sra(x, 8); + AssertEquals("x >> 8", y, Int64Create(0xFFFF0000, 0x00000000)); + + y = Int64Sra(x, 63); + AssertEquals("x >> 63", y, Int64Create(0xFFFFFFFF, 0xFFFFFFFF)); + + /* BSL Tests */ + + x = Int64Create(0x00000000, 0xFF000000); + + y = Int64Sll(x, 4); + AssertEquals("x << 4", y, Int64Create(0x0000000F, 0xF0000000)); + + y = Int64Sll(x, 8); + AssertEquals("x << 8", y, Int64Create(0x000000FF, 0x00000000)); + + y = Int64Sll(x, 36); + AssertEquals("x << 36", y, Int64Create(0xF0000000, 0x00000000)); + + /* ADD Tests */ + + x = Int64Create(0x00000000, 0xF0000001); + y = Int64Create(0x00000000, 0x00000002); + AssertEquals("0xF0000001 + 0x00000002", Int64Add(x, y), Int64Create(0x00000000, 0xF0000003)); + + x = Int64Create(0x00000000, 0xF0000000); + y = Int64Create(0x00000000, 0x10000000); + AssertEquals("0xF0000000 + 0x10000000", Int64Add(x, y), Int64Create(0x00000001, 0x00000000)); + + x = Int64Create(0, 5); + y = Int64Neg(Int64Create(0, 10)); + AssertEquals("5 + (-10)", Int64Add(x, y), Int64Neg(Int64Create(0, 5))); + + /* SUB Tests */ + x = Int64Create(0x00000000, 0x00000005); + y = Int64Create(0x00000000, 0x00000002); + AssertEquals("0x00000005 - 0x00000002", Int64Sub(x, y), Int64Create(0x00000000, 0x00000003)); + + x = Int64Create(0x00000001, 0x00000000); + y = Int64Create(0x00000000, 0x00000001); + AssertEquals("0x00000001 0x00000000 - 0x00000001", Int64Sub(x, y), Int64Create(0x00000000, 0xFFFFFFFF)); + + x = Int64Create(0, 5); + y = Int64Create(0, 10); + AssertEquals("5 - 10", Int64Sub(x, y), Int64Neg(Int64Create(0, 5))); + + x = Int64Create(0, 5); + y = Int64Neg(Int64Create(0, 10)); + AssertEquals("5 - (-10)", Int64Sub(x, y), Int64Create(0, 15)); + + /* MUL Tests */ + x = Int64Create(0, 18); + y = Int64Create(0, 1); + AssertEquals("18 * 1", Int64Mul(x, y), Int64Create(0, 18)); + + x = Int64Create(0, 20); + y = Int64Create(0, 12); + AssertEquals("20 * 12", Int64Mul(x, y), Int64Create(0, 240)); + + x = Int64Create(0x00000000, 0x00000005); + y = Int64Create(0x00000000, 0x00000005); + AssertEquals("0x00000005 * 0x00000005", Int64Mul(x, y), Int64Create(0x00000000, 0x00000019)); + + x = Int64Create(0x00000001, 0x00000000); + y = Int64Create(0x00000000, 0x00000005); + AssertEquals("0x00000001 0x00000000 * 0x00000005", Int64Mul(x, y), Int64Create(0x00000005, 0x00000000)); + + /* DIV Tests */ + x = Int64Create(0, 12); + y = Int64Create(0, 4); + AssertEquals("12 / 4", Int64Div(x, y), Int64Create(0, 3)); + + /* MOD Tests */ + x = Int64Create(0x000000FF, 0x00000000); + y = Int64Create(0x00000000, 0x00000010); + AssertEquals("0x000000FF 0x00000000 mod 0x00000010", Int64Rem(x, y), Int64Create(0, 0)); + + x = Int64Create(0x00000000, 0xFF000000); + y = Int64Create(0x00000000, 0x00000010); + AssertEquals("0x00000000 0xFF000000 mod 0x00000010", Int64Rem(x, y), Int64Create(0, 0)); + + x = Int64Create(0xFF000000, 0x00000000); + y = Int64Create(0x00000000, 0x00000010); + AssertEquals("0xFF000000 0x00000000 mod 0x00000010", Int64Rem(x, y), Int64Create(0, 0)); + + x = Int64Create(0x00000000, 0x000000F0); + y = Int64Create(0x00000000, 0x00000010); + AssertEquals("0x00000000 0x000000F0 mod 0x00000010", Int64Rem(x, y), Int64Create(0, 0)); + + /* TODO: Add more tests for negative multiplication, division, and + * mod */ + + return 0; +} diff --git a/tools/uint64.c b/tools/uint64.c new file mode 100644 index 0000000..8e9f5f5 --- /dev/null +++ b/tools/uint64.c @@ -0,0 +1,119 @@ +#include + +#include + +/* AssertEquals(actual, expected) */ +int +AssertEquals(char *msg, UInt64 x, UInt64 y) +{ + if (!UInt64Eq(x, y)) + { + Log(LOG_ERR, "%s: Expected 0x%X 0x%X, got 0x%X 0x%X", msg, + UInt64High(y), UInt64Low(y), + UInt64High(x), UInt64Low(x)); + + return 0; + } + + return 1; +} + +int +Main(void) +{ + UInt64 x, y; + + Log(LOG_INFO, "sizeof(UInt64) = %lu", sizeof(UInt64)); + +#ifdef UINT64_NATIVE + Log(LOG_INFO, "Using native 64-bit integers."); +#else + Log(LOG_INFO, "Using emulated 64-bit integers."); +#endif + + /* BSR Tests */ + + x = UInt64Create(0x000000FF, 0x00000000); + + y = UInt64Srl(x, 4); + AssertEquals("x >> 4", y, UInt64Create(0x0000000F, 0xF0000000)); + + y = UInt64Srl(x, 8); + AssertEquals("x >> 8", y, UInt64Create(0x00000000, 0xFF000000)); + + y = UInt64Srl(x, 36); + AssertEquals("x >> 36", y, UInt64Create(0x00000000, 0x0000000F)); + + /* BSL Tests */ + + x = UInt64Create(0x00000000, 0xFF000000); + + y = UInt64Sll(x, 4); + AssertEquals("x << 4", y, UInt64Create(0x0000000F, 0xF0000000)); + + y = UInt64Sll(x, 8); + AssertEquals("x << 8", y, UInt64Create(0x000000FF, 0x00000000)); + + y = UInt64Sll(x, 36); + AssertEquals("x << 36", y, UInt64Create(0xF0000000, 0x00000000)); + + /* ADD Tests */ + + x = UInt64Create(0x00000000, 0xF0000001); + y = UInt64Create(0x00000000, 0x00000002); + AssertEquals("0xF0000001 + 0x00000002", UInt64Add(x, y), UInt64Create(0x00000000, 0xF0000003)); + + x = UInt64Create(0x00000000, 0xF0000000); + y = UInt64Create(0x00000000, 0x10000000); + AssertEquals("0xF0000000 + 0x10000000", UInt64Add(x, y), UInt64Create(0x00000001, 0x00000000)); + + /* SUB Tests */ + x = UInt64Create(0x00000000, 0x00000005); + y = UInt64Create(0x00000000, 0x00000002); + AssertEquals("0x00000005 - 0x00000002", UInt64Sub(x, y), UInt64Create(0x00000000, 0x00000003)); + + x = UInt64Create(0x00000001, 0x00000000); + y = UInt64Create(0x00000000, 0x00000001); + AssertEquals("0x00000001 0x00000000 - 0x00000001", UInt64Sub(x, y), UInt64Create(0x00000000, 0xFFFFFFFF)); + + /* MUL Tests */ + x = UInt64Create(0, 18); + y = UInt64Create(0, 1); + AssertEquals("18 * 1", UInt64Mul(x, y), UInt64Create(0, 18)); + + x = UInt64Create(0, 20); + y = UInt64Create(0, 12); + AssertEquals("20 * 12", UInt64Mul(x, y), UInt64Create(0, 240)); + + x = UInt64Create(0x00000000, 0x00000005); + y = UInt64Create(0x00000000, 0x00000005); + AssertEquals("0x00000005 * 0x00000005", UInt64Mul(x, y), UInt64Create(0x00000000, 0x00000019)); + + x = UInt64Create(0x00000001, 0x00000000); + y = UInt64Create(0x00000000, 0x00000005); + AssertEquals("0x00000001 0x00000000 * 0x00000005", UInt64Mul(x, y), UInt64Create(0x00000005, 0x00000000)); + + /* DIV Tests */ + x = UInt64Create(0, 12); + y = UInt64Create(0, 4); + AssertEquals("12 / 4", UInt64Div(x, y), UInt64Create(0, 3)); + + /* MOD Tests */ + x = UInt64Create(0x000000FF, 0x00000000); + y = UInt64Create(0x00000000, 0x00000010); + AssertEquals("0x000000FF 0x00000000 mod 0x00000010", UInt64Rem(x, y), UInt64Create(0, 0)); + + x = UInt64Create(0x00000000, 0xFF000000); + y = UInt64Create(0x00000000, 0x00000010); + AssertEquals("0x00000000 0xFF000000 mod 0x00000010", UInt64Rem(x, y), UInt64Create(0, 0)); + + x = UInt64Create(0xFF000000, 0x00000000); + y = UInt64Create(0x00000000, 0x00000010); + AssertEquals("0xFF000000 0x00000000 mod 0x00000010", UInt64Rem(x, y), UInt64Create(0, 0)); + + x = UInt64Create(0x00000000, 0x000000F0); + y = UInt64Create(0x00000000, 0x00000010); + AssertEquals("0x00000000 0x000000F0 mod 0x00000010", UInt64Rem(x, y), UInt64Create(0, 0)); + + return 0; +} From 138ea1c8e97aee040135176b75ada78cbac94c45 Mon Sep 17 00:00:00 2001 From: lda Date: Sun, 26 May 2024 22:06:56 +0200 Subject: [PATCH 2/2] [FIX] Oops. --- src/Int64.c | 399 ------------------------------------- src/UInt64.c | 265 ------------------------ src/include/Args.h | 82 -------- src/include/Array.h | 187 ----------------- src/include/Base64.h | 99 --------- src/include/Cron.h | 140 ------------- src/include/Db.h | 169 ---------------- src/include/Graph.h | 175 ---------------- src/include/HashMap.h | 185 ----------------- src/include/HeaderParser.h | 125 ------------ src/include/Http.h | 211 -------------------- src/include/HttpClient.h | 104 ---------- src/include/HttpRouter.h | 91 --------- src/include/HttpServer.h | 234 ---------------------- src/include/Int.h | 122 ------------ src/include/Int64.h | 252 ----------------------- src/include/Io.h | 222 --------------------- src/include/Json.h | 323 ------------------------------ src/include/Log.h | 196 ------------------ src/include/Memory.h | 235 ---------------------- src/include/Queue.h | 105 ---------- src/include/Rand.h | 81 -------- src/include/Runtime.h | 53 ----- src/include/Sha.h | 76 ------- src/include/Str.h | 129 ------------ src/include/Stream.h | 223 --------------------- src/include/Tls.h | 109 ---------- src/include/UInt64.h | 252 ----------------------- src/include/Uri.h | 69 ------- src/include/Util.h | 117 ----------- tools/int64.c | 145 -------------- tools/uint64.c | 119 ----------- 32 files changed, 5294 deletions(-) delete mode 100644 src/Int64.c delete mode 100644 src/UInt64.c delete mode 100644 src/include/Args.h delete mode 100644 src/include/Array.h delete mode 100644 src/include/Base64.h delete mode 100644 src/include/Cron.h delete mode 100644 src/include/Db.h delete mode 100644 src/include/Graph.h delete mode 100644 src/include/HashMap.h delete mode 100644 src/include/HeaderParser.h delete mode 100644 src/include/Http.h delete mode 100644 src/include/HttpClient.h delete mode 100644 src/include/HttpRouter.h delete mode 100644 src/include/HttpServer.h delete mode 100644 src/include/Int.h delete mode 100644 src/include/Int64.h delete mode 100644 src/include/Io.h delete mode 100644 src/include/Json.h delete mode 100644 src/include/Log.h delete mode 100644 src/include/Memory.h delete mode 100644 src/include/Queue.h delete mode 100644 src/include/Rand.h delete mode 100644 src/include/Runtime.h delete mode 100644 src/include/Sha.h delete mode 100644 src/include/Str.h delete mode 100644 src/include/Stream.h delete mode 100644 src/include/Tls.h delete mode 100644 src/include/UInt64.h delete mode 100644 src/include/Uri.h delete mode 100644 src/include/Util.h delete mode 100644 tools/int64.c delete mode 100644 tools/uint64.c diff --git a/src/Int64.c b/src/Int64.c deleted file mode 100644 index 21f07f5..0000000 --- a/src/Int64.c +++ /dev/null @@ -1,399 +0,0 @@ -/* - * Copyright (C) 2022-2023 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. - */ -#include - -#include -#include - -#include - -#ifdef INT64_NATIVE -#define Int64Sign(x) ((int) (((UInt64) (x)) >> 63)) -#else -#define Int64Sign(x) ((int) ((x).i[1] >> 31)) -#endif - -size_t -Int64Str(Int64 x, int base, char *out, size_t len) -{ - static const char symbols[] = "0123456789ABCDEF"; - - size_t i = len - 1; - size_t j = 0; - - int neg = Int64Sign(x); - - Int64 base64 = Int64Create(0, base); - - /* We only have symbols up to base 16 */ - if (base < 2 || base > 16) - { - return 0; - } - - /* - * This algorithm doesn't work on INT64_MIN. - * - * But it works on all other integers in the range, so we - * just scoot the range in by one for now. It's a hack and - * I'm not a huge fan of it, but this function is mostly - * used in Json, which shouldn't have a range this large - * anyway (Json is limited to -2^53 -> 2^53-1). - * - * Proper fixes are always welcome. - */ - if (Int64Eq(x, Int64Create(0x80000000, 0x00000000))) - { - x = Int64Add(x, Int64Create(0, 1)); - } -#if 0 - else if (Int64Eq(x, Int64Create(0x7FFFFFFF, 0xFFFFFFFF))) - { - x = Int64Sub(x, Int64Create(0, 1)); - } -#endif - - if (base != 2 && base != 8 && base != 16 && neg) - { - x = Int64Neg(x); - } - - do - { - Int64 mod = Int64Rem(x, base64); - Int32 low = Int64Low(mod); - - out[i] = symbols[low]; - i--; - x = Int64Div(x, base64); - } while (Int64Gt(x, Int64Create(0, 0))); - - if (base != 2 && base != 8 && base != 16) - { - /* - * Binary, octal, and hexadecimal are known to - * be bit representations. Everything else (notably - * decimal) should include the negative sign. - */ - if (neg) - { - out[i] = '-'; - i--; - } - } - - while (++i < len) - { - out[j++] = out[i]; - } - - out[j] = '\0'; - - return j; -} - -#ifndef INT64_NATIVE - -/* No native 64-bit support, add our own */ - -Int64 -Int64Create(UInt32 high, UInt32 low) -{ - Int64 x; - - x.i[0] = low; - x.i[1] = high; - - return x; -} - -Int64 -Int64Add(Int64 x, Int64 y) -{ - Int64 z = Int64Create(0, 0); - int carry; - - z.i[0] = x.i[0] + y.i[0]; - carry = z.i[0] < x.i[0]; - z.i[1] = x.i[1] + y.i[1] + carry; - - return z; -} - -Int64 -Int64Sub(Int64 x, Int64 y) -{ - return Int64Add(x, Int64Neg(y)); -} - -Int64 -Int64Mul(Int64 x, Int64 y) -{ - Int64 z = Int64Create(0, 0); - - int xneg = Int64Sign(x); - int yneg = Int64Sign(y); - - if (xneg) - { - x = Int64Neg(x); - } - - if (yneg) - { - y = Int64Neg(y); - } - - /* while (y > 0) */ - while (Int64Gt(y, Int64Create(0, 0))) - { - /* if (y & 1 != 0) */ - if (Int64Neq(Int64And(y, Int64Create(0, 1)), Int64Create(0, 0))) - { - z = Int64Add(z, x); - } - - x = Int64Sll(x, 1); - y = Int64Sra(y, 1); - } - - if (xneg != yneg) - { - z = Int64Neg(z); - } - - return z; -} - -typedef struct -{ - Int64 q; - Int64 r; -} Int64Ldiv; - -static Int64Ldiv -Int64LongDivision(Int64 n, Int64 d) -{ - Int64Ldiv o; - - int i; - - int nneg = Int64Sign(n); - int dneg = Int64Sign(d); - - o.q = Int64Create(0, 0); - o.r = Int64Create(0, 0); - - if (Int64Eq(d, Int64Create(0, 0))) - { - raise(SIGFPE); - return o; - } - - if (nneg) - { - n = Int64Neg(n); - } - - if (dneg) - { - d = Int64Neg(d); - } - - for (i = 63; i >= 0; i--) - { - Int64 bit = Int64And(Int64Sra(n, i), Int64Create(0, 1)); - - o.r = Int64Sll(o.r, 1); - o.r = Int64Or(o.r, bit); - - if (Int64Geq(o.r, d)) - { - o.r = Int64Sub(o.r, d); - o.q = Int64Or(o.q, Int64Sll(Int64Create(0, 1), i)); - } - } - - if (nneg != dneg) - { - o.r = Int64Neg(o.r); - o.q = Int64Neg(o.q); - } - - return o; -} - -Int64 -Int64Div(Int64 x, Int64 y) -{ - return Int64LongDivision(x, y).q; -} - -Int64 -Int64Rem(Int64 x, Int64 y) -{ - return Int64LongDivision(x, y).r; -} - -Int64 -Int64Sll(Int64 x, int y) -{ - Int64 z; - - if (!y) - { - return x; - } - - z = Int64Create(0, 0); - - if (y < 32) - { - z.i[1] = (x.i[0] >> (32 - y)) | (x.i[1] << y); - z.i[0] = x.i[0] << y; - } - else - { - z.i[1] = x.i[0] << (y - 32); - } - - return z; -} - -Int64 -Int64Sra(Int64 x, int y) -{ - Int64 z; - - int neg = Int64Sign(x); - - if (!y) - { - return x; - } - - z = Int64Create(0, 0); - - if (y < 32) - { - z.i[0] = (x.i[1] << (32 - y)) | (x.i[0] >> y); - z.i[1] = x.i[1] >> y; - } - else - { - z.i[0] = x.i[1] >> (y - 32); - } - - if (neg) - { - Int64 mask = Int64Create(0xFFFFFFFF, 0xFFFFFFFF); - - z = Int64Or(Int64Sll(mask, (64 - y)), z); - } - - return z; -} - -Int64 -Int64And(Int64 x, Int64 y) -{ - return Int64Create(x.i[1] & y.i[1], x.i[0] & y.i[0]); -} - -Int64 -Int64Or(Int64 x, Int64 y) -{ - return Int64Create(x.i[1] | y.i[1], x.i[0] | y.i[0]); -} - -Int64 -Int64Xor(Int64 x, Int64 y) -{ - return Int64Create(x.i[1] ^ y.i[1], x.i[0] ^ y.i[0]); -} - -Int64 -Int64Not(Int64 x) -{ - return Int64Create(~(x.i[1]), ~(x.i[0])); -} - -int -Int64Eq(Int64 x, Int64 y) -{ - return x.i[0] == y.i[0] && x.i[1] == y.i[1]; -} - -int -Int64Lt(Int64 x, Int64 y) -{ - int xneg = Int64Sign(x); - int yneg = Int64Sign(y); - - if (xneg != yneg) - { - return xneg > yneg; - } - else - { - if (xneg) - { - /* Both negative */ - return x.i[1] > y.i[1] || (x.i[1] == y.i[1] && x.i[0] > y.i[0]); - } - else - { - /* Both positive */ - return x.i[1] < y.i[1] || (x.i[1] == y.i[1] && x.i[0] < y.i[0]); - } - } -} - -int -Int64Gt(Int64 x, Int64 y) -{ - int xneg = Int64Sign(x); - int yneg = Int64Sign(y); - - if (xneg != yneg) - { - return xneg < yneg; - } - else - { - if (xneg) - { - /* Both negative */ - return x.i[1] < y.i[1] || (x.i[1] == y.i[1] && x.i[0] < y.i[0]); - } - else - { - /* Both positive */ - return x.i[1] > y.i[1] || (x.i[1] == y.i[1] && x.i[0] > y.i[0]); - } - } - -} - -#endif diff --git a/src/UInt64.c b/src/UInt64.c deleted file mode 100644 index ae8eff7..0000000 --- a/src/UInt64.c +++ /dev/null @@ -1,265 +0,0 @@ -/* - * Copyright (C) 2022-2023 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. - */ -#include - -#include -#include - -size_t -UInt64Str(UInt64 x, int base, char *out, size_t len) -{ - static const char symbols[] = "0123456789ABCDEF"; - - size_t i = len - 1; - size_t j = 0; - - UInt64 base64 = UInt64Create(0, base); - - /* We only have symbols up to base 16 */ - if (base < 2 || base > 16) - { - return 0; - } - - do - { - UInt64 mod = UInt64Rem(x, base64); - UInt32 low = UInt64Low(mod); - - out[i] = symbols[low]; - i--; - x = UInt64Div(x, base64); - } while (UInt64Gt(x, UInt64Create(0, 0))); - - while (++i < len) - { - out[j++] = out[i]; - } - - out[j] = '\0'; - - return j; -} - -#ifndef UINT64_NATIVE - -/* No native 64-bit support, add our own */ - -UInt64 -UInt64Create(UInt32 high, UInt32 low) -{ - UInt64 x; - - x.i[0] = low; - x.i[1] = high; - - return x; -} - -UInt64 -UInt64Add(UInt64 x, UInt64 y) -{ - UInt64 z = UInt64Create(0, 0); - int carry; - - z.i[0] = x.i[0] + y.i[0]; - carry = z.i[0] < x.i[0]; - z.i[1] = x.i[1] + y.i[1] + carry; - - return z; -} - -UInt64 -UInt64Sub(UInt64 x, UInt64 y) -{ - UInt64 twosCompl = UInt64Add(UInt64Not(y), UInt64Create(0, 1)); - - return UInt64Add(x, twosCompl); -} - -UInt64 -UInt64Mul(UInt64 x, UInt64 y) -{ - UInt64 z = UInt64Create(0, 0); - - /* while (y > 0) */ - while (UInt64Gt(y, UInt64Create(0, 0))) - { - /* if (y & 1 != 0) */ - if (UInt64Neq(UInt64And(y, UInt64Create(0, 1)), UInt64Create(0, 0))) - { - z = UInt64Add(z, x); - } - - x = UInt64Sll(x, 1); - y = UInt64Srl(y, 1); - } - - return z; -} - -typedef struct -{ - UInt64 q; - UInt64 r; -} UInt64Ldiv; - -static UInt64Ldiv -UInt64LongDivision(UInt64 n, UInt64 d) -{ - UInt64Ldiv o; - - int i; - - o.q = UInt64Create(0, 0); - o.r = UInt64Create(0, 0); - - if (UInt64Eq(d, UInt64Create(0, 0))) - { - raise(SIGFPE); - return o; - } - - for (i = 63; i >= 0; i--) - { - UInt64 bit = UInt64And(UInt64Srl(n, i), UInt64Create(0, 1)); - - o.r = UInt64Sll(o.r, 1); - o.r = UInt64Or(o.r, bit); - - if (UInt64Geq(o.r, d)) - { - o.r = UInt64Sub(o.r, d); - o.q = UInt64Or(o.q, UInt64Sll(UInt64Create(0, 1), i)); - } - } - - return o; -} - -UInt64 -UInt64Div(UInt64 x, UInt64 y) -{ - return UInt64LongDivision(x, y).q; -} - -UInt64 -UInt64Rem(UInt64 x, UInt64 y) -{ - return UInt64LongDivision(x, y).r; -} - -UInt64 -UInt64Sll(UInt64 x, int y) -{ - UInt64 z; - - if (!y) - { - return x; - } - - z = UInt64Create(0, 0); - - if (y < 32) - { - z.i[1] = (x.i[0] >> (32 - y)) | (x.i[1] << y); - z.i[0] = x.i[0] << y; - } - else - { - z.i[1] = x.i[0] << (y - 32); - } - - return z; -} - -UInt64 -UInt64Srl(UInt64 x, int y) -{ - UInt64 z; - - if (!y) - { - return x; - } - - z = UInt64Create(0, 0); - - if (y < 32) - { - z.i[0] = (x.i[1] << (32 - y)) | (x.i[0] >> y); - z.i[1] = x.i[1] >> y; - } - else - { - z.i[0] = x.i[1] >> (y - 32); - } - - return z; -} - -UInt64 -UInt64And(UInt64 x, UInt64 y) -{ - return UInt64Create(x.i[1] & y.i[1], x.i[0] & y.i[0]); -} - -UInt64 -UInt64Or(UInt64 x, UInt64 y) -{ - return UInt64Create(x.i[1] | y.i[1], x.i[0] | y.i[0]); -} - -UInt64 -UInt64Xor(UInt64 x, UInt64 y) -{ - return UInt64Create(x.i[1] ^ y.i[1], x.i[0] ^ y.i[0]); -} - -UInt64 -UInt64Not(UInt64 x) -{ - return UInt64Create(~(x.i[1]), ~(x.i[0])); -} - -int -UInt64Eq(UInt64 x, UInt64 y) -{ - return x.i[0] == y.i[0] && x.i[1] == y.i[1]; -} - -int -UInt64Lt(UInt64 x, UInt64 y) -{ - return x.i[1] < y.i[1] || (x.i[1] == y.i[1] && x.i[0] < y.i[0]); -} - -int -UInt64Gt(UInt64 x, UInt64 y) -{ - return x.i[1] > y.i[1] || (x.i[1] == y.i[1] && x.i[0] > y.i[0]); -} - -#endif diff --git a/src/include/Args.h b/src/include/Args.h deleted file mode 100644 index 3108de3..0000000 --- a/src/include/Args.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2022-2023 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_ARGS_H -#define CYTOPLASM_ARGS_H - -/*** - * @Nm Args - * @Nd Getopt-style argument parser that operates on arrays. - * @Dd May 12 2023 - * @Xr Array - * - * .Nm - * provides a simple argument parser in the style of - * .Xr getopt 3 . - * It exists because the runtime passes the program arguments as - * an Array, and it is often useful to parse the arguments to - * provide the standard command line interface. - */ - -#include - -/** - * All state is stored in this structure, instead of global - * variables. This makes - * .Nm - * thread-safe and easy to reset. - */ -typedef struct ArgParseState -{ - int optInd; - int optErr; - int optOpt; - char *optArg; - - int optPos; -} ArgParseState; - -/** - * Initialize the variables in the given parser state structure - * to their default values. This should be called before - * .Fn ArgParse - * is called with the parser state. It should also be called if - * .Fn ArgParse - * will be used again on a different array, or the same array all - * over again. - */ -extern void ArgParseStateInit(ArgParseState *); - -/** - * Parse command line arguments stored in the given array, using - * the given state and option string. This function behaves - * identically to the POSIX - * .Fn getopt - * function, and should be used in exactly the same way. Refer to - * your system's - * .Xr getopt 3 - * page for details. - */ -extern int ArgParse(ArgParseState *, Array *, const char *); - -#endif /* CYTOPLASM_ARGS_H */ diff --git a/src/include/Array.h b/src/include/Array.h deleted file mode 100644 index 0ba1335..0000000 --- a/src/include/Array.h +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Copyright (C) 2022-2023 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_ARRAY_H -#define CYTOPLASM_ARRAY_H - -/*** - * @Nm Array - * @Nd A simple dynamic array data structure. - * @Dd November 24 2022 - * @Xr HashMap Queue - * - * These functions implement a simple array data structure that is - * automatically resized as necessary when new values are added. This - * implementation does not actually store the values of the items in 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 these - * functions don't have to copy data, and thus don't care how big the - * data is. Furthermore, arbitrary data can be stored in the array. - * .Pp - * This array implementation is optimized for storage space and - * appending. Deletions are expensive in that all the items of the list - * above a deletion are moved down to fill the hole where the deletion - * occurred. Insertions are also expensive in that all the elements - * above the given index must be shifted up to make room for the new - * element. - * .Pp - * Due to these design choices, this array implementation is best - * suited to linear writing, and then linear or random reading. - */ - -#include -#include - -/** - * The functions in this API operate on an array structure which is - * opaque to the caller. - */ -typedef struct Array Array; - -/** - * Allocate a new array. This function returns a pointer that can be - * used with the other functions in this API, or NULL if there was an - * error allocating memory for the array. - */ -extern Array *ArrayCreate(void); - -/** - * Deallocate an array. Note that this function does not free any of - * the values stored in the array; it is the caller's job to manage the - * memory for each item. Typically, the caller would iterate over all - * the items in the array and free them before freeing the array. - */ -extern void ArrayFree(Array *); - -/** - * Get the size, in number of elements, of the given array. - */ -extern size_t ArraySize(Array *); - -/** - * Get the element at the specified index from the specified array. - * This function will return NULL if the array is NULL, or the index - * is out of bounds. Otherwise, it will return a pointer to a value - * put into the array using - * .Fn ArrayInsert - * or - * .Fn ArraySet . - */ -extern void *ArrayGet(Array *, size_t); - -/** - * Insert the specified element at the specified index in the specified - * array. This function will shift the element currently at that index, - * and any elements after it before inserting the given element. - * .Pp - * This function returns a boolean value indicating whether or not it - * suceeded. - */ -extern int ArrayInsert(Array *, size_t, void *); - -/** - * Set the value at the specified index in the specified array to the - * specified value. This function will return the old value at that - * index, if any. - */ -extern void *ArraySet(Array *, size_t, void *); - -/** - * Append the specified element to the end of the specified array. This - * function uses - * .Fn ArrayInsert - * under the hood to insert an element at the end. It thus has the same - * return value as - * .Fn ArrayInsert . - */ -extern int ArrayAdd(Array *, void *); - -/** - * Remove the element at the specified index from the specified array. - * This function returns the element removed, if any. - */ -extern void *ArrayDelete(Array *, size_t); - -/** - * Sort the specified array using the specified sort function. The - * sort function compares two elements. It takes two void pointers as - * parameters, and returns an integer. The return value indicates to - * the sorting algorithm how the elements relate to each other. A - * return value of 0 indicates that the elements 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. A return value less than 0 indicates the opposite: the - * second element should appear after the first in the array. - */ -extern void ArraySort(Array *, int (*) (void *, void *)); - -/** - * Remove all duplicates from an array by using the given comparison - * function to sort the array, then remove matching values. This - * function returns a new array with all duplicates removed. The - * original array is unaffected. Note that arrays only store pointers - * to values, usually values on the heap. Thus, it is possible to lose - * pointers to duplicate values and have them leak. - * .P - * This is a relatively expensive operation. The array must first be - * duplicated. Then it is sorted, then it is iterated from beginning - * to end to remove duplicate entires. Note that the comparison - * function is executed on each element at least twice. - */ -extern Array *ArrayUnique(Array *, int (*) (void *, void *)); - - -/** - * Reverses the order of all elements in the array into a new array on - * the heap, to be freed using - * .Fn ArrayFree . - */ -extern Array *ArrayReverse(Array *); - -/** - * If possible, reduce the amount of memory allocated to this array - * by calling - * .Fn Realloc - * on the internal structure to perfectly fit the elements in the - * array. This function is intended to be used by functions that return - * relatively read-only arrays that will be long-lived. - */ -extern int ArrayTrim(Array *); - -/** - * Convert a variadic arguments list into an Array. In most cases, the - * Array API is much easier to work with than - * .Fn va_arg - * and friends. - */ -extern Array *ArrayFromVarArgs(size_t, va_list); - -/** - * Duplicate an existing array. Note that arrays only hold pointers to - * their data, not the data itself, so the duplicated array will point - * to the same places in memory as the original array. - */ -extern Array *ArrayDuplicate(Array *); - -#endif /* CYTOPLASM_ARRAY_H */ diff --git a/src/include/Base64.h b/src/include/Base64.h deleted file mode 100644 index b36a48c..0000000 --- a/src/include/Base64.h +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) 2022-2023 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_BASE64_H -#define CYTOPLASM_BASE64_H - -/*** - * @Nm Base64 - * @Nd A simple base64 encoder/decoder with unpadded base64 support. - * @Dd September 30 2022 - * @Xr Sha2 - * - * This is an efficient yet simple base64 encoding and decoding API - * that supports regular base64, as well as the Matrix specification's - * extension to base64, called ``unpadded base64.'' This API provides - * the ability to convert between the two, instead of just implementing - * unpadded base64. - */ - -#include - -/** - * This function computes the amount of bytes needed to store a message - * of the specified number of bytes as base64. - */ -extern size_t - Base64EncodedSize(size_t); - -/** - * This function computes the amount of bytes needed to store a decoded - * representation of the encoded message. It takes a pointer to the - * encoded string because it must read a few bytes off the end in order - * to accurately compute the size. - */ -extern size_t - Base64DecodedSize(const char *, size_t); - -/** - * Encode the specified number of bytes from the specified buffer as - * base64. This function returns a string on the heap that should be - * freed with - * .Fn Free , - * or NULL if a memory allocation error ocurred. - */ -extern char * - Base64Encode(const char *, size_t); - -/** - * Decode the specified number of bytes from the specified buffer of - * base64. This function returns a string on the heap that should be - * freed with - * .Fn Free , - * or NULL if a memory allocation error occured. - */ -extern char * - Base64Decode(const char *, size_t); - -/** - * Remove the padding from a specified base64 string. This function - * modifies the specified string in place. It thus has no return value - * because it cannot fail. If the passed pointer is invalid, the - * behavior is undefined. - */ -extern void - Base64Unpad(char *, size_t); - -/** - * Add padding to an unpadded base64 string. This function takes a - * pointer to a pointer because it may be necessary to grow the memory - * allocated to the string. This function returns a boolean value - * indicating whether the pad operation was successful. In practice, - * this means it will only fail if a bigger string is necessary, but it - * could not be automatically allocated on the heap. - */ -extern int - Base64Pad(char **, size_t); - -#endif /* CYTOPLASM_BASE64_H */ diff --git a/src/include/Cron.h b/src/include/Cron.h deleted file mode 100644 index e78900d..0000000 --- a/src/include/Cron.h +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright (C) 2022-2023 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_CRON_H -#define CYTOPLASM_CRON_H - -/*** - * @Nm Cron - * @Nd Basic periodic job scheduler. - * @Dd December 24 2022 - * - * This is an extremely basic job scheduler. So basic, in fact, that - * it currently runs all jobs on a single thread, which means that it - * is intended for short-lived jobs. In the future, it might be - * extended to support a one-thread-per-job model, but for now, jobs - * should take into consideration the fact that they are sharing their - * thread, so they should not be long-running. - * .Pp - * .Nm - * works by ``ticking'' at an interval defined by the caller of - * .Fn CronCreate . - * At each tick, all the registered jobs are queried, and if they are - * due to run again, their function is executed. As much as possible, - * .Nm - * tries to tick at constant intervals, however it is possible that one - * or more jobs may overrun the tick duration. If this happens, - * .Nm - * ticks again immediately after all the jobs for the previous tick - * have completed. This is in an effort to compensate for lost time, - * however it is important to note that when jobs overrun the tick - * interval, the interval is pushed back by the amount that it was - * overrun. Because of this, - * .Nm - * is best suited for scheduling jobs that should happen - * ``aproximately'' every so often; it is not a real-time scheduler - * by any means. - */ - -#include - -/** - * All functions defined here operate on a structure opaque to the - * caller. - */ -typedef struct Cron Cron; - -/** - * A job function is a function that takes a void pointer and returns - * nothing. The pointer is passed when the job is scheduled, and - * is expected to remain valid until the job is no longer registered. - * The pointer is passed each time the job executes. - */ -typedef void (JobFunc) (void *); - -/** - * Create a new - * .Nm - * object that all other functions operate on. Like most of the other - * APIs in this project, it must be freed with - * .Fn CronFree - * when it is no longer needed. - * .Pp - * This function takes the tick interval in milliseconds. - */ -extern Cron * CronCreate(UInt32); - -/** - * Schedule a one-off job to be executed only at the next tick, and - * then immediately discarded. This is useful for scheduling tasks that - * only have to happen once, or very infrequently depending on - * conditions other than the current time, but don't have to happen - * immediately. The caller simply indicates that it wishes for the task - * to execute at some time in the future. How far into the future this - * practically ends up being is determined by how long it takes for - * other registered jobs to finish, and what the tick interval is. - * .Pp - * This function takes a job function and a pointer to pass to that - * function when it is executed. - */ -extern void - CronOnce(Cron *, JobFunc *, void *); - -/** - * Schedule a repetitive task to be executed at aproximately the given - * interval. As stated above, this is as fuzzy interval; depending on - * the jobs being run and the tick interval, tasks may not execute at - * exactly the scheduled time, but they will eventually execute. - * .Pp - * This function takes an interval in milliseconds, a job function, - * and a pointer to pass to that function when it is executed. - */ -extern void - CronEvery(Cron *, unsigned long, JobFunc *, void *); - -/** - * Start ticking the clock and executing registered jobs. - */ -extern void - CronStart(Cron *); - -/** - * Stop ticking the clock. Jobs that are already executing will - * continue to execute, but when they are finished, no new jobs will - * be executed until - * .Fn CronStart - * is called. - */ -extern void - CronStop(Cron *); - -/** - * Discard all job references and free all memory associated with the - * given - * .Nm Cron - * instance. - */ -extern void - CronFree(Cron *); - -#endif /* CYTOPLASM_CRON_H */ diff --git a/src/include/Db.h b/src/include/Db.h deleted file mode 100644 index 618a2e2..0000000 --- a/src/include/Db.h +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright (C) 2022-2023 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_DB_H -#define CYTOPLASM_DB_H - -/*** - * @Nm Db - * @Nd A minimal flat-file database with mutex locking and cache. - * @Dd April 27 2023 - * @Xr Json - * - * Cytoplasm operates on a flat-file database instead of a - * traditional relational database. This greatly simplifies the - * persistent storage code, and creates a relatively basic API, - * described here. - */ - -#include - -#include -#include - -/** - * All functions in this API operate on a database structure that is - * opaque to the caller. - */ -typedef struct Db Db; - -/** - * When an object is locked, a reference is returned. This reference - * is owned by the current thread, and the database is inaccessible to - * other threads until all references have been returned to the - * database. - * .Pp - * This reference is opaque, but can be manipulated by the functions - * defined here. - */ -typedef struct DbRef DbRef; - -/** - * Open a data directory. This function takes a path to open, and a - * cache size in bytes. If the cache size is 0, then caching is - * disabled and objects are loaded off the disk every time they are - * locked. Otherwise, objects are stored in the cache, and they are - * evicted in a least-recently-used manner. - */ -extern Db * DbOpen(char *, size_t); - -/** - * Close the database. This function will flush anything in the cache - * to the disk, and then close the data directory. It assumes that - * all references have been unlocked. If a reference has not been - * unlocked, undefined behavior results. - */ -extern void DbClose(Db *); - -/** - * Set the maximum cache size allowed before - * .Nm - * starts evicting old objects. If this is set to 0, everything in the - * cache is immediately evicted and caching is disabled. If the - * database was opened with a cache size of 0, setting this will - * initialize the cache, and subsequent calls to - * .Fn DbLock - * will begin caching objects. - */ -extern void DbMaxCacheSet(Db *, size_t); - -/** - * Create a new object in the database with the specified name. This - * function will fail if the object already exists in the database. It - * takes a variable number of C strings, with the exact number being - * specified by the second parameter. These C strings are used to - * generate a filesystem path at which to store the object. These paths - * ensure each object is uniquely identifiable, and provides semantic - * meaning to an object. - */ -extern DbRef * DbCreate(Db *, size_t,...); - -/** - * Lock an existing object in the database. This function will fail - * if the object does not exist. It takes a variable number of C - * strings, with the exact number being specified by the second - * parameter. These C strings are used to generate the filesystem path - * at which to load the object. These paths ensure each object is - * uniquely identifiable, and provides semantic meaning to an object. - */ -extern DbRef * DbLock(Db *, size_t,...); - -/** - * Immediately and permanently remove an object from the database. - * This function assumes the object is not locked, otherwise undefined - * behavior will result. - */ -extern int DbDelete(Db *, size_t,...); - -/** - * Unlock an object and return it back to the database. This function - * immediately syncs the object to the filesystem. The cache is a - * read cache; writes are always immediate to ensure data integrity in - * the event of a system failure. - */ -extern int DbUnlock(Db *, DbRef *); - -/** - * Check the existence of the given database object in a more efficient - * manner than attempting to lock it with - * .Fn DbLock . - * This function does not lock the object, nor does it load it into - * memory if it exists. - */ -extern int DbExists(Db *, size_t,...); - -/** - * List all of the objects at a given path. Unlike the other varargs - * functions, this one does not take a path to a specific object; it - * takes a directory to be iterated, where each path part is its own - * C string. Note that the resulting list only contains the objects - * in the specified directory, it does not list any subdirectories. - * .Pp - * The array returned is an array of C strings containing the object - * name. - */ -extern Array * DbList(Db *, size_t,...); - -/** - * Free the list returned by - * .Fn DbListFree . - */ -extern void DbListFree(Array *); - -/** - * Convert a database reference into JSON that can be manipulated. - * At this time, the database actually stores objects as JSON on the - * disk, so this function just returns an internal pointer, but in the - * future it may have to be generated by decompressing a binary blob, - * or something of that nature. - */ -extern HashMap * DbJson(DbRef *); - -/** - * Free the existing JSON associated with the given reference, and - * replace it with new JSON. This is more efficient than duplicating - * a separate object into the database reference. - */ -extern int DbJsonSet(DbRef *, HashMap *); - -#endif diff --git a/src/include/Graph.h b/src/include/Graph.h deleted file mode 100644 index 02e4e02..0000000 --- a/src/include/Graph.h +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright (C) 2022-2023 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_GRAPH_H -#define CYTOPLASM_GRAPH_H - -/*** - * @Nm Graph - * @Nd Extremely simple graph, implemented as an adjacency matrix. - * @Dd July 15 2023 - * - * .Nm - * is a basic graph data structure originally written for a computer - * science class on data structures and algorithms, in which it - * received full credit. This is an adaptation of the original - * implementation that follows the Cytoplasm style and uses Cytoplasm - * APIs when convenient. - * .P - * .Nm - * stores data in an adjacency matrix, which means the storage - * complexity is O(N^2), where N is the number of vertices (called - * Nodes in this implementation) in the graph. However, this makes the - * algorithms fast and efficient. - * .P - * Nodes are identified by index, so the first node is 0, the second - * is 1, and so on. This data structure does not support storing - * arbitrary data as nodes; rather, the intended use case is to add - * all your node data to an Array, thus giving each node an index, - * and then manipulating the graph with that index. This allows access - * to node data in O(1) time in call cases, and is the most memory - * efficient. - * .P - * .Nm - * can be used to store a variety of types of graphs, although it is - * primarily suited to directed and weighted graphs. - */ - -#include - -/** - * The functions provided here operate on an opaque graph structure. - * This structure really just stores a matrix in a contiguous block of - * memory, as well as the number of nodes in the graph, but the - * structure is kept opaque so that it remains internally consistent. - * It also maintains the style of the Cytoplasm library. - */ -typedef struct Graph Graph; - -/** - * An Edge is really just a weight, which is easily represented by an - * integer. However, it makes sense to alias this to Edge for clarity, - * both in the documentation and in the implementation. - */ -typedef int Edge; - -/** - * A Node is really just a row or column in the matrix, which is easily - * represented by an unsigned integer. However, it makes sense to alias - * this to Node for clarity, both in the documentation and the - * implementation. - */ -typedef size_t Node; - -/** - * Create a new graph structure with the given number of vertices. - */ -extern Graph *GraphCreate(size_t); - -/** - * Create a new graph data structure with the given number of vertices - * and the given adjacency matrix. The adjacency matrix is copied - * verbatim into the graph data structure without any validation. - */ -extern Graph *GraphCreateWithEdges(size_t, Edge *); - -/** - * Free all memory associated with the given graph. Since graphs are - * just a collection of numbers, they do not depend on each other in - * any way. - */ -extern void GraphFree(Graph *); - -/** - * Get the weight of the edge connecting the node specified first to - * the node specified second. If this is a directed graph, it does not - * necessarily follow that there is an edge from the node specified - * second to the node specified first. It also does not follow that - * such an edge, if it exists, has the same weight. - * .P - * This function will return -1 if the graph is invalid or either node - * is out of bounds. It will return 0 if there is no such edge from the - * node specified first to the node specified second. - */ -extern Edge GraphEdgeGet(Graph *, Node, Node); - -/** - * Set the weight of the edge connecting the node specified first to - * the node specified second. If this is not a directed graph, this - * function will have to be called twice, the second time reversing the - * order of the nodes. To remove the edge, specify a weight of 0. - */ -extern Edge GraphEdgeSet(Graph *, Node, Node, Edge); - -/** - * Get the number of nodes in the given graph. This operation is a - * simple memory access that happens in O(1) time. - */ -extern size_t GraphCountNodes(Graph *); - -/** - * Perform a breadth-first search on the given graph, starting at the - * specified node. This function returns a list of nodes in the order - * they were touched. The size of the list is stored in the unsigned - * integer pointed to by the last argument. - * .P - * If an error occurs, NULL will be returned. Otherwise, the returned - * pointer should be freed with the Memory API when it is no longer - * needed. - */ -extern Node * GraphBreadthFirstSearch(Graph *, Node, size_t *); - -/** - * Perform a depth-first search on the given graph, starting at the - * specified node. This function returns a list of nodes in the order - * they were touched. The size of the list is stored in the unsigned - * integer pointed to by the last argument. - * .P - * If an error occurs, NULL will be returned. Otherwise the returned - * pointer should be freed with the Memory API when it is no longer - * needed. - */ -extern Node *GraphDepthFirstSearch(Graph *, Node, size_t *); - -/** - * Perform a topological sort on the given graph. This function returns - * a list of nodes in topological ordering, though note that this is - * probably not the only topological ordering that exists for the - * graph. The size of the list is stored in the unsigned integer - * pointed to by the last argument. It should always be the number of - * nodes in the graph, but is provided for consistency and convenience. - * .P - * If an error occurs, NULL will be returned. Otherwise the returned - * pointer should be freed with the Memory API when it is no longer - * needed. - */ -extern Node *GraphTopologicalSort(Graph *, size_t *); - -/** - * Transpose the given graph, returning a brand new graph that is the - * result of the transposition. - */ -extern Graph * GraphTranspose(Graph *); - -#endif /* CYTOPLASM_GRAPH_H */ diff --git a/src/include/HashMap.h b/src/include/HashMap.h deleted file mode 100644 index 1359400..0000000 --- a/src/include/HashMap.h +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright (C) 2022-2023 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_HASHMAP_H -#define CYTOPLASM_HASHMAP_H - -/*** - * @Nm HashMap - * @Nd A simple hash map implementation. - * @Dd October 11 2022 - * @Xr Array Queue - * - * This is the public interface for Cytoplasm's hash map - * implementation. This hash map is designed to be simple, - * well-documented, and generally readable and understandable, yet also - * performant enough to be useful, because it is used extensively - * throughout the project. - * .Pp - * Fundamentally, this is an entirely generic map implementation. It - * can be used for many general purposes, but it is designed only to - * implement the features Cytoplasm needs to be functional. One - * example of a Cytoplasm-specific feature is that keys cannot be - * arbitrary data; they are NULL-terminated C strings. - */ - -#include - -#include - -/** - * These functions operate on an opaque structure, which the caller - * has no knowledge about. - */ -typedef struct HashMap HashMap; - -/** - * Create a new hash map that is ready to be used with the rest of the - * functions defined here. - */ -extern HashMap * HashMapCreate(void); - -/** - * Free the specified hash map such that it becomes invalid and any - * future use results in undefined behavior. Note that this function - * does not free the values stored in the hash map, but since it stores - * the keys internally, it will free the keys. You should use - * .Fn HashMapIterate - * to free the values stored in this map appropriately before calling - * this function. - */ -extern void HashMapFree(HashMap *); - -/** - * Control 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 they are full, because that - * makes them perform very poorly. The maximum load value is a - * percentage of how full the hash map is, and it should be between - * 0 and 1, where 0 means that no elements will cause the map to be - * expanded, and 1 means that the hash map must be completely full - * before it is expanded. The default maximum load on a new hash map - * is 0.75, which should be good enough for most purposes, however, - * this function exists specifically so that the maximum load can be - * fine-tuned. - */ -extern void HashMapMaxLoadSet(HashMap *, float); - -/** - * Use a custom hashing function with the given hash map. New hash - * maps have a sane hashing function that should work okay for most - * use cases, but if you have a better hashing function, it can be - * specified this way. Do not change the hash function after keys have - * been added; doing so results in undefined behavior. Only set a new - * hash function immediately after constructing a new hash map, before - * anything has been added to it. - * .Pp - * The hash function takes a pointer to a C string, and is expected - * to return a fairly unique numerical hash value which will be - * converted into an array index. - */ -extern void -HashMapFunctionSet(HashMap *, unsigned long (*) (const char *)); - -/** - * Set the given string key to the given value. Note that the key is - * copied into the hash map's own memory space, but the value is not. - * It is the caller's job to ensure that the value pointer remains - * valid for the life of the hash map, and are freed when no longer - * needed. - */ -extern void * HashMapSet(HashMap *, char *, void *); - -/** - * Retrieve the value for the given key, or return NULL if no such - * key exists in the hash map. - */ -extern void * HashMapGet(HashMap *, const char *); - -/** - * Remove a value specified by the given key from the hash map, and - * return it to the caller to deal with. This function returns NULL - * if no such key exists. - */ -extern void * HashMapDelete(HashMap *, const char *); - -/** - * Iterate over all the keys and values of a hash map. This function - * works very similarly to - * .Xr getopt 3 , - * 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; it takes pointer pointers, and stores all - * necessary state inside the hash map itself. - * .Pp - * Note that this function is not thread-safe; two threads cannot be - * iterating over any given hash map at the same time, though they - * can each be iterating over different hash maps. - * .Pp - * 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 to go through in the hash map. If you are not iterating - * over the entire map in one go, and happen to break the loop, then - * the next time you attempt to iterate the hash map, you'll start - * somewhere in the middle, which is most likely not the intended - * behavior. Thus, it is always recommended to iterate over the entire - * hash map if you're going to use this function. - * .Pp - * Also note that the behavior of this function is undefined if - * insertions or deletions occur during the iteration. This - * functionality has not been tested, and will likely not work. - */ -extern int HashMapIterate(HashMap *, char **, void **); - -/** - * A reentrant version of - * .Fn HashMapIterate - * that allows the caller to overcome the flaws of that function by - * storing the cursor outside of the hash map structure itself. This - * allows multiple threads to iterate over the same hash map at the - * same time, and it allows the iteration to be halted midway through - * without causing any unintended side effects. - * .Pp - * The cursor should be initialized to 0 at the start of iteration. - */ -extern int -HashMapIterateReentrant(HashMap *, char **, void **, size_t *); - -/** - * Collect the string keys of a hash map and return them as an array. - * The returned array holds pointers to the strings stored in the - * hash map, so the strings should NOT be freed; it is sufficient to - * free the array itself. Likewise, once the hash map is freed, the - * array elements are invalid and the array should be freed. - */ -extern Array * HashMapKeys(HashMap *); - -/** - * Collect the values of a hash map and return them as an array. The - * returned array holds the same pointers to the values as the hash - * map. - */ -extern Array * HashMapValues(HashMap *); - -#endif /* CYTOPLASM_HASHMAP_H */ diff --git a/src/include/HeaderParser.h b/src/include/HeaderParser.h deleted file mode 100644 index ffb3d0b..0000000 --- a/src/include/HeaderParser.h +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (C) 2022-2023 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_HEADERPARSER_H -#define CYTOPLASM_HEADERPARSER_H - -/*** - * @Nm HeaderParser - * @Nd Parse simple C header files. - * @Dd April 29 2023 - * - * .Nm - * is an extremely simple parser that lacks most of the functionality - * one would expect from a C code parser. It simply maps a stream - * of tokens into a few categories, parsing major ``milestones'' in - * a header, without actually understanding any of the details. - * .Pp - * This exists because it is used to generate man pages from headers. - * See - * .Xr hdoc 1 - * for example usage of this parser. - */ - -#include -#include - -#define HEADER_EXPR_MAX 4096 - -/** - * Headers are parsed as expressions. These are the expressions that - * this parser recognizes. - */ -typedef enum HeaderExprType -{ - HP_COMMENT, - HP_PREPROCESSOR_DIRECTIVE, - HP_TYPEDEF, - HP_DECLARATION, - HP_GLOBAL, - HP_UNKNOWN, - HP_SYNTAX_ERROR, - HP_PARSE_ERROR, - HP_EOF -} HeaderExprType; - -/** - * A representation of a function declaration. - */ -typedef struct HeaderDeclaration -{ - char returnType[64]; - char name[32]; /* Enforced by ANSI C */ - Array *args; -} HeaderDeclaration; - -/** - * A global variable declaration. The type must be of the same size - * as the function declaration's return type due to the way parsing - * them is implemented. - */ -typedef struct HeaderGlobal -{ - char type[64]; - char name[HEADER_EXPR_MAX - 64]; -} HeaderGlobal; - -/** - * A representation of a single header expression. Note that that state - * structure is entirely internally managed, so it should not be - * accessed or manipulated by functions outside the functions defined - * here. - * .Pp - * The type field should be used to determine which field in the data - * union is valid. - */ -typedef struct HeaderExpr -{ - HeaderExprType type; - union - { - char text[HEADER_EXPR_MAX]; - HeaderDeclaration declaration; - HeaderGlobal global; - struct - { - int lineNo; - char *msg; - } error; - } data; - - struct - { - Stream *stream; - int lineNo; - } state; -} HeaderExpr; - -/** - * Parse the next expression into the given header expression structure. - * To parse an entire C header, this function should be called in a - * loop until the type of the expression is HP_EOF. - */ -extern void HeaderParse(Stream *, HeaderExpr *); - -#endif /* CYTOPLASM_HEADERPARSER_H */ diff --git a/src/include/Http.h b/src/include/Http.h deleted file mode 100644 index d2de020..0000000 --- a/src/include/Http.h +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Copyright (C) 2022-2023 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_HTTP_H -#define CYTOPLASM_HTTP_H - -/*** - * @Nm Http - * @Nd Encode and decode various parts of the HTTP protocol. - * @Dd March 12 2023 - * @Xr HttpClient HttpServer HashMap Queue Memory - * - * .Nm - * is a collection of utility functions and type definitions that are - * useful for dealing with HTTP. HTTP is not a complex protocol, but - * this API makes it a lot easier to work with. - * .Pp - * Note that this API doesn't target any particular HTTP version, but - * it is currently used with HTTP 1.0 clients and servers, and - * therefore may be lacking functionality added in later HTTP versions. - */ - -#include - -#include -#include - -#define HTTP_FLAG_NONE 0 -#define HTTP_FLAG_TLS (1 << 0) - -/** - * The request methods defined by the HTTP standard. These numeric - * constants should be preferred to strings when building HTTP APIs - * because they are more efficient. - */ -typedef enum HttpRequestMethod -{ - HTTP_METHOD_UNKNOWN, - HTTP_GET, - HTTP_HEAD, - HTTP_POST, - HTTP_PUT, - HTTP_DELETE, - HTTP_CONNECT, - HTTP_OPTIONS, - HTTP_TRACE, - HTTP_PATCH -} HttpRequestMethod; - -/** - * An enumeration that corresponds to the actual integer values of the - * valid HTTP response codes. - */ -typedef enum HttpStatus -{ - HTTP_STATUS_UNKNOWN = 0, - - /* Informational responses */ - HTTP_CONTINUE = 100, - HTTP_SWITCHING_PROTOCOLS = 101, - HTTP_EARLY_HINTS = 103, - - /* Successful responses */ - HTTP_OK = 200, - HTTP_CREATED = 201, - HTTP_ACCEPTED = 202, - HTTP_NON_AUTHORITATIVE_INFORMATION = 203, - HTTP_NO_CONTENT = 204, - HTTP_RESET_CONTENT = 205, - HTTP_PARTIAL_CONTENT = 206, - - /* Redirection messages */ - HTTP_MULTIPLE_CHOICES = 300, - HTTP_MOVED_PERMANENTLY = 301, - HTTP_FOUND = 302, - HTTP_SEE_OTHER = 303, - HTTP_NOT_MODIFIED = 304, - HTTP_TEMPORARY_REDIRECT = 307, - HTTP_PERMANENT_REDIRECT = 308, - - /* Client error messages */ - HTTP_BAD_REQUEST = 400, - HTTP_UNAUTHORIZED = 401, - HTTP_FORBIDDEN = 403, - HTTP_NOT_FOUND = 404, - HTTP_METHOD_NOT_ALLOWED = 405, - HTTP_NOT_ACCEPTABLE = 406, - HTTP_PROXY_AUTH_REQUIRED = 407, - HTTP_REQUEST_TIMEOUT = 408, - HTTP_CONFLICT = 409, - HTTP_GONE = 410, - HTTP_LENGTH_REQUIRED = 411, - HTTP_PRECONDITION_FAILED = 412, - HTTP_PAYLOAD_TOO_LARGE = 413, - HTTP_URI_TOO_LONG = 414, - HTTP_UNSUPPORTED_MEDIA_TYPE = 415, - HTTP_RANGE_NOT_SATISFIABLE = 416, - HTTP_EXPECTATION_FAILED = 417, - HTTP_TEAPOT = 418, - HTTP_UPGRADE_REQUIRED = 426, - HTTP_PRECONDITION_REQUIRED = 428, - HTTP_TOO_MANY_REQUESTS = 429, - HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE = 431, - HTTP_UNAVAILABLE_FOR_LEGAL_REASONS = 451, - - /* Server error responses */ - HTTP_INTERNAL_SERVER_ERROR = 500, - HTTP_NOT_IMPLEMENTED = 501, - HTTP_BAD_GATEWAY = 502, - HTTP_SERVICE_UNAVAILABLE = 503, - HTTP_GATEWAY_TIMEOUT = 504, - HTTP_VERSION_NOT_SUPPORTED = 505, - HTTP_VARIANT_ALSO_NEGOTIATES = 506, - HTTP_NOT_EXTENDED = 510, - HTTP_NETWORK_AUTH_REQUIRED = 511 -} HttpStatus; - -/** - * Convert an HTTP status enumeration value into a string description - * of the status, which is to be used in server response to a client, - * or a client response to a user. For example, calling - * .Fn HttpStatusToString "HTTP_GATEWAY_TIMEOUT" - * (or - * .Fn HttpStatusToString "504" ) - * produces the string "Gateway Timeout". Note that the returned - * pointers point to static space, so their manipulation is forbidden. - */ -extern const char * HttpStatusToString(const HttpStatus); - -/** - * Convert a string into a numeric code that can be used throughout - * the code of a program in an efficient manner. See the definition - * of HttpRequestMethod. This function does case-sensitive matching, - * and does not trim or otherwise process the input string. - */ -extern HttpRequestMethod HttpRequestMethodFromString(const char *); - -/** - * Convert a numeric code as defined by HttpRequestMethod into a - * string that can be sent to a server. Note that the returned pointers - * point to static space, so their manipulation is forbidden. - */ -extern const char * HttpRequestMethodToString(const HttpRequestMethod); - -/** - * Encode a C string such that it can safely appear in a URL by - * performing the necessary percent escaping. A new string on the - * heap is returned. It should be freed with - * .Fn Free , - * defined in the - * .Xr Memory 3 - * API. - */ -extern char * HttpUrlEncode(char *); - -/** - * Decode a percent-encoded string into a C string, ignoring encoded - * null characters entirely, because those would do nothing but cause - * problems. - */ -extern char * HttpUrlDecode(char *); - -/** - * Decode an encoded parameter string in the form of - * ``key=val&key2=val2'' into a hash map whose values are C strings. - * This function properly decodes keys and values using the functions - * defined above. - */ -extern HashMap * HttpParamDecode(char *); - -/** - * Encode a hash map whose values are strings as an HTTP parameter - * string suitable for GET or POST requests. - */ -extern char * HttpParamEncode(HashMap *); - -/** - * Read HTTP headers from a stream and return a hash map whose values - * are strings. All keys are lowercased to make querying them - * consistent and not dependent on the case that was read from the - * stream. This is useful for both client and server code, since the - * headers are in the same format. This function should be used after - * parsing the HTTP status line, because it does not parse that line. - * It will stop when it encounters the first blank line, which - * indicates that the body is beginning. After this function completes, - * the body may be immediately read from the stream without any - * additional processing. - */ -extern HashMap * HttpParseHeaders(Stream *); - -#endif diff --git a/src/include/HttpClient.h b/src/include/HttpClient.h deleted file mode 100644 index f757041..0000000 --- a/src/include/HttpClient.h +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (C) 2022-2023 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_HTTPCLIENT_H -#define CYTOPLASM_HTTPCLIENT_H - -/*** - * @Nm HttpClient - * @Nd Extremely simple HTTP client. - * @Dd April 29 2023 - * @Xr Http HttpServer Tls - * - * .Nm - * HttpClient - * builds on the HTTP API to provide a simple yet functional HTTP - * client. It aims at being easy to use and minimal, yet also - * efficient. - */ - -#include - -#include -#include - -/** - * A server response is represented by a client context. It is - * opaque, so the functions defined in this API should be used to - * fetch data from and manipulate it. - */ -typedef struct HttpClientContext HttpClientContext; - -/** - * Make an HTTP request. This function takes the request method, - * any flags defined in the HTTP API, the port, hostname, and path, - * all in that order. It returns NULL if there was an error making - * the request. Otherwise it returns a client context. Note that this - * function does not actually send any data, it simply makes the - * connection. Use - * .Fn HttpRequestHeader - * to add headers to the request. Then, send headers with - * .Fn HttpRequestSendHeaders . - * Finally, the request body, if any, can be written to the output - * stream, and then the request can be fully sent using - * .Fn HttpRequestSend . - */ -extern HttpClientContext * - HttpRequest(HttpRequestMethod, int, unsigned short, char *, char *); - -/** - * Set a request header to send to the server when making the - * request. - */ -extern void HttpRequestHeader(HttpClientContext *, char *, char *); - -/** - * Send the request headers to the server. This must be called before - * the request body can be written or a response body can be read. - */ -extern void HttpRequestSendHeaders(HttpClientContext *); - -/** - * Flush the request stream to the server. This function should be - * called before the response body is read. - */ -extern HttpStatus HttpRequestSend(HttpClientContext *); - -/** - * Get the headers returned by the server. - */ -extern HashMap * HttpResponseHeaders(HttpClientContext *); - -/** - * Get the stream used to write the request body and read the - * response body. - */ -extern Stream * HttpClientStream(HttpClientContext *); - -/** - * Free all memory associated with the client context. This closes the - * connection, if it was still open. - */ -extern void HttpClientContextFree(HttpClientContext *); - -#endif /* CYTOPLASM_HTTPCLIENT_H */ diff --git a/src/include/HttpRouter.h b/src/include/HttpRouter.h deleted file mode 100644 index 6816178..0000000 --- a/src/include/HttpRouter.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) 2022-2023 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_HTTPROUTER_H -#define CYTOPLASM_HTTPROUTER_H - -/*** - * @Nm HttpRouter - * @Nd Simple HTTP request router with regular expression support. - * @Dd April 29 2023 - * @Xr HttpServer Http - * - * .Nm - * provides a simple mechanism for assigning functions to an HTTP - * request path. It is a simple tree data structure that parses the - * registered request paths and maps functions onto each part of the - * path. Then, requests can be easily routed to their appropriate - * handler functions. - */ - -#include - -/** - * The router structure is opaque and thus managed entirely by the - * functions defined in this API. - */ -typedef struct HttpRouter HttpRouter; - -/** - * A function written to handle an HTTP request takes an array - * consisting of the matched path parts in the order they appear in - * the path, and a pointer to caller-provided arguments, if any. - * It returns a pointer that the caller is assumed to know how to - * handle. - */ -typedef void *(HttpRouteFunc) (Array *, void *); - -/** - * Create a new empty routing tree. - */ -extern HttpRouter * HttpRouterCreate(void); - -/** - * Free all the memory associated with the given routing tree. - */ -extern void HttpRouterFree(HttpRouter *); - -/** - * Register the specified route function to be executed upon requests - * for the specified HTTP path. The path is parsed by splitting at - * each path separator. Each part of the path is a regular expression - * that matches the entire path part. A regular expression cannot - * match more than one path part. This allows for paths like - * .Pa /some/path/(.*)/parts - * to work as one would expect. - */ -extern int HttpRouterAdd(HttpRouter *, char *, HttpRouteFunc *); - -/** - * Route the specified request path using the specified routing - * tree. This function will parse the path and match it to the - * appropriate route handler function. The return value is a boolean - * value that indicates whether or not an appropriate route function - * was found. If an appropriate function was found, then the void - * pointer is passed to it as arguments that it is expected to know - * how to handle, and the pointer to a void pointer is where the - * route function's response will be placed. - */ -extern int HttpRouterRoute(HttpRouter *, char *, void *, void **); - -#endif /* CYTOPLASM_HTTPROUTER_H */ diff --git a/src/include/HttpServer.h b/src/include/HttpServer.h deleted file mode 100644 index f8e449a..0000000 --- a/src/include/HttpServer.h +++ /dev/null @@ -1,234 +0,0 @@ -/* - * Copyright (C) 2022-2023 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_HTTPSERVER_H -#define CYTOPLASM_HTTPSERVER_H - -/*** - * @Nm HttpServer - * @Nd Extremely simple HTTP server. - * @Dd December 13 2022 - * @Xr Http HttpClient - * - * .Nm - * builds on the - * .Xr Http 3 - * API, and provides a very simple, yet very functional API for - * creating an HTTP server. It aims at being easy to use and minimal, - * yet also efficient. It uses non-blocking I/O, is fully - * multi-threaded, and is very configurable. It can be set up in just - * two function calls and minimal supporting code. - * .Pp - * This API should be familar to those that have dealt with the HTTP - * server libraries of other programming languages, particularly Java. - * In fact, much of the terminology used in this API came from Java, - * and you'll notice that the way responses are sent and received very - * closely resembles Java. - */ - -#include - -#include - -#include -#include - -/** - * The functions on this API operate on an opaque structure. - */ -typedef struct HttpServer HttpServer; - -/** - * Each request receives a context structure. It is opaque, so the - * functions defined in this API should be used to fetch data from - * it. These functions allow the handler to figure out the context of - * the request, which includes the path requested, any parameters, - * and the headers and method used to make the request. The context - * also provides the means by which the handler responds to the - * request, allowing it to set the status code, headers, and body. - */ -typedef struct HttpServerContext HttpServerContext; - -/** - * The request handler function is executed when an HTTP request is - * received. It takes a request context, and a pointer as specified - * in the server configuration. - */ -typedef void (HttpHandler) (HttpServerContext *, void *); - -/** - * The number of arguments to - * .Fn HttpServerCreate - * has grown so large that arguments are now stuffed into a - * configuration structure, which is in turn passed to - * .Fn HttpServerCreate . - * This configuration is copied by value into the internal - * structures of the server. It is copied with very minimal - * validation, so ensure that all values are sensible. It may - * make sense to use - * .Fn memset - * to zero out everything in here before assigning values. - */ -typedef struct HttpServerConfig -{ - unsigned short port; - unsigned int threads; - unsigned int maxConnections; - - int flags; /* Http(3) flags */ - char *tlsCert; /* File path */ - char *tlsKey; /* File path */ - - HttpHandler *handler; - void *handlerArgs; -} HttpServerConfig; - -/** - * Create a new HTTP server using the specified configuration. - * This will set up all internal structures used by the server, - * and bind the socket and start listening for connections. However, - * it will not start accepting connections. - */ -extern HttpServer * HttpServerCreate(HttpServerConfig *); - -/** - * Retrieve the configuration that was used to instantiate the given - * server. Note that this configuration is not necessarily the exact - * one that was provided; even though its values are the same, it - * should be treated as an entirely separate configuration with no - * connection to the original. - */ -extern HttpServerConfig * HttpServerConfigGet(HttpServer *); - -/** - * Free the resources associated with the given HTTP server. Note that - * the server can only be freed after it has been stopped. Calling this - * function while the server is still running results in undefined - * behavior. - */ -extern void HttpServerFree(HttpServer *); - -/** - * Attempt to start the HTTP server, and return immediately with the - * status. This API is fully multi-threaded and asynchronous, so the - * caller can continue working while the HTTP server is running in a - * separate thread and managing a pool of threads to handle responses. - */ -extern int HttpServerStart(HttpServer *); - -/** - * Typically, at some point after calling - * .Fn HttpServerStart , - * the program will have no more work to do, so it will want to wait - * for the HTTP server to finish. This is accomplished via this - * function, which joins the HTTP worker thread to the calling thread, - * pausing the calling thread until the HTTP server has stopped. - */ -extern void HttpServerJoin(HttpServer *); - -/** - * Stop the HTTP server. Only the execution of this function will - * cause the proper shutdown of the HTTP server. If the main program - * is joined to the HTTP thread, then either another thread or a - * signal handler will have to stop the server using this function. - * The typical use case is to install a signal handler that executes - * this function on a global HTTP server. - */ -extern void HttpServerStop(HttpServer *); - -/** - * Get the request headers for the request represented by the given - * context. The data in the returned hash map should be treated as - * read only and should not be freed; it is managed entirely by the - * server. - */ -extern HashMap * HttpRequestHeaders(HttpServerContext *); - -/** - * Get the request method used to make the request represented by - * the given context. - */ -extern HttpRequestMethod HttpRequestMethodGet(HttpServerContext *); - -/** - * Get the request path for the request represented by the given - * context. The return value of this function should be treated as - * read-only, and should not be freed; it is managed entirely by the - * server. - */ -extern char * HttpRequestPath(HttpServerContext *); - -/** - * Retrieve the parsed GET parameters for the request represented by - * the given context. The returned hash map should be treated as - * read-only, and should not be freed; it is managed entirely by the - * server. - */ -extern HashMap * HttpRequestParams(HttpServerContext *); - -/** - * Set a response header to return to the client. The old value for - * the given header is returned, if any, otherwise NULL is returned. - */ -extern char * HttpResponseHeader(HttpServerContext *, char *, char *); - -/** - * Set the response status to return to the client. - */ -extern void HttpResponseStatus(HttpServerContext *, HttpStatus); - -/** - * Get the current response status that will be sent to the client - * making the request represented by the given context. - */ -extern HttpStatus HttpResponseStatusGet(HttpServerContext *); - -/** - * Send the response headers to the client that made the request - * represented by the specified context. This function must be called - * before the response body can be written, otherwise a malformed - * response will be sent. - */ -extern void HttpSendHeaders(HttpServerContext *); - -/** - * Get a stream that is both readable and writable. Reading from the - * stream reads the request body that the client sent, if there is one. - * Note that the rquest headers have already been read, so the stream - * is correctly positioned at the beginning of the body of the request. - * .Fn HttpSendHeaders - * must be called before the stream is written, otherwise a malformed - * HTTP response will be sent. An HTTP handler should properly set all - * the headers it itends to send, send those headers, and then write - * the response body to this stream. - * .Pp - * Note that the stream does not need to be closed by the HTTP - * handler; in fact doing so results in undefined behavior. The stream - * is managed entirely by the server itself, so it will close it when - * necessary. This allows the underlying protocol to differ: for - * instance, an HTTP/1.1 connection may stay for multiple requests and - * responses. - */ -extern Stream * HttpServerStream(HttpServerContext *); - -#endif /* CYTOPLASM_HTTPSERVER_H */ diff --git a/src/include/Int.h b/src/include/Int.h deleted file mode 100644 index 7933a69..0000000 --- a/src/include/Int.h +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (C) 2022-2023 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_INT_H -#define CYTOPLASM_INT_H - -/*** - * @Nm Int - * @Nd Fixed-width integer types. - * @Dd April 27 2023 - * - * This header provides cross-platform, fixed-width integer types. - * Specifically, it uses preprocessor magic to define the following - * types: - * .Bl -bullet -offset indent - * .It - * Int8 and UInt8 - * .It - * Int16 and UInt16 - * .It - * Int32 and UInt32 - * .El - * .Pp - * Note that there is no 64-bit integer type, because the ANSI C - * standard makes no guarantee that such a type will exist, even - * though it does on most platforms. - * .Pp - * The reason Cytoplasm provides its own header for this is - * because ANSI C does not define fixed-width types, and while it - * should be safe to rely on C99 fixed-width types in most cases, - * there may be cases where even that is not possible. - * - * @ignore-typedefs - */ - -#include - -#define BIT32_MAX 4294967295UL -#define BIT16_MAX 65535UL -#define BIT8_MAX 255UL - -#ifndef UCHAR_MAX -#error Size of char data type is unknown. Define UCHAR_MAX. -#endif - -#ifndef USHRT_MAX -#error Size of short data type is unknown. Define USHRT_MAX. -#endif - -#ifndef UINT_MAX -#error Size of int data type is unknown. Define UINT_MAX. -#endif - -#ifndef ULONG_MAX -#error Size of long data type is unknown. Define ULONG_MAX. -#endif - -#if UCHAR_MAX == BIT8_MAX -typedef signed char Int8; -typedef unsigned char UInt8; - -#else -#error Unable to determine suitable data type for 8-bit integers. -#endif - -#if UINT_MAX == BIT16_MAX -typedef signed int Int16; -typedef unsigned int UInt16; - -#elif USHRT_MAX == BIT16_MAX -typedef signed short Int16; -typedef unsigned short UInt16; - -#elif UCHAR_MAX == BIT16_MAX -typedef signed char Int16; -typedef unsigned char UInt16; - -#else -#error Unable to determine suitable data type for 16-bit integers. -#endif - -#if ULONG_MAX == BIT32_MAX -typedef signed long Int32; -typedef unsigned long UInt32; - -#elif UINT_MAX == BIT32_MAX -typedef signed int Int32; -typedef unsigned int UInt32; - -#elif USHRT_MAX == BIT32_MAX -typedef signed short Int32; -typedef unsigned short UInt32; - -#elif UCHAR_MAX == BIT32_MAX -typedef signed char Int32; -typedef unsigned char UInt32; - -#else -#error Unable to determine suitable data type for 32-bit integers. -#endif - -#endif /* CYTOPLASM_INT_H */ diff --git a/src/include/Int64.h b/src/include/Int64.h deleted file mode 100644 index 08083c1..0000000 --- a/src/include/Int64.h +++ /dev/null @@ -1,252 +0,0 @@ -/* - * Copyright (C) 2022-2023 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_INT64_H -#define CYTOPLASM_INT64_H - -/*** - * @Nm Int64 - * @Nd Fixed-width 64 bit integers. - * @Dd August 11, 2023 - * - * .Pp - * ANSI C89 (or C99 for that matter) provides no required mechanism - * for 64 bit integers. Nevertheless, many compilers provide them as - * extensions. However, since it is not a gaurantee, and to be fully - * standards-compliant and thus portable, a platform-agnostic interface - * is required. This header provides such an interface. If the platform - * has a 64 bit integer type, that is used, and native operations are - * performed by C preprocessor macro expansion. Otherwise, a - * compatibility layer is provided, which implements 64-bit - * arithmetic on an array of 2 32-bit numbers which are provided by - * .Xr Int 3 . - * .Pp - * Note that 64-bit emulation is certainly not as performant as using - * native 64-bit operations, so whenever possible, the native - * operations should be preferred. However, since C provides no required - * 64 bit integer on 32-bit and less platforms, this API can be used as - * a "good enough" fallback mechanism. - * .Pp - * Also note that this implementation, both in the native and - * non-native forms, makes some assumptions: - * .Bl -bullet -width Ds - * .It - * When a cast from a larger integer to a smaller integer is performed, - * the upper bits are truncated, not the lower bits. - * .It - * Negative numbers are represented in memory and in registers in two's - * compliment form. - * .El - * .Pp - * This API may provide unexpected output if these assumptions are - * false for a given platform. - * - * @ignore-typedefs - */ - -#include -#include - -#include - -#ifndef INT64_FORCE_EMULATED - -#define BIT64_MAX 18446744073709551615UL - -#if UINT_MAX == BIT64_MAX -typedef signed int Int64; - -#define INT64_NATIVE - -#elif ULONG_MAX == BIT64_MAX -typedef signed long Int64; - -#define INT64_NATIVE - -#endif - -#endif /* ifndef INT64_FORCE_EMULATED */ - -#ifdef INT64_NATIVE - -#define Int64Create(high, low) ((Int64) (((UInt64) (high) << 32) | (low))) -#define Int64Neg(x) (-(x)) - -#define Int64Low(a) ((UInt32) (a)) -#define Int64High(a) ((UInt32) ((a) >> 32)) - -#define Int64Add(a, b) ((a) + (b)) -#define Int64Sub(a, b) ((a) - (b)) -#define Int64Mul(a, b) ((a) * (b)) -#define Int64Div(a, b) ((a) / (b)) -#define Int64Rem(a, b) ((a) % (b)) - -#define Int64Sll(a, b) ((a) << (b)) -#define Int64Sra(a, b) ((a) >> (b)) - -#define Int64And(a, b) ((a) & (b)) -#define Int64Or(a, b) ((a) | (b)) -#define Int64Xor(a, b) ((a) ^ (b)) -#define Int64Not(a) (~(a)) - -#define Int64Eq(a, b) ((a) == (b)) -#define Int64Lt(a, b) ((a) < (b)) -#define Int64Gt(a, b) ((a) > (b)) - -#define Int64Neq(a, b) ((a) != (b)) -#define Int64Leq(a, b) ((a) <= (b)) -#define Int64Geq(a, b) ((a) >= (b)) - -#else - -#define Int64Neg(x) (Int64Add(Int64Not(x), Int64Create(0, 1))) - -/** - * The internal bit representation of a signed integer is identical - * to an unsigned integer, the difference is in the algorithms and - * the way the bits are interpreted. - */ -typedef UInt64 Int64; - -/** - * Create a new signed 64 bit integer using the given high and low - * bits. - */ -extern Int64 Int64Create(UInt32, UInt32); - -/** - * Add two signed 64 bit integers together. - */ -extern Int64 Int64Add(Int64, Int64); - -/** - * Subtract the second 64 bit integer from the first. - */ -extern Int64 Int64Sub(Int64, Int64); - -/** - * Multiply two 64 bit integers together. The non-native version of - * this function uses the Russian Peasant method of multiplication, - * which should afford more performance than a naive multiplication by - * addition, but it is still rather slow and depends on the size of - * the integers being multiplied. - */ -extern Int64 Int64Mul(Int64, Int64); - -/** - * Divide the first 64 bit integer by the second and return the - * quotient. The non-native version of this function uses naive binary - * long division, which is slow, but gauranteed to finish in constant - * time. - */ -extern Int64 Int64Div(Int64, Int64); - -/** - * Divide the first 64 bit integer by the second and return the - * remainder. The non-native version of this function uses naive binary - * long division, which is slow, but gauranteed to finish in constant - * time. - */ -extern Int64 Int64Rem(Int64, Int64); - -/** - * Perform a left logical bit shift of a 64 bit integer. The second - * parameter is how many places to shift, and is declared as a regular - * integer because anything more than 64 does not make sense. - */ -extern Int64 Int64Sll(Int64, int); - -/** - * Perform a right arithmetic bit shift of a 64 bit integer. The second - * parameter is how many places to shift, and is declared as a regular - * integer because anything more than 64 does not make sense. - * .Pp - * Note that on platforms that use the native 64-bit implementation, - * this is technically implementation-defined, and may in fact be a - * logical shift instead of an arithmetic shift. Note that typically - * this operation is not performed on signed integers. - */ -extern Int64 Int64Sra(Int64, int); - -/** - * Perform a bitwise AND (&) of the provided 64 bit integers. - */ -extern Int64 Int64And(Int64, Int64); - -/** - * Perform a bitwise OR (|) of the provided 64 bit integers. - */ -extern Int64 Int64Or(Int64, Int64); - -/** - * Perform a bitwise XOR (^) of the provided 64 bit integers. - */ -extern Int64 Int64Xor(Int64, Int64); - -/** - * Perform a bitwise NOT (~) of the provided 64 bit integer. - */ -extern Int64 Int64Not(Int64); - -/** - * Perform a comparison of the provided 64 bit integers and return a C - * boolean that is true if and only if they are equal. - */ -extern int Int64Eq(Int64, Int64); - -/** - * Perform a comparison of the provided 64 bit integers and return a C - * boolean that is true if and only if the second operand is strictly - * less than the first. - */ -extern int Int64Lt(Int64, Int64); - -/** - * Perform a comparison of the provided 64 bit integers and return a C - * boolean that is true if and only if the second operand is strictly - * greater than the first. - */ -extern int Int64Gt(Int64, Int64); - -#define Int64Low(a) ((a).i[0]) -#define Int64High(a) ((a).i[1]) - -#define Int64Neq(a, b) (!Int64Eq(a, b)) -#define Int64Leq(a, b) (Int64Eq(a, b) || Int64Lt(a, b)) -#define Int64Geq(a, b) (Int64Eq(a, b) || Int64Gt(a, b)) - -#endif - -#define INT64_STRBUF 65 /* Base 2 representation with '\0' */ - -/** - * Convert a 64 bit integer to a string in an arbitrary base - * representation specified by the second parameter, using the provided - * buffer and length specified by the third and fourth parameters. To - * guarantee that the string will fit in the buffer, allocate it of - * size INT64_STRBUF or larger. Note that a buffer size smaller than - * INT64_STRBUF will invoke undefined behavior. - */ -extern size_t Int64Str(Int64, int, char *, size_t); - -#endif /* CYTOPLASM_INT64_H */ diff --git a/src/include/Io.h b/src/include/Io.h deleted file mode 100644 index 06afb77..0000000 --- a/src/include/Io.h +++ /dev/null @@ -1,222 +0,0 @@ -/* - * Copyright (C) 2022-2023 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_IO_H -#define CYTOPLASM_IO_H - -/*** - * @Nm Io - * @Nd Source/sink-agnostic I/O for implementing custom streams. - * @Dd April 29 2023 - * @Xr Stream Tls - * - * Many systems provide platform-specific means of implementing custom - * streams using file pointers. However, POSIX does not define a way - * of programmatically creating custom streams. - * .Nm - * therefore fills this gap in POSIX by mimicking all of the - * functionality of these platform-specific functions, but in pure - * POSIX C. It defines a number of callback funtions to be executed - * in place of the standard POSIX I/O functions, which are used to - * implement arbitrary streams that may not be to a file or socket. - * Additionally, streams can now be pipelined; the sink of one stream - * may be the source of another lower-level stream. Additionally, all - * streams, regardless of their source or sink, share the same API, so - * streams can be handled in a much more generic manner. This allows - * the HTTP client and server libraries to seemlessly support TLS and - * plain connections without having to handle each separately. - * .Pp - * .Nm - * was heavily inspired by GNU's - * .Fn fopencookie - * and BSD's - * .Fn funopen . - * It aims to combine the best of both of these functions into a single - * API that is intuitive and easy to use. - */ - -#include -#include -#include -#include - -#ifndef IO_BUFFER -#define IO_BUFFER 4096 -#endif - -/** - * An opaque structure analogous to a POSIX file descriptor. - */ -typedef struct Io Io; - -/** - * Read input from the source of a stream. This function should - * attempt to read the specified number of bytes of data from the - * given cookie into the given buffer. It should behave identically - * to the POSIX - * .Xr read 2 - * system call, except instead of using an integer descriptor as the - * first parameter, a pointer to an implementation-defined cookie - * stores any information the function needs to read from the source. - */ -typedef ssize_t (IoReadFunc) (void *, void *, size_t); - -/** - * Write output to a sink. This function should attempt to write the - * specified number of bytes of data from the given buffer into the - * stream described by the given cookie. It should behave identically - * to the POSIX - * .Xr write 2 - * system call, except instead of using an integer descriptor as the - * first parameter, a pointer to an implementation-defined cookie - * stores any information the function needs to write to the sink. - */ -typedef ssize_t (IoWriteFunc) (void *, void *, size_t); - -/** - * Repositions the offset of the stream described by the specified - * cookie. This function should behave identically to the POSIX - * .Xr lseek 2 - * system call, except instead of using an integer descriptor as the - * first parameter, a pointer to an implementation-defined cookie - * stores any information the function needs to seek the stream. - */ -typedef off_t (IoSeekFunc) (void *, off_t, int); - -/** - * Close the given stream, making future reads or writes result in - * undefined behavior. This function should also free all memory - * associated with the cookie. It should behave identically to the - * .Xr close 2 - * system call, except instead of using an integer descriptor for the - * parameter, a pointer to an implementation-defined cookie stores any - * information the function needs to close the stream. - */ -typedef int (IoCloseFunc) (void *); - -/** - * A simple mechanism for grouping together a set of stream functions, - * to be passed to - * .Fn IoCreate . - */ -typedef struct IoFunctions -{ - IoReadFunc *read; - IoWriteFunc *write; - IoSeekFunc *seek; - IoCloseFunc *close; -} IoFunctions; - -/** - * Create a new stream using the specified cookie and the specified - * I/O functions. - */ -extern Io * IoCreate(void *, IoFunctions); - -/** - * Read the specified number of bytes from the specified stream into - * the specified buffer. This calls the stream's underlying IoReadFunc, - * which should behave identically to the POSIX - * .Xr read 2 - * system call. - */ -extern ssize_t IoRead(Io *, void *, size_t); - -/** - * Write the specified number of bytes from the specified stream into - * the specified buffer. This calls the stream's underlying - * IoWriteFunc, which should behave identically to the POSIX - * .Xr write 2 - * system call. - */ -extern ssize_t IoWrite(Io *, void *, size_t); - -/** - * Seek the specified stream using the specified offset and whence - * value. This calls the stream's underlying IoSeekFunc, which should - * behave identically to the POSIX - * .Xr lseek 2 - * system call. - */ -extern off_t IoSeek(Io *, off_t, int); - -/** - * Close the specified stream. This calls the stream's underlying - * IoCloseFunc, which should behave identically to the POSIX - * .Xr close 2 - * system call. - */ -extern int IoClose(Io *); - -/** - * Print a formatted string to the given stream. This is a - * re-implementation of the standard library function - * .Xr vfprintf 3 , - * and behaves identically. - */ -extern int IoVprintf(Io *, const char *, va_list); - -/** - * Print a formatted string to the given stream. This is a - * re-implementation of the standard library function - * .Xr fprintf 3 , - * and behaves identically. - */ -extern int IoPrintf(Io *, const char *,...); - -/** - * Read all the bytes from the first stream and write them to the - * second stream. Neither stream is closed upon the completion of this - * function. This can be used for quick and convenient buffered - * copying of data from one stream into another. - */ -extern ssize_t IoCopy(Io *, Io *); - -/** - * Wrap a POSIX file descriptor to take advantage of this API. The - * common use case for this function is when a regular file descriptor - * needs to be accessed by an application that uses this API to also - * access non-POSIX streams. - */ -extern Io * IoFd(int); - -/** - * Open or create a file for reading or writing. The specified file - * name is opened for reading or writing as specified by the given - * flags and mode. This function is a simple convenience wrapper around - * the POSIX - * .Xr open 2 - * system call that passes the opened file descriptor into - * .Fn IoFd . - */ -extern Io * IoOpen(const char *, int, mode_t); - -/** - * Wrap a standard C file pointer to take advantage of this API. The - * common use case for this function is when a regular C file pointer - * needs to be accessed by an application that uses this API to also - * access custom streams. - */ -extern Io * IoFile(FILE *); - -#endif /* CYTOPLASM_IO_H */ diff --git a/src/include/Json.h b/src/include/Json.h deleted file mode 100644 index 4185260..0000000 --- a/src/include/Json.h +++ /dev/null @@ -1,323 +0,0 @@ -/* - * Copyright (C) 2022-2023 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 -#include -#include -#include - -#include -#include - -#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(Int64); - -/** - * 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 Int64 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(int); - -/** - * 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 int 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 int 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 int 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 int 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,...); - -#endif /* CYTOPLASM_JSON_H */ diff --git a/src/include/Log.h b/src/include/Log.h deleted file mode 100644 index d0300ca..0000000 --- a/src/include/Log.h +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Copyright (C) 2022-2023 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_LOG_H -#define CYTOPLASM_LOG_H - -/*** - * @Nm Log - * @Nd A simple logging framework for logging to multiple destinations. - * @Dd April 27 2023 - * @Xr Stream - * - * .Nm - * is a simple C logging library that allows for colorful outputs, - * timestamps, and custom log levels. It also features the ability to - * have multiple logs open at one time, although Cytoplasm primarily - * utilizes the global log. All logs are thread safe. - */ - -#include -#include -#include - -#include - -#define LOG_FLAG_COLOR (1 << 0) -#define LOG_FLAG_SYSLOG (1 << 1) - -/** - * A log is defined as a configuration that describes the properties - * of the log. This opaque structure can be manipulated by the - * functions defined in this API. - */ -typedef struct LogConfig LogConfig; - -/** - * Create a new log configuration with sane defaults that can be used - * immediately with the logging functions. - */ -extern LogConfig * LogConfigCreate(void); - -/** - * Get the global log configuration, creating a new one with - * .Fn LogConfigCreate - * if necessary. - */ -extern LogConfig * LogConfigGlobal(void); - -/** - * Free the given log configuration. Note that this does not close the - * underlying stream associated with the log, if there is one. Also - * note that to avoid memory leaks, the global log configuration must - * also be freed, but it cannot be used after it is freed. - */ -extern void LogConfigFree(LogConfig *); - -/** - * Set the current log level on the specified log configuration. - * This indicates that only messages at or above this level should be - * logged; all others are silently discarded. The passed log level - * should be one of the log levels defined by - * .Xr syslog 3 . - * Refer to that page for a complete list of acceptable log levels, - * and note that passing an invalid log level will result in undefined - * behavior. - */ -extern void LogConfigLevelSet(LogConfig *, int); - -/** - * Cause the log output to be indented two more spaces than it was - * previously. This can be helpful when generating stack traces or - * other hierarchical output. This is a simple convenience wrapper - * around - * .Fn LogConfigIndentSet . - */ -extern void LogConfigIndent(LogConfig *); - -/** - * Cause the log output to be indented two less spaces than it was - * previously. This is a simple convenience wrapper around - * .Fn LogConfigIndentSet . - */ -extern void LogConfigUnindent(LogConfig *); - -/** - * Indent future log output using the specified config by some - * arbitrary amount. - */ -extern void LogConfigIndentSet(LogConfig *, size_t); - -/** - * Set the file stream that logging output should be written to. This - * defaults to standard output, but it can be set to standard error, - * or any other arbitrary stream. Passing a NULL value for the stream - * pointer sets the log output to the standard output. Note that the - * output stream is only used if - * .Va LOG_FLAG_SYSLOG - * is not set. - */ -extern void LogConfigOutputSet(LogConfig *, Stream *); - -/** - * Set a number of boolean options on a log configuration. This - * function uses bitwise operators, so multiple options can be set with - * a single function call using bitwise OR operators. The flags are - * defined as preprocessor macros, and are as follows: - * .Bl -tag -width Ds - * .It LOG_FLAG_COLOR - * When set, enable color-coded output on TTYs. Note that colors are - * implemented as ANSI escape sequences, and are not written to file - * streams that are not actually connected to a TTY, to prevent those - * sequences from being written to a file. - * .Xr isatty 3 - * is checked before writing any terminal sequences. - * .It LOG_FLAG_SYSLOG - * When set, log output to the syslog using - * .Xr syslog 3 , - * instead of logging to the file set by - * .Fn LogConfigOutputSet . - * This flag always overrides the stream set by that function, - * regardless of when it was set, even if it was set after this flag - * was set. - * .El - */ -extern void LogConfigFlagSet(LogConfig *, int); - -/** - * Clear a boolean flag from the specified log format. See above for - * the list of flags. - */ -extern void LogConfigFlagClear(LogConfig *, int); - -/** - * Set a custom timestamp to be prepended to each message if the - * output is not going to the system log. Consult your system's - * documentation for - * .Xr strftime 3 . - * A value of NULL disables the timestamp output before messages. - */ -extern void LogConfigTimeStampFormatSet(LogConfig *, char *); - -/** - * This function does the actual logging of messages using a - * specified configuration. It takes the configuration, the log - * level, a format string, and then a list of arguments, all in that - * order. This function only logs messages if their level is above - * or equal to the currently configured log level, making it easy to - * turn some messages on or off. - * .Pp - * This function has the same usage as - * .Xr vprintf 3 . - * Consult that page for the list of format specifiers and their - * arguments. This function is typically not used directly, see the - * other log functions for the most common use cases. - */ -extern void Logv(LogConfig *, int, const char *, va_list); - -/** - * Log a message using - * .Fn Logv . - * with the specified configuration. This function has the same usage - * as - * .Xr printf 3 . - */ -extern void LogTo(LogConfig *, int, const char *, ...); - -/** - * Log a message to the global log using - * .Fn Logv . - * This function has the same usage as - * .Xr printf 3 . - */ -extern void Log(int, const char *, ...); - -#endif diff --git a/src/include/Memory.h b/src/include/Memory.h deleted file mode 100644 index 5d5ecec..0000000 --- a/src/include/Memory.h +++ /dev/null @@ -1,235 +0,0 @@ -/* - * Copyright (C) 2022-2023 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_MEMORY_H -#define CYTOPLASM_MEMORY_H - -/*** - * @Nm Memory - * @Nd Smart memory management. - * @Dd January 9 2023 - * - * .Nm - * is an API that allows for smart memory management and profiling. It - * wraps the standard library functions - * .Xr malloc 3 , - * .Xr realloc 3 , - * and - * .Xr free 3 , - * and offers identical semantics, while providing functionality that - * the standard library doesn't have, such as getting statistics on the - * total memory allocated on the heap, and getting the size of a block - * given a pointer. Additionally, thanks to preprocessor macros, the - * exact file and line number at which an allocation, re-allocation, or - * free occured can be obtained given a pointer. Finally, all the - * blocks allocated on the heap can be iterated and evaluated, and a - * callback function can be executed every time a memory operation - * occurs. - * .Pp - * In the future, this API could include a garbage collector that - * automatically frees memory it detects as being no longer in use. - * However, this feature does not yet exist. - * .Pp - * A number of macros are available, which make the - * .Nm - * API much easier to use. They are as follows: - * .Bl -bullet -offset indent - * .It - * .Fn Malloc "x" - * .It - * .Fn Realloc "x" "y" - * .It - * .Fn Free "x" - * .El - * .Pp - * These macros expand to - * .Fn MemoryAllocate , - * .Fn MemoryReallocate , - * and - * .Fn MemoryFree - * with the second and third parameters set to __FILE__ and __LINE__. - * This allows - * .Nm - * to be used exactly how the standard library functions would be - * used. In fact, the functions to which these macros expand are not - * intended to be used directly; for the best results, use these - * macros. - */ -#include - -/** - * These values are passed into the memory hook function to indicate - * the action that just happened. - */ -typedef enum MemoryAction -{ - MEMORY_ALLOCATE, - MEMORY_REALLOCATE, - MEMORY_FREE, - MEMORY_BAD_POINTER, - MEMORY_CORRUPTED -} MemoryAction; - -#define Malloc(x) MemoryAllocate(x, __FILE__, __LINE__) -#define Realloc(x, s) MemoryReallocate(x, s, __FILE__, __LINE__) -#define Free(x) MemoryFree(x, __FILE__, __LINE__) - -/** - * The memory information is opaque, but can be accessed using the - * functions defined by this API. - */ -typedef struct MemoryInfo MemoryInfo; - -/** - * Allocate the specified number of bytes on the heap. This function - * has the same semantics as - * .Xr malloc 3 , - * except that it takes the file name and line number at which the - * allocation occurred. - */ -extern void * MemoryAllocate(size_t, const char *, int); - -/** - * Change the size of the object pointed to by the given pointer - * to the given number of bytes. This function has the same semantics - * as - * .Xr realloc 3 , - * except that it takes the file name and line number at which the - * reallocation occurred. - */ -extern void * MemoryReallocate(void *, size_t, const char *, int); - -/** - * Free the memory at the given pointer. This function has the same - * semantics as - * .Xr free 3 , - * except that it takes the file name and line number at which the - * free occurred. - */ -extern void MemoryFree(void *, const char *, int); - -/** - * Get the total number of bytes that the program has allocated on the - * heap. This operation iterates over all heap allocations made with - * .Fn MemoryAllocate - * and then returns a total count, in bytes. - */ -extern size_t MemoryAllocated(void); - -/** - * Iterate over all heap allocations made with - * .Fn MemoryAllocate - * and call - * .Fn MemoryFree - * on them. This function immediately invalidates all pointers to - * blocks on the heap, and any subsequent attempt to read or write to - * data on the heap will result in undefined behavior. This is - * typically called at the end of the program, just before exit. - */ -extern void MemoryFreeAll(void); - -/** - * Fetch information about an allocation. This function takes a raw - * pointer, and if - * . Nm - * knows about the pointer, it returns a structure that can be used - * to obtain information about the block of memory that the pointer - * points to. - */ -extern MemoryInfo * MemoryInfoGet(void *); - -/** - * Get the size in bytes of the block of memory represented by the - * specified memory info structure. - */ -extern size_t MemoryInfoGetSize(MemoryInfo *); - -/** - * Get the file name in which the block of memory represented by the - * specified memory info structure was allocated. - */ -extern const char * MemoryInfoGetFile(MemoryInfo *); - -/** - * Get the line number on which the block of memory represented by the - * specified memory info structure was allocated. - */ -extern int MemoryInfoGetLine(MemoryInfo *); - -/** - * Get a pointer to the block of memory represented by the specified - * memory info structure. - */ -extern void * MemoryInfoGetPointer(MemoryInfo *); - -/** - * This function takes a pointer to a function that takes the memory - * info structure, as well as a void pointer for caller-provided - * arguments. It iterates over all the heap memory currently allocated - * at the time of calling, executing the function on each allocation. - */ -extern void MemoryIterate(void (*) (MemoryInfo *, void *), void *); - -/** - * Specify a function to be executed whenever a memory operation - * occurs. The MemoryAction argument specifies the operation that - * occurred on the block of memory represented by the memory info - * structure. The function also takes a void pointer to caller-provided - * arguments. - */ -extern void MemoryHook(void (*) (MemoryAction, MemoryInfo *, void *), void *); - -/** - * The default memory hook, which has sane behavior and is installed - * at runtime. This function does not use any memory on the heap, - * except for the MemoryInfo passed to it, which it assumes to be - * valid. Everything else happens on the stack only, to ensure that - * the hook doesn't make any memory problems worse. - */ -extern void MemoryDefaultHook(MemoryAction, MemoryInfo *, void *); - -/** - * Read over the block of memory represented by the given memory info - * structure and generate a hexadecimal and ASCII string for each - * chunk of the block. This function takes a callback function that - * takes the following parameters in order: - * .Bl -bullet -offset indent - * .It - * The current offset from the beginning of the block of memory in - * bytes. - * .It - * A null-terminated string containing the next 16 bytes of the block - * encoded as space-separated hex values. - * .It - * A null-terminated string containing the ASCII representation of the - * same 16 bytes of memory. This ASCII representation is safe to print - * to a terminal or other text device, because non-printable characters - * are encoded as a . (period). - * .It - * Caller-passed pointer. - * .El - */ -extern void -MemoryHexDump(MemoryInfo *, void (*) (size_t, char *, char *, void *), void *); - -#endif diff --git a/src/include/Queue.h b/src/include/Queue.h deleted file mode 100644 index 35821d3..0000000 --- a/src/include/Queue.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (C) 2022-2023 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_QUEUE_H -#define CYTOPLASM_QUEUE_H - -/*** - * @Nm Queue - * @Nd A simple static queue data structure. - * @Dd November 25 2022 - * @Xr Array HashMap - * - * .Nm - * implements a simple queue data structure that is statically sized. - * This implementation does not actually store the values of the items - * in it; it only stores pointers to the data. As such, you will have - * to manually maintain data and make sure it remains valid as long as - * it is in the queue. The advantage of this is that - * .Nm - * doesn't have to copy data, and thus doesn't care how big the data - * is. Furthermore, any arbitrary data can be stored in the queue. - * .Pp - * This queue implementation operates on the heap. It is a circular - * queue, and it does not grow as it is used. Once the size is set, - * the queue never gets any bigger. - */ - -#include - -/** - * These functions operate on a queue structure that is opaque to the - * caller. - */ -typedef struct Queue Queue; - -/** - * Allocate a new queue that is able to store the specified number of - * items in it. - */ -extern Queue * QueueCreate(size_t); - -/** - * Free the memory associated with the specified queue structure. Note - * that this function does not free any of the values stored in the - * queue; it is the caller's job to manage memory for each item. - * Typically, the caller would dequeue all the items in the queue and - * deal with them before freeing the queue itself. - */ -extern void QueueFree(Queue *); - -/** - * Push an element into the queue. This function returns a boolean - * value indicating whether or not the push succeeded. Pushing items - * into the queue will fail if the queue is full. - */ -extern int QueuePush(Queue *, void *); - -/** - * Pop an element out of the queue. This function returns NULL if the - * queue is empty. Otherwise, it returns a pointer to the item that is - * next up in the queue. - */ -extern void * QueuePop(Queue *); - -/** - * Retrieve a pointer to the item that is next up in the queue without - * actually discarding it, such that the next call to - * .Fn QueuePeek - * or - * .Fn QueuePop - * will return the same pointer. - */ -extern void * QueuePeek(Queue *); - -/** - * Determine whether or not the queue is full. - */ -extern int QueueFull(Queue *); - -/** - * Determine whether or not the queue is empty. - */ -extern int QueueEmpty(Queue *); - -#endif diff --git a/src/include/Rand.h b/src/include/Rand.h deleted file mode 100644 index a5be3ce..0000000 --- a/src/include/Rand.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) 2022-2023 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_RAND_H -#define CYTOPLASM_RAND_H - -/*** - * @Nm Rand - * @Nd Thread-safe random numbers. - * @Dd February 16 2023 - * @Xr Util - * - * .Nm - * is used for generating random numbers in a thread-safe way. - * Currently, one generator state is shared across all threads, which - * means that only one thread can generate random numbers at a time. - * This state is protected with a mutex to guarantee this behavior. - * In the future, a seed pool may be maintained to allow multiple - * threads to generate random numbers at the same time. - * .Pp - * The generator state is seeded on the first call to a function that - * needs it. The seed is determined by the current timestamp, the ID - * of the process, and the thread ID. These should all be sufficiently - * random sources, so the seed should be secure enough. - * .Pp - * .Nm - * currently uses a simple Mersenne Twister algorithm to generate - * random numbers. This algorithm was chosen because it is extremely - * popular and widespread. While it is likely not cryptographically - * secure, and does suffer some unfortunate pitfalls, this algorithm - * has stood the test of time and is simple enough to implement, so - * it was chosen over the alternatives. - * .Pp - * .Nm - * does not use any random number generator functions from the C - * standard library, since these are often flawed. - */ - -#include - -/** - * Generate a single random integer between 0 and the passed value. - */ -extern int RandInt(unsigned int); - -/** - * Generate the number of integers specified by the second argument - * storing them into the buffer pointed to in the first argument. - * Ensure that each number is between 0 and the third argument. - * .Pp - * This function allows a caller to get multiple random numbers at once - * in a more efficient manner than repeatedly calling - * .Fn RandInt , - * since each call to these functions - * has to lock and unlock a mutex. It is therefore better to obtain - * multiple random numbers in one pass if multiple are needed. - */ -extern void RandIntN(int *, size_t, unsigned int); - -#endif /* CYTOPLASM_RAND_H */ diff --git a/src/include/Runtime.h b/src/include/Runtime.h deleted file mode 100644 index 6e13b6a..0000000 --- a/src/include/Runtime.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2022-2023 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_RUNTIME_H -#define CYTOPLASM_RUNTIME_H - -/*** - * @Nm Runtime - * @Nd Supporting functions for the Cytoplasm runtime. - * @Dd May 23 2023 - * @Xr Memory - * - * .Nm - * provides supporting functions for the Cytoplasm runtime. These - * functions are not intended to be called directly by programs, - * but are used internally. They're exposed via a header because - * the runtime stub needs to know their definitions. - */ - -#include - -/** - * Write a memory report to a file in the current directory, using - * the provided program arguments, including the program name that - * executed. This function is to be called after all memory is - * supposed to have been freed. It iterates over all remaining - * memory and generates a text file containing all of the - * recorded information about each block, including a hex dump of - * the data stored in them. - */ -extern void GenerateMemoryReport(int argc, char **argv); - -#endif /* CYTOPLASM_RUNTIME_H */ diff --git a/src/include/Sha.h b/src/include/Sha.h deleted file mode 100644 index 25b6247..0000000 --- a/src/include/Sha.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) 2022-2023 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_SHA_H -#define CYTOPLASM_SHA_H - -/*** - * @Nm Sha - * @Nd A simple implementation of a few SHA hashing functions. - * @Dd December 19 2022 - * @Xr Memory Base64 - * - * This API defines simple functions for computing SHA hashes. - * At the moment, it only defines - * .Fn Sha256 - * and - * .Fn Sha1 , - * which compute the SHA-256 and SHA-1 hashes of the given C string, - * respectively. It is not trivial to implement SHA-512 in ANSI C - * due to the lack of a 64-bit integer type, so that hash - * function has been omitted. - */ - -/** - * This function takes a pointer to a NULL-terminated C string, and - * returns a NULL-terminated byte buffer allocated on the heap using - * the Memory API, or NULL if there was an error allocating memory. - * The returned byte buffer should be freed when it is no longer - * needed. It is important to note that the returned buffer is not - * a printable string; to get a printable string, use - * .Fn ShaToHex . - */ -extern unsigned char * Sha256(char *); - -/** - * This function takes a pointer to a NULL-terminated C string, and - * returns a NULL-terminated byte buffer allocated on the heap using - * the Memory API, or NULL if there was an error allocating memory. - * The returned byte buffer should be freed when it is no longer - * needed. It is important to note that the returned buffer is not - * a printable string; to get a printable string, use - * .Fn ShaToHex . - */ -extern unsigned char * Sha1(char *); - -/** - * Convert a SHA byte buffer into a hex string. These hex strings - * are typically what is transmitted, stored, and compared, however - * there may be times when it is necessary to work with the raw - * bytes directly, which is why the conversion to a hex string is - * a separate step. - */ -extern char * ShaToHex(unsigned char *); - -#endif /* CYTOPLASM_SHA_H */ diff --git a/src/include/Str.h b/src/include/Str.h deleted file mode 100644 index 714b8d8..0000000 --- a/src/include/Str.h +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (C) 2022-2023 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_STR_H -#define CYTOPLASM_STR_H - -/*** - * @Nm Str - * @Nd Functions for creating and manipulating strings. - * @Dd February 15 2023 - * @Xr Memory - * - * .Nm - * provides string-related functions. It is called - * .Nm , - * not String, because some platforms (Windows) do not have - * case-sensitive filesystems, which poses a problem since - * .Pa string.h - * is a standard library header. - */ - -#include - -#include - -/** - * Convert UTF-16 into a Unicode codepoint. - */ -extern UInt32 StrUtf16Decode(UInt16, UInt16); - -/** - * Take a Unicode codepoint and encode it into a string buffer containing - * between 1 and 4 bytes. The string buffer is allocated on the heap, - * so it should be freed when it is no longer needed. - */ -extern char * StrUtf8Encode(UInt32); - -/** - * Duplicate a null-terminated string, returning a new string on the - * heap. This is useful when a function takes in a string that it needs - * to store for long amounts of time, even perhaps after the original - * string is gone. - */ -extern char * StrDuplicate(const char *); - -/** - * Extract part of a null-terminated string, returning a new string on - * the heap containing only the requested subsection. Like the - * substring functions included with most programming languages, the - * starting index is inclusive, and the ending index is exclusive. - */ -extern char * StrSubstr(const char *, size_t, size_t); - -/** - * A varargs function that takes a number of null-terminated strings - * specified by the first argument, and returns a new string that - * contains their concatenation. It works similarly to - * .Xr strcat 3 , - * but it takes care of allocating memory big enough to hold all the - * strings. Any string in the list may be NULL. If a NULL pointer is - * passed, it is treated like an empty string. - */ -extern char * StrConcat(size_t,...); - -/** - * Return a boolean value indicating whether or not the null-terminated - * string consists only of blank characters, as determined by - * .Xr isblank 3 . - */ -extern int StrBlank(const char *str); - -/** - * Generate a string of the specified length, containing random - * lowercase and uppercase letters. - */ -extern char * StrRandom(size_t); - -/** - * Convert the specified integer into a string, returning the string - * on the heap, or NULL if there was a memory allocation error. The - * returned string should be freed by the caller after it is no longer - * needed. - */ -extern char * StrInt(long); - -/** - * Converts a string into a lowercase version of it using - * .Xr tolower 3 , - * returning the lowercase version on the heap, or NULL if there was - * a memory allocation error. The returned string should be freed by - * the caller after it is no longer needed. - */ -extern char * StrLower(char *); - -/** - * Compare two strings and determine whether or not they are equal. - * This is the most common use case of strcmp() in Cytoplasm, but - * strcmp() doesn't like NULL pointers, so these have to be checked - * explicitly and can cause problems if they aren't. This function, - * on the other hand, makes NULL pointers special cases. If both - * arguments are NULL, then they are considered equal. If only one - * argument is NULL, they are considered not equal. Otherwise, if - * no arguments are NULL, a regular strcmp() takes place and this - * function returns a boolean value indicating whether or not - * strcmp() returned 0. - */ -extern int StrEquals(const char *, const char *); - -#endif /* CYTOPLASM_STR_H */ diff --git a/src/include/Stream.h b/src/include/Stream.h deleted file mode 100644 index 3ddacc6..0000000 --- a/src/include/Stream.h +++ /dev/null @@ -1,223 +0,0 @@ -/* - * Copyright (C) 2022-2023 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_STREAM_H -#define CYTOPLASM_STREAM_H - -/*** - * @Nm Stream - * @Nd An abstraction over the Io API that implements standard C I/O. - * @Dd April 29 2023 - * @Xr Io - * - * .Nm - * implements an abstraction layer over the Io API. This layer buffers - * I/O and makes it much easier to work with, mimicking the standard - * C library and offering some more convenience features. - */ - -#include - -#include - -/** - * An opaque structure analogous to C's FILE pointers. - */ -typedef struct Stream Stream; - -/** - * Create a new stream using the specified Io for underlying I/O - * operations. - */ -extern Stream * StreamIo(Io * io); - -/** - * Create a new stream using the specified POSIX file descriptor. - * This is a convenience function for calling - * .Fn IoFd - * and then passing the result into - * .Fn StreamIo . - */ -extern Stream * StreamFd(int); - -/** - * Create a new stream using the specified C FILE pointer. This is a - * convenience function for calling - * .Fn IoFile - * and then passing the result into - * .Fn StreamIo . - */ -extern Stream * StreamFile(FILE *); - -/** - * Create a new stream using the specified path and mode. This is a - * convenience function for calling - * .Xr fopen 3 - * and then passing the result into - * .Fn StreamFile . - */ -extern Stream * StreamOpen(const char *, const char *); - -/** - * Get a stream that writes to the standard output. - */ -extern Stream * StreamStdout(void); - -/** - * Get a stream that writes to the standard error. - */ -extern Stream * StreamStderr(void); - -/** - * Get a stream that reads from the standard input. - */ -extern Stream * StreamStdin(void); - -/** - * Close the stream. This flushes the buffers and closes the underlying - * Io. It is analogous to the standard - * .Xr fclose 3 - * function. - */ -extern int StreamClose(Stream *); - -/** - * Print a formatted string. This function is analogous to the standard - * .Xr vfprintf 3 - * function. - */ -extern int StreamVprintf(Stream *, const char *, va_list); - -/** - * Print a formatted string. This function is analogous to the - * standard - * .Xr fprintf 3 - * function. - */ -extern int StreamPrintf(Stream *, const char *,...); - -/** - * Get a single character from a stream. This function is analogous to - * the standard - * .Xr fgetc 3 - * function. - */ -extern int StreamGetc(Stream *); - -/** - * Push a character back onto the input stream. This function is - * analogous to the standard - * .Xr ungetc 3 - * function. - */ -extern int StreamUngetc(Stream *, int); - -/** - * Write a single character to the stream. This function is analogous - * to the standard - * .Xr fputc 3 - * function. - */ -extern int StreamPutc(Stream *, int); - -/** - * Write a null-terminated string to the stream. This function is - * analogous to the standard - * .Xr fputs 3 - * function. - */ -extern int StreamPuts(Stream *, char *); - -/** - * Read at most the specified number of characters minus 1 from the - * specified stream and store them at the memory located at the - * specified pointer. This function is analogous to the standard - * .Xr fgets 3 - * function. - */ -extern char * StreamGets(Stream *, char *, int); - -/** - * Set the file position indicator for the specified stream. This - * function is analogous to the standard - * .Xr fseeko - * function. - */ -extern off_t StreamSeek(Stream *, off_t, int); - -/** - * Test the end-of-file indicator for the given stream, returning a - * boolean value indicating whether or not it is set. This is analogous - * to the standard - * .Xr feof 3 - * function. - */ -extern int StreamEof(Stream *); - -/** - * Test the stream for an error condition, returning a boolean value - * indicating whether or not one is present. This is analogous to the - * standard - * .Xr ferror 3 - * function. - */ -extern int StreamError(Stream *); - -/** - * Clear the error condition associated with the given stream, allowing - * future reads or writes to potentially be successful. This functio - * is analogous to the standard - * .Xr clearerr 3 - * function. - */ -extern void StreamClearError(Stream *); - -/** - * Flush all buffered data using the streams underlying write function. - * This function is analogous to the standard - * .Xr fflush 3 - * function. - */ -extern int StreamFlush(Stream *); - -/** - * Read all the bytes from the first stream and write them to the - * second stream. This is analogous to - * .Fn IoCopy , - * but it uses the internal buffers of the streams. It is probably - * less efficient than doing a - * .Fn IoCopy - * instead, but it is more convenient. - */ -extern ssize_t StreamCopy(Stream *, Stream *); - -/** - * Get the file descriptor associated with the given stream, or -1 if - * the stream is not associated with any file descriptor. This function - * is analogous to the standard - * .Xr fileno 3 - * function. - */ -extern int StreamFileno(Stream *); - -#endif /* CYTOPLASM_STREAM_H */ diff --git a/src/include/Tls.h b/src/include/Tls.h deleted file mode 100644 index 4407ed0..0000000 --- a/src/include/Tls.h +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (C) 2022-2023 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_TLS_H -#define CYTOPLASM_TLS_H - -/*** - * @Nm Tls - * @Nd Interface to platform-dependent TLS libraries. - * @Dd April 29 2023 - * @Xr Stream Io - * - * .Nm - * provides an interface to platform-dependent TLS libraries. It allows - * Cytoplasm to support any TLS library with no changes to existing - * code. Support for additional TLS libraries is added by creating a - * new compilation unit that implements all the functions here, with - * the exception of a few, which are noted. - * .Pp - * Currently, Cytoplasm has support for the following TLS libraries: - * .Bl -bullet -offset indent - * .It - * LibreSSL - * .It - * OpenSSL - * .El - */ - -#include - -#define TLS_LIBRESSL 2 -#define TLS_OPENSSL 3 - -/** - * Create a new TLS client stream using the given file descriptor and - * the given server hostname. The hostname should be used to verify - * that the server actually is who it says it is. - * .Pp - * This function does not need to be implemented by the individual - * TLS support stubs. - */ -extern Stream * TlsClientStream(int, const char *); - -/** - * Create a new TLS server stream using the given certificate and key - * file, in the format natively supported by the TLS library. - * .Pp - * This function does not need to be implemented by the individual - * TLS support stubs. - */ -extern Stream * TlsServerStream(int, const char *, const char *); - -/** - * Initialize a cookie that stores information about the given client - * connection. This cookie will be passed into the other functions - * defined by this API. - */ -extern void * TlsInitClient(int, const char *); - -/** - * Initialize a cookie that stores information about the given - * server connection. This cookie will be passed into the other - * functions defined by this API. - */ -extern void * TlsInitServer(int, const char *, const char *); - -/** - * Read from a TLS stream, decrypting it and storing the result in the - * specified buffer. This function takes the cookie, buffer, and - * number of decrypted bytes to read into it. See the documentation for - * .Fn IoRead . - */ -extern ssize_t TlsRead(void *, void *, size_t); - -/** - * Write to a TLS stream, encrypting the buffer. This function takes - * the cookie, buffer, and number of unencrypted bytes to write to - * the stream. See the documentation for - * .Fn IoWrite . - */ -extern ssize_t TlsWrite(void *, void *, size_t); - -/** - * Close the TLS stream, also freeing all memory associated with the - * cookie. - */ -extern int TlsClose(void *); - -#endif /* CYTOPLASM_TLS_H */ diff --git a/src/include/UInt64.h b/src/include/UInt64.h deleted file mode 100644 index 51cc30c..0000000 --- a/src/include/UInt64.h +++ /dev/null @@ -1,252 +0,0 @@ -/* - * Copyright (C) 2022-2023 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_UINT64_H -#define CYTOPLASM_UINT64_H - -/*** - * @Nm UInt64 - * @Nd Fixed-width 64 bit integers. - * @Dd August 11, 2023 - * - * .Pp - * ANSI C89 (or C99 for that matter) provides no required mechanism - * for 64 bit integers. Nevertheless, many compilers provide them as - * extensions. However, since it is not a gaurantee, and to be fully - * standards-compliant and thus portable, a platform-agnostic interface - * is required. This header provides such an interface. If the platform - * has a 64 bit integer type, that is used, and native operations are - * performed by C preprocessor macro expansion. Otherwise, a - * compatibility layer is provided, which implements 64-bit - * arithmetic on an array of 2 32-bit numbers which are provided by - * .Xr Int 3 . - * .Pp - * Note that 64-bit emulation is certainly not as performant as using - * native 64-bit operations, so whenever possible, the native - * operations should be preferred. However, since C provides no required - * 64 bit integer on 32-bit and less platforms, this API can be used as - * a "good enough" fallback mechanism. - * .Pp - * Also note that this implementation, both in the native and - * non-native forms, makes some assumptions: - * .Bl -bullet -width Ds - * .It - * When a cast from a larger integer to a smaller integer is performed, - * the upper bits are truncated, not the lower bits. - * .It - * Negative numbers are represented in memory and in registers in two's - * compliment form. - * .El - * .Pp - * This API may provide unexpected output if these assumptions are - * false for a given platform. - * - * @ignore-typedefs - */ - -#include - -#include - -#ifndef INT64_FORCE_EMULATED - -#define BIT64_MAX 18446744073709551615UL - -#if UINT_MAX == BIT64_MAX -/* typedef signed int Int64; */ -typedef unsigned int UInt64; - -#define UINT64_NATIVE - -#elif ULONG_MAX == BIT64_MAX -/* typedef signed int Int64; */ -typedef unsigned long UInt64; - -#define UINT64_NATIVE - -#endif - -#endif /* ifndef INT64_FORCE_EMULATED */ - -#ifdef UINT64_NATIVE - -#define UInt64Create(high, low) (((UInt64) (high) << 32) | (low)) -#define UInt64Low(a) ((UInt32) ((a) & 0x00000000FFFFFFFF)) -#define UInt64High(a) ((UInt32) ((a) >> 32)) - -#define UInt64Add(a, b) ((a) + (b)) -#define UInt64Sub(a, b) ((a) - (b)) -#define UInt64Mul(a, b) ((a) * (b)) -#define UInt64Div(a, b) ((a) / (b)) -#define UInt64Rem(a, b) ((a) % (b)) - -#define UInt64Sll(a, b) ((a) << (b)) -#define UInt64Srl(a, b) ((a) >> (b)) - -#define UInt64And(a, b) ((a) & (b)) -#define UInt64Or(a, b) ((a) | (b)) -#define UInt64Xor(a, b) ((a) ^ (b)) -#define UInt64Not(a) (~(a)) - -#define UInt64Eq(a, b) ((a) == (b)) -#define UInt64Lt(a, b) ((a) < (b)) -#define UInt64Gt(a, b) ((a) > (b)) - -#define UInt64Neq(a, b) ((a) != (b)) -#define UInt64Leq(a, b) ((a) <= (b)) -#define UInt64Geq(a, b) ((a) >= (b)) - -#else - -/** - * For platforms that do not have a native integer large enough to - * store a 64 bit integer, this struct is used. i[0] contains the low - * bits of integer, and i[1] contains the high bits of the integer. - * .Pp - * This struct should not be accessed directly, because UInt64 may not - * actually be this struct, it might be an actual integer type. For - * maximum portability, only use the functions defined here to - * manipulate 64 bit integers. - */ -typedef struct -{ - UInt32 i[2]; -} UInt64; - -/** - * Create a new unsigned 64 bit integer using the given high and low - * bits. - */ -extern UInt64 UInt64Create(UInt32, UInt32); - -/** - * Add two unsigned 64 bit integers together. - */ -extern UInt64 UInt64Add(UInt64, UInt64); - -/** - * Subtract the second 64 bit integer from the first. - */ -extern UInt64 UInt64Sub(UInt64, UInt64); - -/** - * Multiply two 64 bit integers together. The non-native version of - * this function uses the Russian Peasant method of multiplication, - * which should afford more performance than a naive multiplication by - * addition, but it is still rather slow and depends on the size of - * the integers being multiplied. - */ -extern UInt64 UInt64Mul(UInt64, UInt64); - -/** - * Divide the first 64 bit integer by the second and return the - * quotient. The non-native version of this function uses naive binary - * long division, which is slow, but gauranteed to finish in constant - * time. - */ -extern UInt64 UInt64Div(UInt64, UInt64); - -/** - * Divide the first 64 bit integer by the second and return the - * remainder. The non-native version of this function uses naive binary - * long division, which is slow, but gauranteed to finish in constant - * time. - */ -extern UInt64 UInt64Rem(UInt64, UInt64); - -/** - * Perform a left logical bit shift of a 64 bit integer. The second - * parameter is how many places to shift, and is declared as a regular - * integer because anything more than 64 does not make sense. - */ -extern UInt64 UInt64Sll(UInt64, int); - -/** - * Perform a right logical bit shift of a 64 bit integer. The second - * parameter is how many places to shift, and is declared as a regular - * integer because anything more than 64 does not make sense. - */ -extern UInt64 UInt64Srl(UInt64, int); - -/** - * Perform a bitwise AND (&) of the provided 64 bit integers. - */ -extern UInt64 UInt64And(UInt64, UInt64); - -/** - * Perform a bitwise OR (|) of the provided 64 bit integers. - */ -extern UInt64 UInt64Or(UInt64, UInt64); - -/** - * Perform a bitwise XOR (^) of the provided 64 bit integers. - */ -extern UInt64 UInt64Xor(UInt64, UInt64); - -/** - * Perform a bitwise NOT (~) of the provided 64 bit integer. - */ -extern UInt64 UInt64Not(UInt64); - -/** - * Perform a comparison of the provided 64 bit integers and return a C - * boolean that is true if and only if they are equal. - */ -extern int UInt64Eq(UInt64, UInt64); - -/** - * Perform a comparison of the provided 64 bit integers and return a C - * boolean that is true if and only if the second operand is strictly - * less than the first. - */ -extern int UInt64Lt(UInt64, UInt64); - -/** - * Perform a comparison of the provided 64 bit integers and return a C - * boolean that is true if and only if the second operand is strictly - * greater than the first. - */ -extern int UInt64Gt(UInt64, UInt64); - -#define UInt64Low(a) ((a).i[0]) -#define UInt64High(a) ((a).i[1]) - -#define UInt64Neq(a, b) (!UInt64Eq(a, b)) -#define UInt64Leq(a, b) (UInt64Eq(a, b) || UInt64Lt(a, b)) -#define UInt64Geq(a, b) (UInt64Eq(a, b) || UInt64Gt(a, b)) - -#endif - -#define UINT64_STRBUF 65 /* Base 2 representation with '\0' */ - -/** - * Convert a 64 bit integer to a string in an arbitrary base - * representation specified by the second parameter, using the provided - * buffer and length specified by the third and fourth parameters. To - * guarantee that the string will fit in the buffer, allocate it of - * size UINT64_STRBUF or larger. Note that a buffer size smaller than - * UINT64_STRBUF will invoke undefined behavior. - */ -extern size_t UInt64Str(UInt64, int, char *, size_t); - -#endif /* CYTOPLASM_UINT64_H */ diff --git a/src/include/Uri.h b/src/include/Uri.h deleted file mode 100644 index a47bf28..0000000 --- a/src/include/Uri.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (C) 2022-2023 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_URI_H -#define CYTOPLASM_URI_H - -/*** - * @Nm Uri - * @Nd Parse a URI. Typically used to parse HTTP(s) URLs. - * @Dd April 29 2023 - * @Xr Http - * - * .Nm - * provides a simple mechanism for parsing URIs. This is an extremely - * basic parser that (ab)uses - * .Xr sscanf 3 - * to parse URIs, so it may not be the most reliable, but it should - * work in most cases and on reasonable URIs that aren't too long, as - * the _MAX definitions are modest. - */ - -#define URI_PROTO_MAX 8 -#define URI_HOST_MAX 128 -#define URI_PATH_MAX 256 - -/** - * The parsed URI is stored in this structure. - */ -typedef struct Uri -{ - char proto[URI_PROTO_MAX]; - char host[URI_HOST_MAX]; - char path[URI_PATH_MAX]; - unsigned short port; -} Uri; - -/** - * Parse a URI string into the Uri structure as described above, or - * return NULL if there was a parsing error. - */ -extern Uri * UriParse(const char *); - -/** - * Free the memory associated with a Uri structure returned by - * .Fn UriParse . - */ -extern void UriFree(Uri *); - -#endif /* CYTOPLASM_URI_H */ diff --git a/src/include/Util.h b/src/include/Util.h deleted file mode 100644 index d6c3830..0000000 --- a/src/include/Util.h +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (C) 2022-2023 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_UTIL_H -#define CYTOPLASM_UTIL_H - -/*** - * @Nm Util - * @Nd Some misc. helper functions that don't need their own headers. - * @Dd February 15 2023 - * - * This header holds a number of random functions related to strings, - * time, the filesystem, and other simple tasks that don't require a - * full separate API. For the most part, the functions here are - * entirely standalone, depending only on POSIX functions, however - * there are a few that depend explicitly on a few other APIs. Those - * are noted. - */ - -#include -#include -#include - -#include -#include - -/** - * Get the current timestamp in milliseconds since the Unix epoch. This - * uses - * .Xr gettimeofday 2 - * and time_t, and converts it to a single number, which is then - * returned to the caller. - * .Pp - * A note on the 2038 problem: as long as sizeof(time_t) >= 8, that is, - * as long as the time_t type is 64 bits or more, then everything - * should be fine. On most, if not, all, 64-bit systems, time_t is 64 - * bits. time_t is promoted to a 64-bit integer before it is converted - * to milliseconds, so there is no risk of overflue due to the - * multiplication by 1000. However, if time_t is only 32 bits, it will - * overflow before it even gets to this function, which will cause this - * function to produce unexpected results. - */ -extern UInt64 UtilServerTs(void); - -/** - * Use - * .Xr stat 2 - * to get the last modified time of the given file, or zero if there - * was an error getting the last modified time of a file. This is - * primarily useful for caching file data. - */ -extern UInt64 UtilLastModified(char *); - -/** - * This function behaves just like the system call - * .Xr mkdir 2 , - * but it creates any intermediate directories as necessary, unlike - * .Xr mkdir 2 . - */ -extern int UtilMkdir(const char *, const mode_t); - -/** - * Sleep the calling thread for the given number of milliseconds. - * POSIX does not have a very friendly way to sleep, so this wraps - * .Xr nanosleep 2 - * to make its usage much, much simpler. - */ -extern int UtilSleepMillis(UInt64); - -/** - * This function works identically to the POSIX - * .Xr getdelim 3 , - * except that it assumes pointers were allocated with the Memory API - * and it reads from a Stream instead of a file pointer. - */ -extern ssize_t UtilGetDelim(char **, size_t *, int, Stream *); - -/** - * This function is just a special case of - * .Fn UtilGetDelim - * that sets the delimiter to the newline character. - */ -extern ssize_t UtilGetLine(char **, size_t *, Stream *); - -/** - * Get a unique number associated with the current thread. - * Numbers are assigned in the order that threads call this - * function, but are guaranteed to be unique in identifying - * the thread in a more human-readable way than just casting - * the return value of - * .Fn pthread_self - * to a number. - */ -extern UInt32 UtilThreadNo(void); - -#endif /* CYTOPLASM_UTIL_H */ diff --git a/tools/int64.c b/tools/int64.c deleted file mode 100644 index fb9c5cf..0000000 --- a/tools/int64.c +++ /dev/null @@ -1,145 +0,0 @@ -#include - -#include - -/* AssertEquals(actual, expected) */ -int -AssertEquals(char *msg, Int64 x, Int64 y) -{ - if (!Int64Eq(x, y)) - { - Log(LOG_ERR, "%s: Expected 0x%X 0x%X, got 0x%X 0x%X", msg, - Int64High(y), Int64Low(y), - Int64High(x), Int64Low(x)); - - return 0; - } - - return 1; -} - -int -Main(void) -{ - Int64 x, y; - - Log(LOG_INFO, "sizeof(Int64) = %lu", sizeof(Int64)); - -#ifdef INT64_NATIVE - Log(LOG_INFO, "Using native 64-bit integers."); -#else - Log(LOG_INFO, "Using emulated 64-bit integers."); -#endif - - /* BSR Tests */ - - x = Int64Create(0x000000FF, 0x00000000); - - y = Int64Sra(x, 4); - AssertEquals("x >> 4", y, Int64Create(0x0000000F, 0xF0000000)); - - y = Int64Sra(x, 8); - AssertEquals("x >> 8", y, Int64Create(0x00000000, 0xFF000000)); - - y = Int64Sra(x, 36); - AssertEquals("x >> 36", y, Int64Create(0x00000000, 0x0000000F)); - - x = Int64Create(0xFF000000, 0x00000000); - - y = Int64Sra(x, 4); - AssertEquals("x >> 4", y, Int64Create(0xFFF00000, 0x00000000)); - - y = Int64Sra(x, 8); - AssertEquals("x >> 8", y, Int64Create(0xFFFF0000, 0x00000000)); - - y = Int64Sra(x, 63); - AssertEquals("x >> 63", y, Int64Create(0xFFFFFFFF, 0xFFFFFFFF)); - - /* BSL Tests */ - - x = Int64Create(0x00000000, 0xFF000000); - - y = Int64Sll(x, 4); - AssertEquals("x << 4", y, Int64Create(0x0000000F, 0xF0000000)); - - y = Int64Sll(x, 8); - AssertEquals("x << 8", y, Int64Create(0x000000FF, 0x00000000)); - - y = Int64Sll(x, 36); - AssertEquals("x << 36", y, Int64Create(0xF0000000, 0x00000000)); - - /* ADD Tests */ - - x = Int64Create(0x00000000, 0xF0000001); - y = Int64Create(0x00000000, 0x00000002); - AssertEquals("0xF0000001 + 0x00000002", Int64Add(x, y), Int64Create(0x00000000, 0xF0000003)); - - x = Int64Create(0x00000000, 0xF0000000); - y = Int64Create(0x00000000, 0x10000000); - AssertEquals("0xF0000000 + 0x10000000", Int64Add(x, y), Int64Create(0x00000001, 0x00000000)); - - x = Int64Create(0, 5); - y = Int64Neg(Int64Create(0, 10)); - AssertEquals("5 + (-10)", Int64Add(x, y), Int64Neg(Int64Create(0, 5))); - - /* SUB Tests */ - x = Int64Create(0x00000000, 0x00000005); - y = Int64Create(0x00000000, 0x00000002); - AssertEquals("0x00000005 - 0x00000002", Int64Sub(x, y), Int64Create(0x00000000, 0x00000003)); - - x = Int64Create(0x00000001, 0x00000000); - y = Int64Create(0x00000000, 0x00000001); - AssertEquals("0x00000001 0x00000000 - 0x00000001", Int64Sub(x, y), Int64Create(0x00000000, 0xFFFFFFFF)); - - x = Int64Create(0, 5); - y = Int64Create(0, 10); - AssertEquals("5 - 10", Int64Sub(x, y), Int64Neg(Int64Create(0, 5))); - - x = Int64Create(0, 5); - y = Int64Neg(Int64Create(0, 10)); - AssertEquals("5 - (-10)", Int64Sub(x, y), Int64Create(0, 15)); - - /* MUL Tests */ - x = Int64Create(0, 18); - y = Int64Create(0, 1); - AssertEquals("18 * 1", Int64Mul(x, y), Int64Create(0, 18)); - - x = Int64Create(0, 20); - y = Int64Create(0, 12); - AssertEquals("20 * 12", Int64Mul(x, y), Int64Create(0, 240)); - - x = Int64Create(0x00000000, 0x00000005); - y = Int64Create(0x00000000, 0x00000005); - AssertEquals("0x00000005 * 0x00000005", Int64Mul(x, y), Int64Create(0x00000000, 0x00000019)); - - x = Int64Create(0x00000001, 0x00000000); - y = Int64Create(0x00000000, 0x00000005); - AssertEquals("0x00000001 0x00000000 * 0x00000005", Int64Mul(x, y), Int64Create(0x00000005, 0x00000000)); - - /* DIV Tests */ - x = Int64Create(0, 12); - y = Int64Create(0, 4); - AssertEquals("12 / 4", Int64Div(x, y), Int64Create(0, 3)); - - /* MOD Tests */ - x = Int64Create(0x000000FF, 0x00000000); - y = Int64Create(0x00000000, 0x00000010); - AssertEquals("0x000000FF 0x00000000 mod 0x00000010", Int64Rem(x, y), Int64Create(0, 0)); - - x = Int64Create(0x00000000, 0xFF000000); - y = Int64Create(0x00000000, 0x00000010); - AssertEquals("0x00000000 0xFF000000 mod 0x00000010", Int64Rem(x, y), Int64Create(0, 0)); - - x = Int64Create(0xFF000000, 0x00000000); - y = Int64Create(0x00000000, 0x00000010); - AssertEquals("0xFF000000 0x00000000 mod 0x00000010", Int64Rem(x, y), Int64Create(0, 0)); - - x = Int64Create(0x00000000, 0x000000F0); - y = Int64Create(0x00000000, 0x00000010); - AssertEquals("0x00000000 0x000000F0 mod 0x00000010", Int64Rem(x, y), Int64Create(0, 0)); - - /* TODO: Add more tests for negative multiplication, division, and - * mod */ - - return 0; -} diff --git a/tools/uint64.c b/tools/uint64.c deleted file mode 100644 index 8e9f5f5..0000000 --- a/tools/uint64.c +++ /dev/null @@ -1,119 +0,0 @@ -#include - -#include - -/* AssertEquals(actual, expected) */ -int -AssertEquals(char *msg, UInt64 x, UInt64 y) -{ - if (!UInt64Eq(x, y)) - { - Log(LOG_ERR, "%s: Expected 0x%X 0x%X, got 0x%X 0x%X", msg, - UInt64High(y), UInt64Low(y), - UInt64High(x), UInt64Low(x)); - - return 0; - } - - return 1; -} - -int -Main(void) -{ - UInt64 x, y; - - Log(LOG_INFO, "sizeof(UInt64) = %lu", sizeof(UInt64)); - -#ifdef UINT64_NATIVE - Log(LOG_INFO, "Using native 64-bit integers."); -#else - Log(LOG_INFO, "Using emulated 64-bit integers."); -#endif - - /* BSR Tests */ - - x = UInt64Create(0x000000FF, 0x00000000); - - y = UInt64Srl(x, 4); - AssertEquals("x >> 4", y, UInt64Create(0x0000000F, 0xF0000000)); - - y = UInt64Srl(x, 8); - AssertEquals("x >> 8", y, UInt64Create(0x00000000, 0xFF000000)); - - y = UInt64Srl(x, 36); - AssertEquals("x >> 36", y, UInt64Create(0x00000000, 0x0000000F)); - - /* BSL Tests */ - - x = UInt64Create(0x00000000, 0xFF000000); - - y = UInt64Sll(x, 4); - AssertEquals("x << 4", y, UInt64Create(0x0000000F, 0xF0000000)); - - y = UInt64Sll(x, 8); - AssertEquals("x << 8", y, UInt64Create(0x000000FF, 0x00000000)); - - y = UInt64Sll(x, 36); - AssertEquals("x << 36", y, UInt64Create(0xF0000000, 0x00000000)); - - /* ADD Tests */ - - x = UInt64Create(0x00000000, 0xF0000001); - y = UInt64Create(0x00000000, 0x00000002); - AssertEquals("0xF0000001 + 0x00000002", UInt64Add(x, y), UInt64Create(0x00000000, 0xF0000003)); - - x = UInt64Create(0x00000000, 0xF0000000); - y = UInt64Create(0x00000000, 0x10000000); - AssertEquals("0xF0000000 + 0x10000000", UInt64Add(x, y), UInt64Create(0x00000001, 0x00000000)); - - /* SUB Tests */ - x = UInt64Create(0x00000000, 0x00000005); - y = UInt64Create(0x00000000, 0x00000002); - AssertEquals("0x00000005 - 0x00000002", UInt64Sub(x, y), UInt64Create(0x00000000, 0x00000003)); - - x = UInt64Create(0x00000001, 0x00000000); - y = UInt64Create(0x00000000, 0x00000001); - AssertEquals("0x00000001 0x00000000 - 0x00000001", UInt64Sub(x, y), UInt64Create(0x00000000, 0xFFFFFFFF)); - - /* MUL Tests */ - x = UInt64Create(0, 18); - y = UInt64Create(0, 1); - AssertEquals("18 * 1", UInt64Mul(x, y), UInt64Create(0, 18)); - - x = UInt64Create(0, 20); - y = UInt64Create(0, 12); - AssertEquals("20 * 12", UInt64Mul(x, y), UInt64Create(0, 240)); - - x = UInt64Create(0x00000000, 0x00000005); - y = UInt64Create(0x00000000, 0x00000005); - AssertEquals("0x00000005 * 0x00000005", UInt64Mul(x, y), UInt64Create(0x00000000, 0x00000019)); - - x = UInt64Create(0x00000001, 0x00000000); - y = UInt64Create(0x00000000, 0x00000005); - AssertEquals("0x00000001 0x00000000 * 0x00000005", UInt64Mul(x, y), UInt64Create(0x00000005, 0x00000000)); - - /* DIV Tests */ - x = UInt64Create(0, 12); - y = UInt64Create(0, 4); - AssertEquals("12 / 4", UInt64Div(x, y), UInt64Create(0, 3)); - - /* MOD Tests */ - x = UInt64Create(0x000000FF, 0x00000000); - y = UInt64Create(0x00000000, 0x00000010); - AssertEquals("0x000000FF 0x00000000 mod 0x00000010", UInt64Rem(x, y), UInt64Create(0, 0)); - - x = UInt64Create(0x00000000, 0xFF000000); - y = UInt64Create(0x00000000, 0x00000010); - AssertEquals("0x00000000 0xFF000000 mod 0x00000010", UInt64Rem(x, y), UInt64Create(0, 0)); - - x = UInt64Create(0xFF000000, 0x00000000); - y = UInt64Create(0x00000000, 0x00000010); - AssertEquals("0xFF000000 0x00000000 mod 0x00000010", UInt64Rem(x, y), UInt64Create(0, 0)); - - x = UInt64Create(0x00000000, 0x000000F0); - y = UInt64Create(0x00000000, 0x00000010); - AssertEquals("0x00000000 0x000000F0 mod 0x00000010", UInt64Rem(x, y), UInt64Create(0, 0)); - - return 0; -}