Compare commits

..

No commits in common. "846e50a76e90e917928735542d52198ff05a96ac" and "d0969d0dd7466eda19af6da67f37cc4502645076" have entirely different histories.

24 changed files with 1524 additions and 201 deletions

4
configure vendored
View file

@ -13,12 +13,12 @@ SRC="src"
INCLUDE="src/include" INCLUDE="src/include"
TOOLS="tools" TOOLS="tools"
CFLAGS="-Wall -Wextra -pedantic -std=c99 -O3 -pipe -D_DEFAULT_SOURCE -I${INCLUDE}" CFLAGS="-Wall -Wextra -pedantic -std=c89 -O3 -pipe -D_DEFAULT_SOURCE -I${INCLUDE}"
LIBS="-lm -pthread" LIBS="-lm -pthread"
# Set default args for all platforms # Set default args for all platforms
SCRIPT_ARGS="--cc=c99 --prefix=/usr/local --enable-ld-extra --lib-name=Cytoplasm --lib-version=0.4.1 $@" SCRIPT_ARGS="--cc=cc --prefix=/usr/local --enable-ld-extra --lib-name=Cytoplasm --lib-version=0.4.1 $@"
# Set platform specific args # Set platform specific args
case "$(uname)" in case "$(uname)" in

View file

@ -23,33 +23,32 @@
*/ */
#include <Cron.h> #include <Cron.h>
#include <UInt64.h>
#include <Array.h> #include <Array.h>
#include <Memory.h> #include <Memory.h>
#include <Util.h> #include <Util.h>
#include <stdbool.h>
#include <pthread.h> #include <pthread.h>
struct Cron struct Cron
{ {
uint64_t tick; UInt64 tick;
Array *jobs; Array *jobs;
pthread_mutex_t lock; pthread_mutex_t lock;
volatile unsigned int stop:1;
pthread_t thread; pthread_t thread;
volatile bool stop;
}; };
typedef struct Job typedef struct Job
{ {
uint64_t interval; UInt64 interval;
uint64_t lastExec; UInt64 lastExec;
JobFunc *func; JobFunc *func;
void *args; void *args;
} Job; } Job;
static Job * static Job *
JobCreate(uint64_t interval, JobFunc * func, void *args) JobCreate(UInt32 interval, JobFunc * func, void *args)
{ {
Job *job; Job *job;
@ -64,8 +63,8 @@ JobCreate(uint64_t interval, JobFunc * func, void *args)
return NULL; return NULL;
} }
job->interval = interval; job->interval = UInt64Create(0, interval);
job->lastExec = 0; job->lastExec = UInt64Create(0, 0);
job->func = func; job->func = func;
job->args = args; job->args = args;
@ -80,51 +79,51 @@ CronThread(void *args)
while (!cron->stop) while (!cron->stop)
{ {
size_t i; size_t i;
uint64_t ts; /* tick start */ UInt64 ts; /* tick start */
uint64_t te; /* tick end */ UInt64 te; /* tick end */
pthread_mutex_lock(&cron->lock); pthread_mutex_lock(&cron->lock);
ts = UtilTsMillis(); ts = UtilServerTs();
for (i = 0; i < ArraySize(cron->jobs); i++) for (i = 0; i < ArraySize(cron->jobs); i++)
{ {
Job *job = ArrayGet(cron->jobs, i); Job *job = ArrayGet(cron->jobs, i);
if ((ts - job->lastExec) > job->interval) if (UInt64Gt(UInt64Sub(ts, job->lastExec), job->interval))
{ {
job->func(job->args); job->func(job->args);
job->lastExec = ts; job->lastExec = ts;
} }
if (!job->interval) if (UInt64Eq(job->interval, UInt64Create(0, 0)))
{ {
ArrayDelete(cron->jobs, i); ArrayDelete(cron->jobs, i);
Free(job); Free(job);
} }
} }
te = UtilTsMillis(); te = UtilServerTs();
pthread_mutex_unlock(&cron->lock); pthread_mutex_unlock(&cron->lock);
// Only sleep if the jobs didn't overrun the tick /* Only sleep if the jobs didn't overrun the tick */
if (cron->tick > (te - ts)) if (UInt64Gt(cron->tick, UInt64Sub(te, ts)))
{ {
const uint64_t microTick = 100; const UInt64 microTick = UInt64Create(0, 100);
uint64_t remainingTick = cron->tick - (te - ts); UInt64 remainingTick = UInt64Sub(cron->tick, UInt64Sub(te, ts));
/* Only sleep for microTick ms at a time because if the job /* Only sleep for microTick ms at a time because if the job
* scheduler is supposed to stop before the tick is up, we * scheduler is supposed to stop before the tick is up, we
* don't want to be stuck in a long sleep */ * don't want to be stuck in a long sleep */
while (remainingTick >= microTick && !cron->stop) while (UInt64Geq(remainingTick, microTick) && !cron->stop)
{ {
UtilSleepMillis(microTick); UtilSleepMillis(microTick);
remainingTick -= microTick; remainingTick = UInt64Sub(remainingTick, microTick);
} }
if (remainingTick && !cron->stop) if (UInt64Neq(remainingTick, UInt64Create(0, 0)) && !cron->stop)
{ {
UtilSleepMillis(remainingTick); UtilSleepMillis(remainingTick);
} }
@ -135,7 +134,7 @@ CronThread(void *args)
} }
Cron * Cron *
CronCreate(uint64_t tick) CronCreate(UInt32 tick)
{ {
Cron *cron = Malloc(sizeof(Cron)); Cron *cron = Malloc(sizeof(Cron));
@ -151,8 +150,8 @@ CronCreate(uint64_t tick)
return NULL; return NULL;
} }
cron->tick = tick; cron->tick = UInt64Create(0, tick);
cron->stop = true; cron->stop = 1;
pthread_mutex_init(&cron->lock, NULL); pthread_mutex_init(&cron->lock, NULL);
@ -181,7 +180,7 @@ CronOnce(Cron * cron, JobFunc * func, void *args)
} }
void void
CronEvery(Cron * cron, uint64_t interval, JobFunc * func, void *args) CronEvery(Cron * cron, unsigned long interval, JobFunc * func, void *args)
{ {
Job *job; Job *job;
@ -209,7 +208,7 @@ CronStart(Cron * cron)
return; return;
} }
cron->stop = false; cron->stop = 0;
pthread_create(&cron->thread, NULL, CronThread, cron); pthread_create(&cron->thread, NULL, CronThread, cron);
} }
@ -222,7 +221,7 @@ CronStop(Cron * cron)
return; return;
} }
cron->stop = true; cron->stop = 1;
pthread_join(cron->thread, NULL); pthread_join(cron->thread, NULL);
} }

View file

@ -23,6 +23,7 @@
*/ */
#include <Db.h> #include <Db.h>
#include <UInt64.h>
#include <Memory.h> #include <Memory.h>
#include <Json.h> #include <Json.h>
#include <Util.h> #include <Util.h>
@ -76,7 +77,7 @@ struct DbRef
{ {
HashMap *json; HashMap *json;
uint64_t ts; UInt64 ts;
size_t size; size_t size;
Array *name; Array *name;
@ -494,12 +495,12 @@ DbLockFromArr(Db * db, Array * args)
if (ref) /* In cache */ if (ref) /* In cache */
{ {
uint64_t diskTs = UtilLastModified(file); UInt64 diskTs = UtilLastModified(file);
ref->fd = fd; ref->fd = fd;
ref->stream = stream; ref->stream = stream;
if (diskTs > ref->ts) if (UInt64Gt(diskTs, ref->ts))
{ {
/* File was modified on disk since it was cached */ /* File was modified on disk since it was cached */
HashMap *json = JsonDecode(ref->stream); HashMap *json = JsonDecode(ref->stream);
@ -587,7 +588,7 @@ DbLockFromArr(Db * db, Array * args)
if (db->cache) if (db->cache)
{ {
ref->ts = UtilTsMillis(); ref->ts = UtilServerTs();
ref->size = DbComputeSize(ref->json); ref->size = DbComputeSize(ref->json);
HashMapSet(db->cache, hash, ref); HashMapSet(db->cache, hash, ref);
db->cacheSize += ref->size; db->cacheSize += ref->size;
@ -651,7 +652,7 @@ DbCreate(Db * db, size_t nArgs,...)
file = DbFileName(db, args); file = DbFileName(db, args);
if (UtilLastModified(file)) if (UInt64Neq(UtilLastModified(file), UInt64Create(0, 0)))
{ {
Free(file); Free(file);
ArrayFree(args); ArrayFree(args);
@ -754,7 +755,7 @@ DbDelete(Db * db, size_t nArgs,...)
Free(hash); Free(hash);
if (UtilLastModified(file)) if (UInt64Neq(UtilLastModified(file), UInt64Create(0, 0)))
{ {
ret = remove(file) == 0; ret = remove(file) == 0;
} }
@ -872,7 +873,7 @@ DbExists(Db * db, size_t nArgs,...)
pthread_mutex_lock(&db->lock); pthread_mutex_lock(&db->lock);
file = DbFileName(db, args); file = DbFileName(db, args);
ret = UtilLastModified(file) != 0; ret = UInt64Neq(UtilLastModified(file), UInt64Create(0, 0));
pthread_mutex_unlock(&db->lock); pthread_mutex_unlock(&db->lock);

View file

@ -465,7 +465,7 @@ HttpServerWorkerThread(void *args)
ssize_t i = 0; ssize_t i = 0;
HttpRequestMethod requestMethod; HttpRequestMethod requestMethod;
uint64_t firstRead; UInt64 firstRead;
fp = DequeueConnection(server); fp = DequeueConnection(server);
@ -473,7 +473,7 @@ HttpServerWorkerThread(void *args)
{ {
/* Block for 1 millisecond before continuing so we don't /* Block for 1 millisecond before continuing so we don't
* murder the CPU if the queue is empty. */ * murder the CPU if the queue is empty. */
UtilSleepMillis(1); UtilSleepMillis(UInt64Create(0, 1));
continue; continue;
} }
@ -483,25 +483,21 @@ HttpServerWorkerThread(void *args)
* happens, UtilGetLine() sets errno to EAGAIN. If we get * happens, UtilGetLine() sets errno to EAGAIN. If we get
* EAGAIN, then clear the error on the stream and try again * EAGAIN, then clear the error on the stream and try again
* after a few ms. This is typically more than enough time for * after a few ms. This is typically more than enough time for
* the client to send data. * the client to send data. */
* firstRead = UtilServerTs();
* TODO: Instead of looping, abort immediately, and place the request
* at the end of the queue.
*/
firstRead = UtilTsMillis();
while ((lineLen = UtilGetLine(&line, &lineSize, fp)) == -1 while ((lineLen = UtilGetLine(&line, &lineSize, fp)) == -1
&& errno == EAGAIN) && errno == EAGAIN)
{ {
StreamClearError(fp); StreamClearError(fp);
// If the server is stopped, or it's been a while, just /* If the server is stopped, or it's been a while, just
// give up so we aren't wasting a thread on this client. * give up so we aren't wasting a thread on this client. */
if (server->stop || (UtilTsMillis() - firstRead) > (1000 * 30)) if (server->stop || UInt64Gt(UInt64Sub(UtilServerTs(), firstRead), UInt64Create(0, 1000 * 30)))
{ {
goto finish; goto finish;
} }
UtilSleepMillis(5); UtilSleepMillis(UInt64Create(0, 5));
} }
if (lineLen == -1) if (lineLen == -1)

399
src/Int64.c Normal file
View 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

View file

@ -26,15 +26,14 @@
#include <Memory.h> #include <Memory.h>
#include <Str.h> #include <Str.h>
#include <Util.h> #include <Util.h>
#include <Int.h>
#include <Int64.h>
#include <stdio.h> #include <stdio.h>
#include <stddef.h> #include <stddef.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <ctype.h> #include <ctype.h>
#include <stdbool.h>
#include <inttypes.h>
#include <errno.h> #include <errno.h>
struct JsonValue struct JsonValue
@ -45,9 +44,9 @@ struct JsonValue
HashMap *object; HashMap *object;
Array *array; Array *array;
char *string; char *string;
uint64_t integer; Int64 integer;
double floating; double floating;
bool boolean; int boolean:1;
} as; } as;
}; };
@ -202,7 +201,7 @@ JsonValueAsString(JsonValue * value)
} }
JsonValue * JsonValue *
JsonValueInteger(uint64_t integer) JsonValueInteger(Int64 integer)
{ {
JsonValue *value; JsonValue *value;
@ -218,12 +217,12 @@ JsonValueInteger(uint64_t integer)
return value; return value;
} }
uint64_t Int64
JsonValueAsInteger(JsonValue * value) JsonValueAsInteger(JsonValue * value)
{ {
if (!value || value->type != JSON_INTEGER) if (!value || value->type != JSON_INTEGER)
{ {
return 0; return Int64Create(0, 0);
} }
return value->as.integer; return value->as.integer;
@ -259,7 +258,7 @@ JsonValueAsFloat(JsonValue * value)
} }
JsonValue * JsonValue *
JsonValueBoolean(bool boolean) JsonValueBoolean(int boolean)
{ {
JsonValue *value; JsonValue *value;
@ -275,12 +274,12 @@ JsonValueBoolean(bool boolean)
return value; return value;
} }
bool int
JsonValueAsBoolean(JsonValue * value) JsonValueAsBoolean(JsonValue * value)
{ {
if (!value || value->type != JSON_BOOLEAN) if (!value || value->type != JSON_BOOLEAN)
{ {
return false; return 0;
} }
return value->as.boolean; return value->as.boolean;
@ -336,12 +335,12 @@ JsonValueFree(JsonValue * value)
Free(value); Free(value);
} }
size_t int
JsonEncodeString(const char *str, Stream * out) JsonEncodeString(const char *str, Stream * out)
{ {
size_t i; size_t i;
char c; char c;
size_t length = 0; int length = 0;
StreamPutc(out, '"'); StreamPutc(out, '"');
length++; length++;
@ -404,9 +403,9 @@ JsonDecodeString(Stream * in)
int c; int c;
char a[5]; char a[5];
uint32_t codepoint; UInt32 codepoint;
uint16_t high; UInt16 high;
uint16_t low; UInt16 low;
char *utf8Ptr; char *utf8Ptr;
@ -423,7 +422,7 @@ JsonDecodeString(Stream * in)
{ {
if (c <= 0x001F) if (c <= 0x001F)
{ {
/* Bad byte; these must be escaped */ /* Bad byte; these must be escaped */
Free(str); Free(str);
return NULL; return NULL;
} }
@ -599,13 +598,15 @@ JsonDecodeString(Stream * in)
return NULL; return NULL;
} }
size_t int
JsonEncodeValue(JsonValue * value, Stream * out, int level) JsonEncodeValue(JsonValue * value, Stream * out, int level)
{ {
size_t i; size_t i;
size_t len; size_t len;
Array *arr; Array *arr;
size_t length = 0; int length = 0;
char ibuf[INT64_STRBUF];
switch (value->type) switch (value->type)
{ {
@ -643,7 +644,8 @@ JsonEncodeValue(JsonValue * value, Stream * out, int level)
length += JsonEncodeString(value->as.string, out); length += JsonEncodeString(value->as.string, out);
break; break;
case JSON_INTEGER: case JSON_INTEGER:
length += StreamPrintf(out, "%" PRId64, value->as.integer); Int64Str(value->as.integer, 10, ibuf, INT64_STRBUF);
length += StreamPrintf(out, "%s", ibuf);
break; break;
case JSON_FLOAT: case JSON_FLOAT:
length += StreamPrintf(out, "%f", value->as.floating); length += StreamPrintf(out, "%f", value->as.floating);
@ -671,14 +673,14 @@ JsonEncodeValue(JsonValue * value, Stream * out, int level)
return length; return length;
} }
size_t int
JsonEncode(HashMap * object, Stream * out, int level) JsonEncode(HashMap * object, Stream * out, int level)
{ {
size_t index; size_t index;
size_t count; size_t count;
char *key; char *key;
JsonValue *value; JsonValue *value;
size_t length; int length;
if (!object) if (!object)
{ {
@ -861,7 +863,6 @@ JsonConsumeWhitespace(JsonParserState * state)
break; break;
} }
// TODO: This logic should be moved into Stream as a sync function.
if (StreamError(state->stream)) if (StreamError(state->stream))
{ {
if (errno == EAGAIN) if (errno == EAGAIN)
@ -875,7 +876,7 @@ JsonConsumeWhitespace(JsonParserState * state)
} }
else else
{ {
UtilSleepMillis(delay); UtilSleepMillis(UInt64Create(0, delay));
continue; continue;
} }
} }
@ -1122,7 +1123,7 @@ JsonDecodeValue(JsonParserState * state)
JsonValue *value; JsonValue *value;
char *strValue; char *strValue;
int64_t iValue; Int64 iValue;
size_t i; size_t i;
int neg; int neg;
@ -1145,7 +1146,7 @@ JsonDecodeValue(JsonParserState * state)
Free(strValue); Free(strValue);
break; break;
case TOKEN_INTEGER: case TOKEN_INTEGER:
iValue = 0; iValue = Int64Create(0, 0);
i = 0; i = 0;
neg = 0; neg = 0;
@ -1161,14 +1162,14 @@ JsonDecodeValue(JsonParserState * state)
} }
d = state->token[i] - '0'; d = state->token[i] - '0';
iValue *= 10; iValue = Int64Mul(iValue, Int64Create(0, 10));
iValue += d; iValue = Int64Add(iValue, Int64Create(0, d));
i++; i++;
} }
if (neg) if (neg)
{ {
iValue *= -1; iValue = Int64Neg(iValue);
} }
value = JsonValueInteger(iValue); value = JsonValueInteger(iValue);
break; break;

View file

@ -32,6 +32,8 @@
#include <unistd.h> #include <unistd.h>
#include <pthread.h> #include <pthread.h>
#include <Int.h>
#ifndef MEMORY_TABLE_CHUNK #ifndef MEMORY_TABLE_CHUNK
#define MEMORY_TABLE_CHUNK 256 #define MEMORY_TABLE_CHUNK 256
#endif #endif
@ -48,12 +50,12 @@ struct MemoryInfo
void *pointer; void *pointer;
}; };
#define MEM_BOUND_TYPE uint32_t #define MEM_BOUND_TYPE UInt32
#define MEM_BOUND 0xDEADBEEF #define MEM_BOUND 0xDEADBEEF
#define MEM_BOUND_LOWER(p) *((MEM_BOUND_TYPE *) p) #define MEM_BOUND_LOWER(p) *((MEM_BOUND_TYPE *) p)
#define MEM_BOUND_UPPER(p, x) *(((MEM_BOUND_TYPE *) (((uint8_t *) p) + x)) + 1) #define MEM_BOUND_UPPER(p, x) *(((MEM_BOUND_TYPE *) (((UInt8 *) p) + x)) + 1)
#define MEM_SIZE_ACTUAL(x) (((x) * sizeof(uint8_t)) + (2 * sizeof(MEM_BOUND_TYPE))) #define MEM_SIZE_ACTUAL(x) (((x) * sizeof(UInt8)) + (2 * sizeof(MEM_BOUND_TYPE)))
static pthread_mutex_t lock; static pthread_mutex_t lock;
static void (*hook) (MemoryAction, MemoryInfo *, void *) = MemoryDefaultHook; static void (*hook) (MemoryAction, MemoryInfo *, void *) = MemoryDefaultHook;

View file

@ -23,12 +23,12 @@
*/ */
#include <Rand.h> #include <Rand.h>
#include <Int.h>
#include <UInt64.h>
#include <Util.h> #include <Util.h>
#include <Memory.h> #include <Memory.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdbool.h>
#include <pthread.h> #include <pthread.h>
#include <unistd.h> #include <unistd.h>
@ -42,12 +42,12 @@
typedef struct RandState typedef struct RandState
{ {
uint32_t mt[RAND_STATE_VECTOR_LENGTH]; UInt32 mt[RAND_STATE_VECTOR_LENGTH];
int index; int index;
} RandState; } RandState;
static void static void
RandSeed(RandState * state, uint32_t seed) RandSeed(RandState * state, UInt32 seed)
{ {
state->mt[0] = seed & 0xFFFFFFFF; state->mt[0] = seed & 0xFFFFFFFF;
@ -57,12 +57,12 @@ RandSeed(RandState * state, uint32_t seed)
} }
} }
static uint32_t static UInt32
RandGenerate(RandState * state) RandGenerate(RandState * state)
{ {
static const uint32_t mag[2] = {0x0, 0x9908B0DF}; static const UInt32 mag[2] = {0x0, 0x9908B0DF};
uint32_t result; UInt32 result;
if (state->index >= RAND_STATE_VECTOR_LENGTH || state->index < 0) if (state->index >= RAND_STATE_VECTOR_LENGTH || state->index < 0)
{ {
@ -118,22 +118,22 @@ RandDestructor(void *p)
/* This algorithm therefore computes N random numbers generally in O(N) /* This algorithm therefore computes N random numbers generally in O(N)
* time, while being less biased. */ * time, while being less biased. */
void void
RandIntN(uint32_t *buf, size_t size, uint32_t max) RandIntN(int *buf, size_t size, unsigned int max)
{ {
static pthread_key_t stateKey; static pthread_key_t stateKey;
static bool createdKey = false; static int createdKey = 0;
/* Limit the range to banish all previously biased results */ /* Limit the range to banish all previously biased results */
const uint32_t allowed = RAND_MAX - RAND_MAX % max; const int allowed = RAND_MAX - RAND_MAX % max;
RandState *state; RandState *state;
uint32_t tmp; int tmp;
size_t i; size_t i;
if (!createdKey) if (!createdKey)
{ {
pthread_key_create(&stateKey, RandDestructor); pthread_key_create(&stateKey, RandDestructor);
createdKey = true; createdKey = 1;
} }
state = pthread_getspecific(stateKey); state = pthread_getspecific(stateKey);
@ -141,8 +141,8 @@ RandIntN(uint32_t *buf, size_t size, uint32_t max)
if (!state) if (!state)
{ {
/* Generate a seed from the system time, PID, and TID */ /* Generate a seed from the system time, PID, and TID */
uint64_t ts = UtilTsMillis(); UInt64 ts = UtilServerTs();
uint32_t seed = ts ^ getpid() ^ (unsigned long) pthread_self(); UInt32 seed = UInt64Low(ts) ^ getpid() ^ (unsigned long) pthread_self();
state = Malloc(sizeof(RandState)); state = Malloc(sizeof(RandState));
RandSeed(state, seed); RandSeed(state, seed);
@ -164,10 +164,10 @@ RandIntN(uint32_t *buf, size_t size, uint32_t max)
} }
/* Generate just 1 random number */ /* Generate just 1 random number */
uint32_t int
RandInt(uint32_t max) RandInt(unsigned int max)
{ {
uint32_t val = 0; int val = 0;
RandIntN(&val, 1, max); RandIntN(&val, 1, max);
return val; return val;

View file

@ -23,6 +23,7 @@
*/ */
#include <Sha.h> #include <Sha.h>
#include <Memory.h> #include <Memory.h>
#include <Int.h>
#include <string.h> #include <string.h>
@ -30,10 +31,10 @@
#define LOAD32H(x, y) \ #define LOAD32H(x, y) \
{ \ { \
x = ((uint32_t)((y)[0] & 255) << 24) | \ x = ((UInt32)((y)[0] & 255) << 24) | \
((uint32_t)((y)[1] & 255) << 16) | \ ((UInt32)((y)[1] & 255) << 16) | \
((uint32_t)((y)[2] & 255) << 8) | \ ((UInt32)((y)[2] & 255) << 8) | \
((uint32_t)((y)[3] & 255)); \ ((UInt32)((y)[3] & 255)); \
} }
#define ROL(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) #define ROL(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
@ -48,22 +49,22 @@
typedef union typedef union
{ {
uint8_t c[64]; UInt8 c[64];
uint32_t l[16]; UInt32 l[16];
} Char64Long16; } Char64Long16;
typedef struct typedef struct
{ {
uint32_t state[5]; UInt32 state[5];
uint32_t count[2]; UInt32 count[2];
uint8_t buffer[64]; UInt8 buffer[64];
} Sha1Context; } Sha1Context;
static void static void
Sha1Transform(uint32_t state[5], const uint8_t *buffer) Sha1Transform(UInt32 state[5], const UInt8 buffer[64])
{ {
uint32_t a, b, c, d, e, i; UInt32 a, b, c, d, e, i;
uint8_t workspace[64]; UInt8 workspace[64];
Char64Long16 *block = (Char64Long16 *) workspace; Char64Long16 *block = (Char64Long16 *) workspace;
for (i = 0; i < 16; i++) for (i = 0; i < 16; i++)
@ -179,9 +180,9 @@ Sha1Init(Sha1Context * ctx)
} }
static void static void
Sha1Update(Sha1Context * ctx, const void *buf, uint32_t size) Sha1Update(Sha1Context * ctx, const void *buf, UInt32 size)
{ {
uint32_t i, j; UInt32 i, j;
j = (ctx->count[0] >> 3) & 63; j = (ctx->count[0] >> 3) & 63;
@ -201,7 +202,7 @@ Sha1Update(Sha1Context * ctx, const void *buf, uint32_t size)
for (; i + 63 < size; i += 64) for (; i + 63 < size; i += 64)
{ {
Sha1Transform(ctx->state, (uint8_t *) buf + i); Sha1Transform(ctx->state, (UInt8 *) buf + i);
} }
j = 0; j = 0;
@ -211,14 +212,14 @@ Sha1Update(Sha1Context * ctx, const void *buf, uint32_t size)
i = 0; i = 0;
} }
memcpy(&ctx->buffer[j], &((uint8_t *) buf)[i], size - i); memcpy(&ctx->buffer[j], &((UInt8 *) buf)[i], size - i);
} }
static void static void
Sha1Calculate(Sha1Context * ctx, unsigned char *out) Sha1Calculate(Sha1Context * ctx, unsigned char *out)
{ {
uint32_t i; UInt32 i;
uint8_t count[8]; UInt8 count[8];
for (i = 0; i < 8; i++) for (i = 0; i < 8; i++)
{ {
@ -226,16 +227,16 @@ Sha1Calculate(Sha1Context * ctx, unsigned char *out)
>> ((3 - (i & 3)) * 8)) & 255); >> ((3 - (i & 3)) * 8)) & 255);
} }
Sha1Update(ctx, (uint8_t *) "\x80", 1); Sha1Update(ctx, (UInt8 *) "\x80", 1);
while ((ctx->count[0] & 504) != 448) while ((ctx->count[0] & 504) != 448)
{ {
Sha1Update(ctx, (uint8_t *) "\0", 1); Sha1Update(ctx, (UInt8 *) "\0", 1);
} }
Sha1Update(ctx, count, 8); Sha1Update(ctx, count, 8);
for (i = 0; i < (160 / 8); i++) for (i = 0; i < (160 / 8); i++)
{ {
out[i] = (uint8_t) ((ctx->state[i >> 2] >> ((3 - (i & 3)) * 8)) & 255); out[i] = (UInt8) ((ctx->state[i >> 2] >> ((3 - (i & 3)) * 8)) & 255);
} }
} }

View file

@ -23,6 +23,7 @@
*/ */
#include <Sha.h> #include <Sha.h>
#include <Memory.h> #include <Memory.h>
#include <Int.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
@ -30,10 +31,10 @@
#include <limits.h> #include <limits.h>
#define GET_UINT32(x) \ #define GET_UINT32(x) \
(((uint32_t)(x)[0] << 24) | \ (((UInt32)(x)[0] << 24) | \
((uint32_t)(x)[1] << 16) | \ ((UInt32)(x)[1] << 16) | \
((uint32_t)(x)[2] << 8) | \ ((UInt32)(x)[2] << 8) | \
((uint32_t)(x)[3])) ((UInt32)(x)[3]))
#define PUT_UINT32(dst, x) { \ #define PUT_UINT32(dst, x) { \
(dst)[0] = (x) >> 24; \ (dst)[0] = (x) >> 24; \
@ -55,8 +56,8 @@
#define WW(i) (w[i] = w[i - 16] + S0(w[i - 15]) + w[i - 7] + S1(w[i - 2])) #define WW(i) (w[i] = w[i - 16] + S0(w[i - 15]) + w[i - 7] + S1(w[i - 2]))
#define ROUND(a, b, c, d, e, f, g, h, k, w) { \ #define ROUND(a, b, c, d, e, f, g, h, k, w) { \
uint32_t tmp0 = h + T0(e) + CH(e, f, g) + k + w; \ UInt32 tmp0 = h + T0(e) + CH(e, f, g) + k + w; \
uint32_t tmp1 = T1(a) + MAJ(a, b, c); \ UInt32 tmp1 = T1(a) + MAJ(a, b, c); \
h = tmp0 + tmp1; \ h = tmp0 + tmp1; \
d += tmp0; \ d += tmp0; \
} }
@ -64,7 +65,7 @@
typedef struct Sha256Context typedef struct Sha256Context
{ {
size_t length; size_t length;
uint32_t state[8]; UInt32 state[8];
size_t bufLen; size_t bufLen;
unsigned char buffer[64]; unsigned char buffer[64];
} Sha256Context; } Sha256Context;
@ -72,7 +73,7 @@ typedef struct Sha256Context
static void static void
Sha256Chunk(Sha256Context * context, unsigned char chunk[64]) Sha256Chunk(Sha256Context * context, unsigned char chunk[64])
{ {
const uint32_t rk[64] = { const UInt32 rk[64] = {
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1,
0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786,
@ -86,8 +87,8 @@ Sha256Chunk(Sha256Context * context, unsigned char chunk[64])
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
}; };
uint32_t w[64]; UInt32 w[64];
uint32_t a, b, c, d, e, f, g, h; UInt32 a, b, c, d, e, f, g, h;
int i; int i;
@ -177,10 +178,10 @@ Sha256(char *str)
unsigned char *out; unsigned char *out;
unsigned char fill[64]; unsigned char fill[64];
uint32_t fillLen; UInt32 fillLen;
unsigned char buf[8]; unsigned char buf[8];
uint32_t hiLen; UInt32 hiLen;
uint32_t loLen; UInt32 loLen;
if (!str) if (!str)
{ {
@ -212,8 +213,8 @@ Sha256(char *str)
fill[0] = 0x80; fill[0] = 0x80;
fillLen = (context.bufLen < 56) ? 56 - context.bufLen : 120 - context.bufLen; fillLen = (context.bufLen < 56) ? 56 - context.bufLen : 120 - context.bufLen;
hiLen = (uint32_t) (context.length >> 29); hiLen = (UInt32) (context.length >> 29);
loLen = (uint32_t) (context.length << 3); loLen = (UInt32) (context.length << 3);
PUT_UINT32(&buf[0], hiLen); PUT_UINT32(&buf[0], hiLen);
PUT_UINT32(&buf[4], loLen); PUT_UINT32(&buf[4], loLen);

View file

@ -26,6 +26,7 @@
#include <Memory.h> #include <Memory.h>
#include <Util.h> #include <Util.h>
#include <Rand.h> #include <Rand.h>
#include <Int.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -34,8 +35,8 @@
#include <pthread.h> #include <pthread.h>
#include <unistd.h> #include <unistd.h>
uint32_t UInt32
StrUtf16Decode(uint16_t high, uint16_t low) StrUtf16Decode(UInt16 high, UInt16 low)
{ {
if (high <= 0xD7FF) if (high <= 0xD7FF)
{ {
@ -55,7 +56,7 @@ StrUtf16Decode(uint16_t high, uint16_t low)
} }
char * char *
StrUtf8Encode(uint32_t codepoint) StrUtf8Encode(UInt32 codepoint)
{ {
char *str; char *str;
@ -219,10 +220,10 @@ StrConcat(size_t nStr,...)
return str; return str;
} }
bool int
StrBlank(const char *str) StrBlank(const char *str)
{ {
bool blank = true; int blank = 1;
size_t i = 0; size_t i = 0;
while (str[i]) while (str[i])
@ -244,7 +245,7 @@ StrRandom(size_t len)
static const char charset[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; static const char charset[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
char *str; char *str;
uint32_t *nums; int *nums;
size_t i; size_t i;
if (!len) if (!len)
@ -259,7 +260,7 @@ StrRandom(size_t len)
return NULL; return NULL;
} }
nums = Malloc(len * sizeof(uint32_t)); nums = Malloc(len * sizeof(int));
if (!nums) if (!nums)
{ {
Free(str); Free(str);
@ -322,21 +323,21 @@ StrLower(char *str)
return ret; return ret;
} }
bool int
StrEquals(const char *str1, const char *str2) StrEquals(const char *str1, const char *str2)
{ {
/* Both strings are NULL, they're equal */ /* Both strings are NULL, they're equal */
if (!str1 && !str2) if (!str1 && !str2)
{ {
return true; return 1;
} }
/* One or the other is NULL, they're not equal */ /* One or the other is NULL, they're not equal */
if (!str1 || !str2) if (!str1 || !str2)
{ {
return false; return 0;
} }
/* Neither are NULL, do a regular string comparison */ /* Neither are NULL, do a regular string comparison */
return (strcmp(str1, str2) == 0); return strcmp(str1, str2) == 0;
} }

View file

@ -26,6 +26,7 @@
#include <Io.h> #include <Io.h>
#include <Memory.h> #include <Memory.h>
#include <Util.h> #include <Util.h>
#include <Int.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@ -49,11 +50,11 @@ struct Stream
{ {
Io *io; Io *io;
uint8_t *rBuf; UInt8 *rBuf;
size_t rLen; size_t rLen;
size_t rOff; size_t rOff;
uint8_t *wBuf; UInt8 *wBuf;
size_t wLen; size_t wLen;
char *ugBuf; char *ugBuf;
@ -625,7 +626,7 @@ StreamCopy(Stream * in, Stream * out)
} }
else else
{ {
UtilSleepMillis(STREAM_DELAY); UtilSleepMillis(UInt64Create(0, STREAM_DELAY));
continue; continue;
} }
} }

265
src/UInt64.c Normal file
View 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

View file

@ -40,6 +40,8 @@
#include <limits.h> #include <limits.h>
#include <pthread.h> #include <pthread.h>
#include <UInt64.h>
#ifndef PATH_MAX #ifndef PATH_MAX
#define PATH_MAX 256 #define PATH_MAX 256
#endif #endif
@ -48,14 +50,14 @@
#define SSIZE_MAX LONG_MAX #define SSIZE_MAX LONG_MAX
#endif #endif
uint64_t UInt64
UtilTsMillis(void) UtilServerTs(void)
{ {
struct timeval tv; struct timeval tv;
uint64_t ts; UInt64 ts;
uint64_t sec; UInt64 sec;
uint64_t usec; UInt64 usec;
gettimeofday(&tv, NULL); gettimeofday(&tv, NULL);
@ -75,31 +77,54 @@ UtilTsMillis(void)
* *
* The same goes for suseconds_t. * The same goes for suseconds_t.
*/ */
if (sizeof(time_t) == sizeof(UInt64))
{
/* 64 bit time_t: convert it to a 64 bit integer */
time_t ms = tv.tv_sec * 1000;
UInt32 high = (UInt32) (ms >> 32);
UInt32 low = (UInt32) ms;
// Two separate steps because time_t might be 32-bit. In that sec = UInt64Create(high, low);
// case, we want the multiplication to happen after the promotion }
// to uint64_t. else
sec = tv.tv_sec; {
sec *= 1000; /* Assume 32 bit time_t: promote to 64 bit, then multiply, in
* case multiplication overflows 32 bits. */
sec = UInt64Create(0, tv.tv_sec);
sec = UInt64Mul(sec, UInt64Create(0, 1000));
}
usec = tv.tv_usec / 1000; usec = UInt64Create(0, tv.tv_usec / 1000);
ts = UInt64Add(sec, usec);
ts = sec + usec;
return ts; return ts;
} }
uint64_t UInt64
UtilLastModified(char *path) UtilLastModified(char *path)
{ {
struct stat st; struct stat st;
uint64_t ts = 0; UInt64 ts = UInt64Create(0, 0);
if (stat(path, &st) == 0) if (stat(path, &st) == 0)
{ {
ts = st.st_mtim.tv_sec; if (sizeof(time_t) == sizeof(UInt64))
ts *= 1000; {
ts += st.st_mtim.tv_nsec / 1000000; /* 64 bit time_t: convert it to a 64 bit integer */
time_t ms = st.st_mtim.tv_sec * 1000;
UInt32 high = (UInt32) (ms >> 32);
UInt32 low = (UInt32) ms;
ts = UInt64Create(high, low);
}
else
{
ts = UInt64Create(0, st.st_mtim.tv_sec);
ts = UInt64Mul(ts, UInt64Create(0, 1000));
}
/* nsec gauanteed to fit in 32 bits */
ts = UInt64Add(ts, UInt64Create(0, st.st_mtim.tv_nsec / 1000000));
} }
return ts; return ts;
@ -177,13 +202,21 @@ UtilMkdir(const char *dir, const mode_t mode)
} }
int int
UtilSleepMillis(uint64_t ms) UtilSleepMillis(UInt64 ms)
{ {
struct timespec ts; struct timespec ts;
int res; int res;
ts.tv_sec = ms / 1000; if (sizeof(time_t) == sizeof(UInt64))
ts.tv_nsec = (ms % 1000) * 1000000; {
ts.tv_sec = ((time_t) UInt64High(ms) << 32 | UInt64Low(ms)) / 1000;
}
else
{
ts.tv_sec = UInt64Low(ms) / 1000;
}
ts.tv_nsec = UInt64Low(UInt64Rem(ms, UInt64Create(0, 1000))) * 1000000;
res = nanosleep(&ts, &ts); res = nanosleep(&ts, &ts);
@ -279,14 +312,14 @@ ThreadNoDestructor(void *p)
free(p); free(p);
} }
uint32_t UInt32
UtilThreadNo(void) UtilThreadNo(void)
{ {
static pthread_key_t key; static pthread_key_t key;
static int createdKey = 0; static int createdKey = 0;
static unsigned long count = 0; static unsigned long count = 0;
uint32_t *no; UInt32 *no;
if (!createdKey) if (!createdKey)
{ {
@ -297,7 +330,7 @@ UtilThreadNo(void)
no = pthread_getspecific(key); no = pthread_getspecific(key);
if (!no) if (!no)
{ {
no = malloc(sizeof(uint32_t)); no = malloc(sizeof(UInt32));
*no = count++; *no = count++;
pthread_setspecific(key, no); pthread_setspecific(key, no);
} }

View file

@ -24,8 +24,6 @@
#ifndef CYTOPLASM_CRON_H #ifndef CYTOPLASM_CRON_H
#define CYTOPLASM_CRON_H #define CYTOPLASM_CRON_H
#include <stdint.h>
/*** /***
* @Nm Cron * @Nm Cron
* @Nd Basic periodic job scheduler. * @Nd Basic periodic job scheduler.
@ -58,6 +56,8 @@
* by any means. * by any means.
*/ */
#include "Int.h"
/** /**
* All functions defined here operate on a structure opaque to the * All functions defined here operate on a structure opaque to the
* caller. * caller.
@ -82,7 +82,7 @@ typedef void (JobFunc) (void *);
* .Pp * .Pp
* This function takes the tick interval in milliseconds. * This function takes the tick interval in milliseconds.
*/ */
extern Cron * CronCreate(uint64_t); extern Cron * CronCreate(UInt32);
/** /**
* Schedule a one-off job to be executed only at the next tick, and * Schedule a one-off job to be executed only at the next tick, and
@ -110,7 +110,7 @@ extern void
* and a pointer to pass to that function when it is executed. * and a pointer to pass to that function when it is executed.
*/ */
extern void extern void
CronEvery(Cron *, uint64_t, JobFunc *, void *); CronEvery(Cron *, unsigned long, JobFunc *, void *);
/** /**
* Start ticking the clock and executing registered jobs. * Start ticking the clock and executing registered jobs.

122
src/include/Int.h Normal file
View 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
View 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 */

View file

@ -71,10 +71,10 @@
#include "HashMap.h" #include "HashMap.h"
#include "Array.h" #include "Array.h"
#include "Stream.h" #include "Stream.h"
#include "Int64.h"
#include <stdio.h> #include <stdio.h>
#include <stddef.h> #include <stddef.h>
#include <stdbool.h>
#define JSON_DEFAULT -1 #define JSON_DEFAULT -1
#define JSON_PRETTY 0 #define JSON_PRETTY 0
@ -152,7 +152,7 @@ extern char * JsonValueAsString(JsonValue *);
* Encode a number as a JSON value that can be added to an object or * Encode a number as a JSON value that can be added to an object or
* an array. * an array.
*/ */
extern JsonValue * JsonValueInteger(uint64_t); extern JsonValue * JsonValueInteger(Int64);
/** /**
* Unwrap a JSON value that represents a number. This function will * Unwrap a JSON value that represents a number. This function will
@ -160,7 +160,7 @@ extern JsonValue * JsonValueInteger(uint64_t);
* misleading. Check the type of the value before making assumptions * misleading. Check the type of the value before making assumptions
* about its value. * about its value.
*/ */
extern uint64_t JsonValueAsInteger(JsonValue *); extern Int64 JsonValueAsInteger(JsonValue *);
/** /**
* Encode a floating point number as a JSON value that can be added * Encode a floating point number as a JSON value that can be added
@ -181,7 +181,7 @@ extern double JsonValueAsFloat(JsonValue *);
* expressions as a JSON value that can be added to an object or an * expressions as a JSON value that can be added to an object or an
* array. * array.
*/ */
extern JsonValue * JsonValueBoolean(bool); extern JsonValue * JsonValueBoolean(int);
/** /**
* Unwrap a JSON value that represents a boolean. This function will * Unwrap a JSON value that represents a boolean. This function will
@ -189,7 +189,7 @@ extern JsonValue * JsonValueBoolean(bool);
* misleading. Check the type of the value before making assumptions * misleading. Check the type of the value before making assumptions
* about its type. * about its type.
*/ */
extern bool JsonValueAsBoolean(JsonValue *); extern int JsonValueAsBoolean(JsonValue *);
/** /**
* This is a special case that represents a JSON null. Because the * This is a special case that represents a JSON null. Because the
@ -253,7 +253,7 @@ extern void JsonFree(HashMap *);
* or if the stream is NULL, the number of bytes that would have * or if the stream is NULL, the number of bytes that would have
* been written. * been written.
*/ */
extern size_t JsonEncodeString(const char *, Stream *); extern int JsonEncodeString(const char *, Stream *);
/** /**
* Serialize a JSON value as it would appear in JSON output. This is * Serialize a JSON value as it would appear in JSON output. This is
@ -277,7 +277,7 @@ extern size_t JsonEncodeString(const char *, Stream *);
* or if the stream is NULL, the number of bytes that would have * or if the stream is NULL, the number of bytes that would have
* been written. * been written.
*/ */
extern size_t JsonEncodeValue(JsonValue *, Stream *, int); extern int JsonEncodeValue(JsonValue *, Stream *, int);
/** /**
* Encode a JSON object as it would appear in JSON output, writing it * Encode a JSON object as it would appear in JSON output, writing it
@ -289,7 +289,7 @@ extern size_t JsonEncodeValue(JsonValue *, Stream *, int);
* or if the stream is NULL, the number of bytes that would have * or if the stream is NULL, the number of bytes that would have
* been written. * been written.
*/ */
extern size_t JsonEncode(HashMap *, Stream *, int); extern int JsonEncode(HashMap *, Stream *, int);
/** /**
* Decode a JSON object from the given input stream and parse it into * Decode a JSON object from the given input stream and parse it into

View file

@ -76,7 +76,6 @@
* macros. * macros.
*/ */
#include <stddef.h> #include <stddef.h>
#include <stdint.h>
/** /**
* These values are passed into the memory hook function to indicate * These values are passed into the memory hook function to indicate

View file

@ -58,13 +58,11 @@
*/ */
#include <stddef.h> #include <stddef.h>
#include <stdint.h>
/** /**
* Generate a single random 32-bit integer between 0 and the * Generate a single random integer between 0 and the passed value.
* passed value.
*/ */
extern uint32_t RandInt(uint32_t); extern int RandInt(unsigned int);
/** /**
* Generate the number of integers specified by the second argument * Generate the number of integers specified by the second argument
@ -78,6 +76,6 @@ extern uint32_t RandInt(uint32_t);
* has to lock and unlock a mutex. It is therefore better to obtain * has to lock and unlock a mutex. It is therefore better to obtain
* multiple random numbers in one pass if multiple are needed. * multiple random numbers in one pass if multiple are needed.
*/ */
extern void RandIntN(uint32_t *, size_t, uint32_t); extern void RandIntN(int *, size_t, unsigned int);
#endif /* CYTOPLASM_RAND_H */ #endif /* CYTOPLASM_RAND_H */

View file

@ -39,21 +39,21 @@
* is a standard library header. * is a standard library header.
*/ */
#include "Int.h"
#include <stddef.h> #include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
/** /**
* Convert UTF-16 into a Unicode codepoint. * Convert UTF-16 into a Unicode codepoint.
*/ */
extern uint32_t StrUtf16Decode(uint16_t, uint16_t); extern UInt32 StrUtf16Decode(UInt16, UInt16);
/** /**
* Take a Unicode codepoint and encode it into a string buffer containing * 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, * between 1 and 4 bytes. The string buffer is allocated on the heap,
* so it should be freed when it is no longer needed. * so it should be freed when it is no longer needed.
*/ */
extern char * StrUtf8Encode(uint32_t); extern char * StrUtf8Encode(UInt32);
/** /**
* Duplicate a null-terminated string, returning a new string on the * Duplicate a null-terminated string, returning a new string on the
@ -87,7 +87,7 @@ extern char * StrConcat(size_t,...);
* string consists only of blank characters, as determined by * string consists only of blank characters, as determined by
* .Xr isblank 3 . * .Xr isblank 3 .
*/ */
extern bool StrBlank(const char *str); extern int StrBlank(const char *str);
/** /**
* Generate a string of the specified length, containing random * Generate a string of the specified length, containing random
@ -124,6 +124,6 @@ extern char * StrLower(char *);
* function returns a boolean value indicating whether or not * function returns a boolean value indicating whether or not
* strcmp() returned 0. * strcmp() returned 0.
*/ */
extern bool StrEquals(const char *, const char *); extern int StrEquals(const char *, const char *);
#endif /* CYTOPLASM_STR_H */ #endif /* CYTOPLASM_STR_H */

View file

@ -39,7 +39,6 @@
#include "Io.h" #include "Io.h"
#include <stdarg.h> #include <stdarg.h>
#include <stdint.h>
/** /**
* An opaque structure analogous to C's FILE pointers. * An opaque structure analogous to C's FILE pointers.

252
src/include/UInt64.h Normal file
View 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 */

View file

@ -43,6 +43,7 @@
#include <sys/types.h> #include <sys/types.h>
#include "Stream.h" #include "Stream.h"
#include "UInt64.h"
/** /**
* Get the current timestamp in milliseconds since the Unix epoch. This * Get the current timestamp in milliseconds since the Unix epoch. This
@ -60,7 +61,7 @@
* overflow before it even gets to this function, which will cause this * overflow before it even gets to this function, which will cause this
* function to produce unexpected results. * function to produce unexpected results.
*/ */
extern uint64_t UtilTsMillis(void); extern UInt64 UtilServerTs(void);
/** /**
* Use * Use
@ -69,7 +70,7 @@ extern uint64_t UtilTsMillis(void);
* was an error getting the last modified time of a file. This is * was an error getting the last modified time of a file. This is
* primarily useful for caching file data. * primarily useful for caching file data.
*/ */
extern uint64_t UtilLastModified(char *); extern UInt64 UtilLastModified(char *);
/** /**
* This function behaves just like the system call * This function behaves just like the system call
@ -85,7 +86,7 @@ extern int UtilMkdir(const char *, const mode_t);
* .Xr nanosleep 2 * .Xr nanosleep 2
* to make its usage much, much simpler. * to make its usage much, much simpler.
*/ */
extern int UtilSleepMillis(uint64_t); extern int UtilSleepMillis(UInt64);
/** /**
* This function works identically to the POSIX * This function works identically to the POSIX
@ -111,6 +112,6 @@ extern ssize_t UtilGetLine(char **, size_t *, Stream *);
* .Fn pthread_self * .Fn pthread_self
* to a number. * to a number.
*/ */
extern uint32_t UtilThreadNo(void); extern UInt32 UtilThreadNo(void);
#endif /* CYTOPLASM_UTIL_H */ #endif /* CYTOPLASM_UTIL_H */