Compare commits

..

7 commits

Author SHA1 Message Date
32f31fe6d6 Merge pull request 'Add the DbOperationArgs set of functions, and fix bug with LMDB' (#62) from lda/Cytoplasm:db-args into master
Reviewed-on: Telodendria/Cytoplasm#62
2024-11-10 18:09:05 +00:00
lda
f3838e775c Merge branch 'master' into db-args 2024-11-09 16:03:20 +00:00
LDA
100d61050f [ADD/FIX] Add DbOPERATIONArgs functions, fix LMDB 2024-11-09 16:59:49 +01:00
b4841fffaa Merge pull request 'Add a function to decode a JSON value directly.' (#60) from lda/Cytoplasm:raw-types into master
Reviewed-on: Telodendria/Cytoplasm#60
2024-10-26 18:49:52 +00:00
LDA
39e81139f0 [ADD] Add JsonValueDecode to decode a simple value 2024-10-25 18:45:54 +02:00
8df5f0f1c1 Merge pull request 'Make Memory API more friendly with alignment' (#59) from lda/Cytoplasm:alignment into master
Reviewed-on: Telodendria/Cytoplasm#59
2024-10-24 11:57:50 +00:00
LDA
708c5daad9 [FIX] Fix memory alignment issues
Some architectures(DEC Alpha as a main outlier, but x86 may behave that
way by setting flags) raise traps on unaligned operations, which can be
either costly(having to talk to the kernel, which may have to emulate
the read) or could cause program termination.

Also adds a basic memory interval for checking if a pointer has any
business living within the heap. Most systems separate those anyways so
it avoids doing potentially dangerous operations.
2024-10-24 11:41:33 +02:00
8 changed files with 195 additions and 25 deletions

View file

@ -21,6 +21,8 @@ are not even initialized to a default value.
- Make `HttpRouter` decode path parts before matching them on regular expressions. - Make `HttpRouter` decode path parts before matching them on regular expressions.
- Fixed a bug in `ArraySort()` that would crash programs if the passed array has no - Fixed a bug in `ArraySort()` that would crash programs if the passed array has no
elements. elements.
- Adds `Db[OP]Args` functions that are equivalent to their `Db[OP]` counter parts, but
uses an array of string instead of variadic arguments.
## v0.4.0 ## v0.4.0

View file

@ -119,6 +119,13 @@ extern void DbMaxCacheSet(Db *, size_t);
*/ */
extern DbRef * DbCreate(Db *, size_t,...); extern DbRef * DbCreate(Db *, size_t,...);
/**
* Behaves like
* .Fn DbCreate ,
* but with an array of strings instead of variadic arguments.
*/
extern DbRef * DbCreateArgs(Db *, Array *);
/** /**
* Lock an existing object in the database. This function will fail * Lock an existing object in the database. This function will fail
* if the object does not exist. It takes a variable number of C * if the object does not exist. It takes a variable number of C
@ -129,6 +136,13 @@ extern DbRef * DbCreate(Db *, size_t,...);
*/ */
extern DbRef * DbLock(Db *, size_t,...); extern DbRef * DbLock(Db *, size_t,...);
/**
* Behaves like
* .Fn DbLock ,
* but with an array of strings instead of variadic arguments.
*/
extern DbRef * DbLockArgs(Db *, Array *);
/** /**
* Behaves like * Behaves like
* .Fn DbLock , * .Fn DbLock ,
@ -138,6 +152,13 @@ extern DbRef * DbLock(Db *, size_t,...);
*/ */
extern DbRef * DbLockIntent(Db *, DbHint, size_t,...); extern DbRef * DbLockIntent(Db *, DbHint, size_t,...);
/**
* Behaves like
* .Fn DbLockIntent ,
* but with an array of strings instead of variadic arguments.
*/
extern DbRef * DbLockIntentArgs(Db *, DbHint, Array *);
/** /**
* Immediately and permanently remove an object from the database. * Immediately and permanently remove an object from the database.
* This function assumes the object is not locked, otherwise undefined * This function assumes the object is not locked, otherwise undefined
@ -145,6 +166,13 @@ extern DbRef * DbLockIntent(Db *, DbHint, size_t,...);
*/ */
extern bool DbDelete(Db *, size_t,...); extern bool DbDelete(Db *, size_t,...);
/**
* Behaves like
* .Fn DbDelete ,
* but with an array of strings instead of variadic arguments.
*/
extern bool DbDeleteArgs(Db *, Array *);
/** /**
* Unlock an object and return it back to the database. This function * Unlock an object and return it back to the database. This function
* immediately syncs the object to the filesystem. The cache is a * immediately syncs the object to the filesystem. The cache is a
@ -162,6 +190,13 @@ extern bool DbUnlock(Db *, DbRef *);
*/ */
extern bool DbExists(Db *, size_t,...); extern bool DbExists(Db *, size_t,...);
/**
* Behaves like
* .Fn DbExists ,
* but with an array of strings instead of variadic arguments.
*/
extern bool DbExistsArgs(Db *, Array *);
/** /**
* List all of the objects at a given path. Unlike the other varargs * 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 * functions, this one does not take a path to a specific object; it
@ -174,6 +209,13 @@ extern bool DbExists(Db *, size_t,...);
*/ */
extern Array * DbList(Db *, size_t,...); extern Array * DbList(Db *, size_t,...);
/**
* Behaves like
* .Fn DbList ,
* but with an array of strings instead of variadic arguments.
*/
extern Array * DbListArgs(Db *, Array *);
/** /**
* Free the list returned by * Free the list returned by
* .Fn DbListFree . * .Fn DbListFree .

View file

@ -297,6 +297,12 @@ extern size_t JsonEncode(HashMap *, Stream *, int);
*/ */
extern HashMap * JsonDecode(Stream *); extern HashMap * JsonDecode(Stream *);
/**
* Decodes a JSON value from thr current input strram and parse it
* into a JsonValue.
*/
extern JsonValue * JsonValueDecode(Stream *);
/** /**
* A convenience function that allows the caller to retrieve and * A convenience function that allows the caller to retrieve and
* arbitrarily deep keys within a JSON object. It takes a root JSON * arbitrarily deep keys within a JSON object. It takes a root JSON

View file

@ -262,6 +262,16 @@ DbClose(Db * db)
Free(db); Free(db);
} }
DbRef *
DbCreateArgs(Db * db, Array *args)
{
if (!args || !db || !db->create)
{
return NULL;
}
return db->create(db, args);
}
DbRef * DbRef *
DbCreate(Db * db, size_t nArgs,...) DbCreate(Db * db, size_t nArgs,...)
{ {
@ -283,12 +293,22 @@ DbCreate(Db * db, size_t nArgs,...)
return NULL; return NULL;
} }
ret = db->create(db, args); ret = DbCreateArgs(db, args);
ArrayFree(args); ArrayFree(args);
return ret; return ret;
} }
bool
DbDeleteArgs(Db * db, Array *args)
{
if (!args || !db || !db->delete)
{
return false;
}
return db->delete(db, args);
}
bool bool
DbDelete(Db * db, size_t nArgs,...) DbDelete(Db * db, size_t nArgs,...)
{ {
@ -305,12 +325,22 @@ DbDelete(Db * db, size_t nArgs,...)
args = ArrayFromVarArgs(nArgs, ap); args = ArrayFromVarArgs(nArgs, ap);
va_end(ap); va_end(ap);
ret = db->delete(db, args); ret = DbDeleteArgs(db, args);
ArrayFree(args); ArrayFree(args);
return ret; return ret;
} }
DbRef *
DbLockArgs(Db * db, Array *args)
{
if (!args || !db || !db->lockFunc)
{
return NULL;
}
return db->lockFunc(db, DB_HINT_WRITE, args);
}
DbRef * DbRef *
DbLock(Db * db, size_t nArgs,...) DbLock(Db * db, size_t nArgs,...)
{ {
@ -318,6 +348,11 @@ DbLock(Db * db, size_t nArgs,...)
Array *args; Array *args;
DbRef *ret; DbRef *ret;
if (!db)
{
return NULL;
}
va_start(ap, nArgs); va_start(ap, nArgs);
args = ArrayFromVarArgs(nArgs, ap); args = ArrayFromVarArgs(nArgs, ap);
va_end(ap); va_end(ap);
@ -327,13 +362,22 @@ DbLock(Db * db, size_t nArgs,...)
return NULL; return NULL;
} }
ret = db->lockFunc(db, DB_HINT_WRITE, args); ret = DbLockArgs(db, args);
ArrayFree(args); ArrayFree(args);
return ret; return ret;
} }
DbRef *
DbLockIntentArgs(Db * db, DbHint hint, Array *args)
{
if (!db || !args || !db->lockFunc)
{
return NULL;
}
return db->lockFunc(db, hint, args);
}
DbRef * DbRef *
DbLockIntent(Db * db, DbHint hint, size_t nArgs,...) DbLockIntent(Db * db, DbHint hint, size_t nArgs,...)
{ {
@ -350,7 +394,7 @@ DbLockIntent(Db * db, DbHint hint, size_t nArgs,...)
return NULL; return NULL;
} }
ret = db->lockFunc(db, hint, args); ret = DbLockIntentArgs(db, hint, args);
ArrayFree(args); ArrayFree(args);
@ -368,6 +412,16 @@ DbUnlock(Db * db, DbRef * ref)
return db->unlock(db, ref); return db->unlock(db, ref);
} }
bool
DbExistsArgs(Db * db, Array *args)
{
if (!args || !db || !db->exists)
{
return false;
}
return db->exists(db, args);
}
bool bool
DbExists(Db * db, size_t nArgs,...) DbExists(Db * db, size_t nArgs,...)
{ {
@ -388,12 +442,21 @@ DbExists(Db * db, size_t nArgs,...)
return false; return false;
} }
ret = db->exists(db, args); ret = DbExistsArgs(db, args);
ArrayFree(args); ArrayFree(args);
return ret; return ret;
} }
Array *
DbListArgs(Db *db, Array *args)
{
if (!db || !args || !db->list)
{
return NULL;
}
return db->list(db, args);
}
Array * Array *
DbList(Db * db, size_t nArgs,...) DbList(Db * db, size_t nArgs,...)
{ {
@ -410,7 +473,7 @@ DbList(Db * db, size_t nArgs,...)
path = ArrayFromVarArgs(nArgs, ap); path = ArrayFromVarArgs(nArgs, ap);
va_end(ap); va_end(ap);
result = db->list(db, path); result = DbListArgs(db, path);
ArrayFree(path); ArrayFree(path);
return result; return result;

View file

@ -54,7 +54,8 @@ DbDirName(FlatDb * db, Array * args, size_t strip)
{ {
char *tmp; char *tmp;
char *sanitise = StrDuplicate(ArrayGet(args, i)); char *sanitise = StrDuplicate(ArrayGet(args, i));
for (j = 0; j < strlen(sanitise); j++) size_t len = strlen(sanitise);
for (j = 0; j < len; j++)
{ {
sanitise[j] = DbSanitiseChar(sanitise[j]); sanitise[j] = DbSanitiseChar(sanitise[j]);
} }

View file

@ -128,7 +128,9 @@ LMDBKeyHead(MDB_val key)
return NULL; return NULL;
} }
while ((void *) (end - 1) >= key.mv_data && *(end - 1)) /* Doing >= will lead to cases where it is sent straight to the
* start. Don't do that. */
while ((void *) (end - 1) > key.mv_data && *(end - 1))
{ {
end--; end--;
} }

View file

@ -1348,6 +1348,25 @@ JsonDecode(Stream * stream)
return result; return result;
} }
JsonValue *
JsonValueDecode(Stream *stream)
{
JsonValue *result;
JsonParserState state;
state.stream = stream;
state.token = NULL;
JsonTokenSeek(&state);
result = JsonDecodeValue(&state);
if (state.token)
{
Free(state.token);
}
return result;
}
JsonValue * JsonValue *
JsonGet(HashMap * json, size_t nArgs,...) JsonGet(HashMap * json, size_t nArgs,...)

View file

@ -42,26 +42,30 @@
#define MEMORY_FILE_SIZE 256 #define MEMORY_FILE_SIZE 256
#define MEM_BOUND_TYPE uint64_t
#define MEM_BOUND 0xDEADBEEFBEEFDEAD
#define MEM_MAGIC 0xDEADBEEFDEADBEEF
struct MemoryInfo struct MemoryInfo
{ {
uint64_t magic; uint64_t magic;
size_t size; size_t size;
size_t unalignedSize;
char file[MEMORY_FILE_SIZE]; char file[MEMORY_FILE_SIZE];
int line; int line;
void *pointer; void *pointer;
MemoryInfo *prev; MemoryInfo *prev;
MemoryInfo *next; MemoryInfo *next;
MEM_BOUND_TYPE leftBoundary;
}; };
#define MEM_BOUND_TYPE uint32_t #define MEM_SIZE_ACTUAL(x) (MemoryAlignBoundary((x) * sizeof(uint8_t)) + sizeof(MEM_BOUND_TYPE))
#define MEM_BOUND 0xDEADBEEF #define MEM_START_BOUNDARY(info) (info->leftBoundary)
#define MEM_MAGIC 0xDEADBEEFDEADBEEF #define MEM_END_BOUNDARY(info) (*(((MEM_BOUND_TYPE *) (((uint8_t *) info->pointer) + info->size)) - 1))
#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_SIZE_ACTUAL(x) (((x) * sizeof(uint8_t)) + (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;
@ -71,6 +75,19 @@ static size_t allocationsLen = 0;
static MemoryInfo *allocationTail = NULL; static MemoryInfo *allocationTail = NULL;
/* Simple range of "plausible" boundaries for heap, serving as an extra
* check */
static void *heapStart, *heapEnd;
static size_t MemoryAlignBoundary(size_t size)
{
size_t boundSize = sizeof(MEM_BOUND_TYPE);
size_t remainder = size % boundSize;
size_t closest = size / boundSize + !!remainder;
return closest * boundSize;
}
int int
MemoryRuntimeInit(void) MemoryRuntimeInit(void)
{ {
@ -85,6 +102,8 @@ MemoryRuntimeInit(void)
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
ret = pthread_mutex_init(&lock, &attr); ret = pthread_mutex_init(&lock, &attr);
pthread_mutexattr_destroy(&attr); pthread_mutexattr_destroy(&attr);
heapStart = NULL;
heapEnd = NULL;
ret = (ret == 0); ret = (ret == 0);
@ -110,6 +129,15 @@ MemoryInsert(MemoryInfo * a)
a->prev = allocationTail; a->prev = allocationTail;
a->magic = MEM_MAGIC; a->magic = MEM_MAGIC;
if (!heapStart || heapStart > (void *) a)
{
heapStart = a;
}
if (!heapEnd || heapEnd < (void *) a)
{
heapEnd = a;
}
allocationTail = a; allocationTail = a;
allocationsLen++; allocationsLen++;
@ -142,8 +170,9 @@ MemoryDelete(MemoryInfo * a)
static int static int
MemoryCheck(MemoryInfo * a) MemoryCheck(MemoryInfo * a)
{ {
if (MEM_BOUND_LOWER(a->pointer) != MEM_BOUND || if (MEM_START_BOUNDARY(a) != MEM_BOUND ||
MEM_BOUND_UPPER(a->pointer, a->size - (2 * sizeof(MEM_BOUND_TYPE))) != MEM_BOUND) a->magic != MEM_MAGIC ||
MEM_END_BOUNDARY(a) != MEM_BOUND)
{ {
if (hook) if (hook)
{ {
@ -174,13 +203,14 @@ MemoryAllocate(size_t size, const char *file, int line)
p = a + 1; p = a + 1;
memset(p, 0, MEM_SIZE_ACTUAL(size)); memset(p, 0, MEM_SIZE_ACTUAL(size));
MEM_BOUND_LOWER(p) = MEM_BOUND;
MEM_BOUND_UPPER(p, size) = MEM_BOUND;
a->size = MEM_SIZE_ACTUAL(size); a->size = MEM_SIZE_ACTUAL(size);
a->unalignedSize = size;
strncpy(a->file, file, MEMORY_FILE_SIZE - 1); strncpy(a->file, file, MEMORY_FILE_SIZE - 1);
a->line = line; a->line = line;
a->pointer = p; a->pointer = p;
MEM_START_BOUNDARY(a) = MEM_BOUND;
MEM_END_BOUNDARY(a) = MEM_BOUND;
if (!MemoryInsert(a)) if (!MemoryInsert(a))
{ {
@ -195,7 +225,7 @@ MemoryAllocate(size_t size, const char *file, int line)
} }
pthread_mutex_unlock(&lock); pthread_mutex_unlock(&lock);
return ((MEM_BOUND_TYPE *) p) + 1; return p;
} }
void * void *
@ -220,6 +250,7 @@ MemoryReallocate(void *p, size_t size, const char *file, int line)
if (new) if (new)
{ {
a = new; a = new;
a->unalignedSize = size;
a->size = MEM_SIZE_ACTUAL(size); a->size = MEM_SIZE_ACTUAL(size);
strncpy(a->file, file, MEMORY_FILE_SIZE - 1); strncpy(a->file, file, MEMORY_FILE_SIZE - 1);
a->line = line; a->line = line;
@ -228,8 +259,8 @@ MemoryReallocate(void *p, size_t size, const char *file, int line)
a->pointer = a + 1; a->pointer = a + 1;
MemoryInsert(a); MemoryInsert(a);
MEM_BOUND_LOWER(a->pointer) = MEM_BOUND; MEM_START_BOUNDARY(a) = MEM_BOUND;
MEM_BOUND_UPPER(a->pointer, size) = MEM_BOUND; MEM_END_BOUNDARY(a) = MEM_BOUND;
if (hook) if (hook)
{ {
@ -253,7 +284,7 @@ MemoryReallocate(void *p, size_t size, const char *file, int line)
} }
} }
return ((MEM_BOUND_TYPE *) a->pointer) + 1; return a->pointer;
} }
void void
@ -344,8 +375,12 @@ MemoryInfoGet(void *po)
pthread_mutex_lock(&lock); pthread_mutex_lock(&lock);
p = ((MEM_BOUND_TYPE *) po) - 1;
p = ((MemoryInfo *) p) - 1; p = ((MemoryInfo *) p) - 1;
if (p < heapStart || p > heapEnd)
{
pthread_mutex_unlock(&lock);
return NULL;
}
if (((MemoryInfo *)p)->magic != MEM_MAGIC) if (((MemoryInfo *)p)->magic != MEM_MAGIC)
{ {
@ -364,7 +399,7 @@ MemoryInfoGetSize(MemoryInfo * a)
return 0; return 0;
} }
return a->size ? a->size - (2 * sizeof(MEM_BOUND_TYPE)) : 0; return a->size ? a->unalignedSize : 0;
} }
const char * const char *
@ -397,7 +432,7 @@ MemoryInfoGetPointer(MemoryInfo * a)
return NULL; return NULL;
} }
return ((MEM_BOUND_TYPE *) a->pointer) + 1; return a->pointer;
} }
void void