[FIX] Fix issue with const strings which may be invalidated
This commit is contained in:
parent
346b912a06
commit
cdf4430a9e
33 changed files with 5302 additions and 6 deletions
399
src/Int64.c
Normal file
399
src/Int64.c
Normal file
|
@ -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 <Int64.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include <Log.h>
|
||||
|
||||
#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
|
14
src/Memory.c
14
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;
|
||||
|
|
265
src/UInt64.c
Normal file
265
src/UInt64.c
Normal file
|
@ -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 <UInt64.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <signal.h>
|
||||
|
||||
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
|
82
src/include/Args.h
Normal file
82
src/include/Args.h
Normal file
|
@ -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 <Array.h>
|
||||
|
||||
/**
|
||||
* 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 */
|
187
src/include/Array.h
Normal file
187
src/include/Array.h
Normal file
|
@ -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 <stddef.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
/**
|
||||
* 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 */
|
99
src/include/Base64.h
Normal file
99
src/include/Base64.h
Normal file
|
@ -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 <stddef.h>
|
||||
|
||||
/**
|
||||
* 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 */
|
140
src/include/Cron.h
Normal file
140
src/include/Cron.h
Normal file
|
@ -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 <Int.h>
|
||||
|
||||
/**
|
||||
* 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 */
|
169
src/include/Db.h
Normal file
169
src/include/Db.h
Normal file
|
@ -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 <stddef.h>
|
||||
|
||||
#include <HashMap.h>
|
||||
#include <Array.h>
|
||||
|
||||
/**
|
||||
* 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
|
175
src/include/Graph.h
Normal file
175
src/include/Graph.h
Normal file
|
@ -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 <stddef.h>
|
||||
|
||||
/**
|
||||
* 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 */
|
185
src/include/HashMap.h
Normal file
185
src/include/HashMap.h
Normal file
|
@ -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 <stddef.h>
|
||||
|
||||
#include <Array.h>
|
||||
|
||||
/**
|
||||
* 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 */
|
125
src/include/HeaderParser.h
Normal file
125
src/include/HeaderParser.h
Normal file
|
@ -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 <Stream.h>
|
||||
#include <Array.h>
|
||||
|
||||
#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 */
|
211
src/include/Http.h
Normal file
211
src/include/Http.h
Normal file
|
@ -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 <stdio.h>
|
||||
|
||||
#include <HashMap.h>
|
||||
#include <Stream.h>
|
||||
|
||||
#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
|
104
src/include/HttpClient.h
Normal file
104
src/include/HttpClient.h
Normal file
|
@ -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 <stdio.h>
|
||||
|
||||
#include <HashMap.h>
|
||||
#include <Http.h>
|
||||
|
||||
/**
|
||||
* 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 */
|
91
src/include/HttpRouter.h
Normal file
91
src/include/HttpRouter.h
Normal file
|
@ -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 <Array.h>
|
||||
|
||||
/**
|
||||
* 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 */
|
234
src/include/HttpServer.h
Normal file
234
src/include/HttpServer.h
Normal file
|
@ -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 <Http.h>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <HashMap.h>
|
||||
#include <Stream.h>
|
||||
|
||||
/**
|
||||
* 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 */
|
122
src/include/Int.h
Normal file
122
src/include/Int.h
Normal file
|
@ -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 <limits.h>
|
||||
|
||||
#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 */
|
252
src/include/Int64.h
Normal file
252
src/include/Int64.h
Normal file
|
@ -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 <Int.h>
|
||||
#include <UInt64.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#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 */
|
222
src/include/Io.h
Normal file
222
src/include/Io.h
Normal file
|
@ -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 <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#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 */
|
323
src/include/Json.h
Normal file
323
src/include/Json.h
Normal file
|
@ -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 <HashMap.h>
|
||||
#include <Array.h>
|
||||
#include <Stream.h>
|
||||
#include <Int64.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#define JSON_DEFAULT -1
|
||||
#define JSON_PRETTY 0
|
||||
|
||||
/**
|
||||
* This opaque structure encapsulates all the possible types that can
|
||||
* be stored in JSON. It is managed entirely by the functions defined
|
||||
* in this API. It is important to note that strings, integers, floats,
|
||||
* booleans, and the NULL value are all stored by value, but objects
|
||||
* and arrays are stored by reference. That is, it doesn't store these
|
||||
* itself, just pointers to them, however, the data
|
||||
* .Em is
|
||||
* freed when using
|
||||
* .Fn JsonFree .
|
||||
*/
|
||||
typedef struct JsonValue JsonValue;
|
||||
|
||||
/**
|
||||
* These are the types that can be used to identify a JsonValue
|
||||
* and act on it accordingly.
|
||||
*/
|
||||
typedef enum JsonType
|
||||
{
|
||||
JSON_NULL, /* Maps to a C NULL */
|
||||
JSON_OBJECT, /* Maps to a HashMap of JsonValues */
|
||||
JSON_ARRAY, /* Maps to an Array of JsonValues */
|
||||
JSON_STRING, /* Maps to a null-terminated C string */
|
||||
JSON_INTEGER, /* Maps to an Int64 */
|
||||
JSON_FLOAT, /* Maps to a C double */
|
||||
JSON_BOOLEAN /* Maps to a C integer of either 0 or 1 */
|
||||
} JsonType;
|
||||
|
||||
/**
|
||||
* Determine the type of the specified JSON value.
|
||||
*/
|
||||
extern JsonType JsonValueType(JsonValue *);
|
||||
|
||||
/**
|
||||
* Encode a JSON object as a JSON value that can be added to another
|
||||
* object, or an array.
|
||||
*/
|
||||
extern JsonValue * JsonValueObject(HashMap *);
|
||||
|
||||
/**
|
||||
* Unwrap a JSON value that represents an object. This function will
|
||||
* return NULL if the value is not actually an object.
|
||||
*/
|
||||
extern HashMap * JsonValueAsObject(JsonValue *);
|
||||
|
||||
/**
|
||||
* Encode a JSON array as a JSON value that can be added to an object
|
||||
* or another array.
|
||||
*/
|
||||
extern JsonValue * JsonValueArray(Array *);
|
||||
|
||||
/**
|
||||
* Unwrap a JSON value that represents an array. This function will
|
||||
* return NULL if the value is not actually an array.
|
||||
*/
|
||||
extern Array * JsonValueAsArray(JsonValue *);
|
||||
|
||||
/**
|
||||
* Encode a C string as a JSON value that can be added to an object or
|
||||
* an array.
|
||||
*/
|
||||
extern JsonValue * JsonValueString(char *);
|
||||
|
||||
/**
|
||||
* Unwrap a JSON value that represents a string. This function will
|
||||
* return NULL if the value is not actually a string.
|
||||
*/
|
||||
extern char * JsonValueAsString(JsonValue *);
|
||||
|
||||
/**
|
||||
* Encode a number as a JSON value that can be added to an object or
|
||||
* an array.
|
||||
*/
|
||||
extern JsonValue * JsonValueInteger(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 */
|
196
src/include/Log.h
Normal file
196
src/include/Log.h
Normal file
|
@ -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 <stdio.h>
|
||||
#include <stddef.h>
|
||||
#include <syslog.h>
|
||||
|
||||
#include <Stream.h>
|
||||
|
||||
#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
|
235
src/include/Memory.h
Normal file
235
src/include/Memory.h
Normal file
|
@ -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 <stddef.h>
|
||||
|
||||
/**
|
||||
* 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
|
105
src/include/Queue.h
Normal file
105
src/include/Queue.h
Normal file
|
@ -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 <stddef.h>
|
||||
|
||||
/**
|
||||
* 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
|
81
src/include/Rand.h
Normal file
81
src/include/Rand.h
Normal file
|
@ -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 <stddef.h>
|
||||
|
||||
/**
|
||||
* 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 */
|
53
src/include/Runtime.h
Normal file
53
src/include/Runtime.h
Normal file
|
@ -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 <Array.h>
|
||||
|
||||
/**
|
||||
* 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 */
|
76
src/include/Sha.h
Normal file
76
src/include/Sha.h
Normal file
|
@ -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 */
|
129
src/include/Str.h
Normal file
129
src/include/Str.h
Normal file
|
@ -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 <Int.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
/**
|
||||
* 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 */
|
223
src/include/Stream.h
Normal file
223
src/include/Stream.h
Normal file
|
@ -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 <Io.h>
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
/**
|
||||
* 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 */
|
109
src/include/Tls.h
Normal file
109
src/include/Tls.h
Normal file
|
@ -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 <Stream.h>
|
||||
|
||||
#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 */
|
252
src/include/UInt64.h
Normal file
252
src/include/UInt64.h
Normal file
|
@ -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 <Int.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#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 */
|
69
src/include/Uri.h
Normal file
69
src/include/Uri.h
Normal file
|
@ -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 */
|
117
src/include/Util.h
Normal file
117
src/include/Util.h
Normal file
|
@ -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 <stdio.h>
|
||||
#include <stddef.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <Stream.h>
|
||||
#include <UInt64.h>
|
||||
|
||||
/**
|
||||
* 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 */
|
145
tools/int64.c
Normal file
145
tools/int64.c
Normal file
|
@ -0,0 +1,145 @@
|
|||
#include <Int64.h>
|
||||
|
||||
#include <Log.h>
|
||||
|
||||
/* 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;
|
||||
}
|
119
tools/uint64.c
Normal file
119
tools/uint64.c
Normal file
|
@ -0,0 +1,119 @@
|
|||
#include <UInt64.h>
|
||||
|
||||
#include <Log.h>
|
||||
|
||||
/* 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;
|
||||
}
|
Loading…
Reference in a new issue