Compare commits

...

26 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
4f316ff7b3
Documentation generator doesn't support C99 comments. 2024-09-21 16:31:31 -04:00
6827d4fc39
Documentation is enforced. 2024-09-21 16:26:46 -04:00
63bd879101
Run CI on pull requests. 2024-09-21 16:23:06 -04:00
f7c51ee019 Basic work toward compiling on Darwin 2024-09-21 15:47:01 -04:00
af4a142261 Whoops, typo 2024-09-21 15:00:08 -04:00
10c8784f25 . 2024-09-21 14:59:13 -04:00
4a21567bc5 2nd CI attempt 2024-09-21 14:52:41 -04:00
ff094b50f2 First attempt to use new CI runner. (#55)
Reviewed-on: Telodendria/Cytoplasm#55
Co-authored-by: Jordan Bancino <jordan@bancino.net>
Co-committed-by: Jordan Bancino <jordan@bancino.net>
2024-09-21 18:46:43 +00:00
8987802437 Merge pull request 'Fix LMDB use-after-free' (#53) from lda/Cytoplasm:fix-deadlock into master
Reviewed-on: Telodendria/Cytoplasm#53
2024-09-13 20:11:12 -04:00
LDA
9fed42d2ac [FIX] Fix LMDB use-after-free 2024-09-09 13:05:30 +02:00
9c6781c458 Merge pull request 'Fix j2s generation issue' (#52) from lda/Cytoplasm:master into master
Reviewed-on: Telodendria/Cytoplasm#52
2024-09-02 14:38:02 -04:00
LDA
33139510b9 [FIX] Fix j2s generation issue
Aie...
2024-08-27 18:50:56 +02:00
4831f2e03d Merge pull request 'Add key->[known structure] schemas to j2s' (#50) from lda/Cytoplasm:master into master
Reviewed-on: Telodendria/Cytoplasm#50
2024-08-27 12:34:49 -04:00
lda
cc665ac7fc Merge branch 'master' into master 2024-08-27 12:25:28 -04:00
a121793795 Merge pull request 'Fix out the configure for the AUR' (#51) from lda/Cytoplasm:fix-configure into master
Reviewed-on: Telodendria/Cytoplasm#51
2024-08-27 09:44:49 -04:00
9e1026d893 Merge pull request 'Add raw SHA inputs and hashtypes to ShaToHex' (#47) from lda/Cytoplasm:sha-revamp into master
Reviewed-on: Telodendria/Cytoplasm#47
2024-08-27 09:38:26 -04:00
LDA
5889bec95f [MOD] Use macro to determine if it is delimited 2024-08-27 11:24:18 +02:00
lda
d0b5c441dd [FIX] Fix out the configure 2024-08-26 21:28:44 +02:00
LDA
d3379d8157 [ADD] {Field}s in j2s 2024-08-26 15:38:20 +02:00
14 changed files with 558 additions and 62 deletions

View file

@ -0,0 +1,18 @@
name: Compile Cytoplasm
run-name: Compile Cytoplasm on ${{ forgejo.actor }}
on: [push, pull_request]
jobs:
"Compile Cytoplasm":
strategy:
matrix:
os: [alpine]
arch: [aarch64]
runs-on: ["${{ matrix.os }}", "${{ matrix.arch }}"]
steps:
- name: Check out repository
uses: actions/checkout@v4
- name: Configure Cytoplasm
run: ./configure
- name: Build Cytoplasm
run: make

View file

@ -1,25 +0,0 @@
name: Compile Cytoplasm
run-name: Compile Cytoplasm on ${{ gitea.actor }}
on: [push]
jobs:
"Compile Cytoplasm":
strategy:
matrix:
os: [debian-v12.4, alpine-v3.19, openbsd-v7.4, freebsd-v14.0, netbsd-v9.3]
arch: [x86, x86_64]
exclude:
# 32-bit OpenBSD does not behave well in QEMU. Even when using
# QEMU to emulate i386, it utilizes 100% of its CPU core and is
# still extremely sluggish. Thus, we don't have a working 32-bit
# OpenBSD runner, so exclude it from the matrix configuration.
- os: openbsd-v7.4
arch: x86
runs-on: ["${{ matrix.os }}", "${{ matrix.arch }}"]
steps:
- name: Check out repository
uses: actions/checkout@v3
- name: Configure Cytoplasm
run: ./configure
- name: Build Cytoplasm
run: make

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.
- Fixed a bug in `ArraySort()` that would crash programs if the passed array has no
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

5
configure vendored
View file

@ -37,7 +37,7 @@ case "$(uname)" in
# These systems typically use GCC.
SCRIPT_ARGS="${SCRIPT_ARGS} --cc=gcc"
;;
OpenBSD|FreeBSD)
OpenBSD|FreeBSD|Darwin)
# These systems typically use Clang.
SCRIPT_ARGS="${SCRIPT_ARGS} --cc=clang"
;;
@ -269,7 +269,8 @@ ${TAB}done
${LIB_NAME}: ${OUT}/lib/lib${LIB_NAME}.a ${OUT}/lib/lib${LIB_NAME}.so
install: ${LIB_NAME}
${TAB}mkdir -p ${OUT}/lib
${TAB}mkdir -p \$(PREFIX)/${OUT}/lib
${TAB}mkdir -p \$(PREFIX)/lib
${TAB}cp ${OUT}/lib/lib${LIB_NAME}.a \$(PREFIX)/lib/lib${LIB_NAME}.a
${TAB}cp ${OUT}/lib/lib${LIB_NAME}.so \$(PREFIX)/lib/lib${LIB_NAME}.so
$(collect ${INCLUDE}/ '' '' \$\(PREFIX\)/include/${LIB_NAME}/ install_out)

View file

@ -119,6 +119,13 @@ extern void DbMaxCacheSet(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
* 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,...);
/**
* Behaves like
* .Fn DbLock ,
* but with an array of strings instead of variadic arguments.
*/
extern DbRef * DbLockArgs(Db *, Array *);
/**
* Behaves like
* .Fn DbLock ,
@ -138,6 +152,13 @@ extern DbRef * DbLock(Db *, 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.
* 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,...);
/**
* 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
* 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,...);
/**
* 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
* 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,...);
/**
* Behaves like
* .Fn DbList ,
* but with an array of strings instead of variadic arguments.
*/
extern Array * DbListArgs(Db *, Array *);
/**
* Free the list returned by
* .Fn DbListFree .

View file

@ -297,6 +297,12 @@ extern size_t JsonEncode(HashMap *, Stream *, int);
*/
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
* arbitrarily deep keys within a JSON object. It takes a root JSON

View file

@ -0,0 +1,69 @@
/*
* Copyright (C) 2022-2024 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_PLATFORM_H
#define CYTOPLASM_PLATFORM_H
/***
* @Nm Platform
* @Nd A simple macro header that determines what platform the application
* is being built for.
* @Dd September 21, 2024
*/
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
#define PLATFORM_WINDOWS
#ifdef _WIN64
#define PLATFORM_WIN64
#else
#define PLATFORM_WIN32
#endif
#elif __APPLE__
#define PLATFORM_DARWIN
#include <TargetConditionals.h>
#if TARGET_IPHONE_SIMULATOR
#define PLATFORM_IPHONE
#elif TARGET_OS_MACCATALYST
#define PLATFORM_CATALYST
#elif TARGET_OS_IPHONE
#define PLATFORM_IPHONE
#elif TARGET_OS_MAC
#define PLATFORM_MAC
#else
# error "Unknown Apple platform"
#endif
#elif __ANDROID__
#define PLATFORM_ANDROID
#elif __linux__
#define PLATFORM_LINUX
#elif __unix__
#define PLATFORM_UNIX
#elif defined(_POSIX_VERSION)
#define PLATFORM_POSIX
#else
# error "Unknown compiler"
#endif
#endif /* CYTOPLASM_PLATFORM_H */

View file

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

View file

@ -54,7 +54,8 @@ DbDirName(FlatDb * db, Array * args, size_t strip)
{
char *tmp;
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]);
}

View file

@ -128,7 +128,9 @@ LMDBKeyHead(MDB_val key)
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--;
}
@ -293,6 +295,7 @@ LMDBUnlock(Db *d, DbRef *r)
Stream *stream;
MDB_val key, val;
bool ret = true;
DbHint hint = r ? r->hint : 0;
if (!d || !r)
{
@ -324,7 +327,7 @@ LMDBUnlock(Db *d, DbRef *r)
{
free(val.mv_data);
}
if (ret && r->hint == DB_HINT_WRITE)
if (ret && hint == DB_HINT_WRITE)
{
pthread_mutex_unlock(&d->lock);
}

View file

@ -1348,6 +1348,25 @@ JsonDecode(Stream * stream)
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 *
JsonGet(HashMap * json, size_t nArgs,...)

View file

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

View file

@ -24,6 +24,7 @@
#include <Util.h>
#include <Memory.h>
#include <Platform.h>
#include <stdio.h>
#include <stdlib.h>
@ -89,6 +90,10 @@ UtilTsMillis(void)
return ts;
}
#ifdef PLATFORM_DARWIN
#define st_mtim st_mtimespec
#endif
uint64_t
UtilLastModified(char *path)
{
@ -105,6 +110,10 @@ UtilLastModified(char *path)
return ts;
}
#ifdef PLATFORM_DARWIN
#undef st_mtim
#endif
int
UtilMkdir(const char *dir, const mode_t mode)
{

View file

@ -38,6 +38,7 @@
#define MAX_DEPENDENCIES 32
#define IsDelimited(field, c1, c2) (*field == c1 && field[strlen(field) - 1] == c2)
static char *
Trim(char c, char *str)
{
@ -75,10 +76,14 @@ TypeToJsonType(char *type)
}
else
{
if (*type == '[' && type[strlen(type) - 1] == ']')
if (IsDelimited(type, '[', ']'))
{
return JSON_ARRAY;
}
else if (IsDelimited(type, '{', '}'))
{
return JSON_OBJECT;
}
else
{
return JSON_OBJECT;
@ -325,6 +330,7 @@ Main(Array * args)
{
char *fieldType;
bool isArrType = false;
bool isObjType = false;
JsonValue *requiredVal;
JsonValue *ignoreVal;
@ -343,12 +349,18 @@ Main(Array * args)
goto finish;
}
if (*fieldType == '[' && fieldType[strlen(fieldType) - 1] == ']')
if (IsDelimited(fieldType, '[', ']'))
{
fieldType++;
fieldType[strlen(fieldType) - 1] = '\0';
isArrType = true;
}
else if (IsDelimited(fieldType, '{', '}'))
{
fieldType++;
fieldType[strlen(fieldType) - 1] = '\0';
isObjType = true;
}
if (!StrEquals(fieldType, "object") &&
!StrEquals(fieldType, "array") &&
@ -374,6 +386,10 @@ Main(Array * args)
{
fieldType[strlen(fieldType)] = ']';
}
else if (isObjType)
{
fieldType[strlen(fieldType)] = '}';
}
requiredVal = HashMapGet(fieldObj, "required");
if (requiredVal && JsonValueType(requiredVal) != JSON_BOOLEAN)
@ -529,11 +545,13 @@ Main(Array * args)
{
cType = "double";
}
else if (StrEquals(fieldType, "object"))
else if (StrEquals(fieldType, "object") ||
IsDelimited(fieldType, '{', '}'))
{
cType = "HashMap *";
}
else if (StrEquals(fieldType, "array") || (*fieldType == '[' && fieldType[strlen(fieldType) - 1] == ']'))
else if (StrEquals(fieldType, "array") ||
IsDelimited(fieldType, '[', ']'))
{
cType = "Array *";
}
@ -542,8 +560,21 @@ Main(Array * args)
cType = fieldType;
}
if (IsDelimited(fieldType, '{', '}') ||
IsDelimited(fieldType, '[', ']'))
{
char end = fieldType[strlen(fieldType) - 1];
fieldType[strlen(fieldType) - 1] = '\0';
StreamPrintf(headerFile, " %s /* of %s */ %s;\n", cType, fieldType + 1, field);
fieldType[strlen(fieldType)] = end;
}
else
{
StreamPrintf(headerFile, " %s %s;\n", cType, field);
}
}
StreamPrintf(headerFile, "} %s;\n\n", type);
}
@ -673,7 +704,134 @@ Main(Array * args)
StreamPrintf(implFile, " out->%s = JsonValueAsObject(val);\n", key);
StreamPrintf(implFile, " Free(val); /* Not JsonValueFree() because we want the inner value. */\n");
}
else if (*fieldType == '[' && fieldType[strlen(fieldType) - 1] == ']')
else if (IsDelimited(fieldType, '{', '}'))
{
fieldType++;
fieldType[strlen(fieldType) - 1] = '\0';
isEnum = StrEquals(JsonValueAsString(JsonGet(types, 2, fieldType, "type")), "enum");
jsonType = isEnum ? JSON_STRING : TypeToJsonType(fieldType);
StreamPrintf(implFile, " out->%s = HashMapCreate();\n", key);
StreamPrintf(implFile, " if (!out->%s)\n", key);
StreamPrintf(implFile, " {\n");
StreamPrintf(implFile, " *errp = \"Failed to allocate memory for %s.%s.\";\n", type, key);
StreamPrintf(implFile, " return false;\n");
StreamPrintf(implFile, " }\n");
StreamPrintf(implFile, " else\n");
StreamPrintf(implFile, " {\n");
StreamPrintf(implFile, " HashMap *obj = JsonValueAsObject(val);\n");
StreamPrintf(implFile, " char *objKey;\n");
StreamPrintf(implFile, " JsonValue *v;\n");
StreamPrintf(implFile, "\n");
StreamPrintf(implFile, " while (HashMapIterate(obj, &objKey, (void **) &v))\n");
StreamPrintf(implFile, " {\n");
if (StrEquals(fieldType, "integer") ||
StrEquals(fieldType, "float") ||
StrEquals(fieldType, "boolean"))
{
char *cType;
if (StrEquals(fieldType, "integer"))
{
cType = "int64_t";
}
else if (StrEquals(fieldType, "float"))
{
cType = "double";
}
else if (StrEquals(fieldType, "boolean"))
{
cType = "bool";
}
else
{
/* Should never happen */
cType = NULL;
}
*fieldType = toupper(*fieldType);
StreamPrintf(implFile, " %s *ref;\n", cType);
StreamPrintf(implFile, " if (JsonValueType(v) != %s)\n", JsonTypeToStr(jsonType));
StreamPrintf(implFile, " {\n");
StreamPrintf(implFile, " *errp = \"%s.%s{} contains an invalid value.\";\n", type, key);
StreamPrintf(implFile, " return false;\n");
StreamPrintf(implFile, " }\n");
StreamPrintf(implFile, " ref = Malloc(sizeof(%s));\n", cType);
StreamPrintf(implFile, " if (!ref)\n");
StreamPrintf(implFile, " {\n");
StreamPrintf(implFile, " *errp = \"Unable to allocate memory for object value.\";\n");
StreamPrintf(implFile, " return false;\n");
StreamPrintf(implFile, " }\n");
StreamPrintf(implFile, " *ref = JsonValueAs%s(v);\n", fieldType);
StreamPrintf(implFile, " HashMapSet(out->%s, objKey, ref);\n", key);
*fieldType = tolower(*fieldType);
}
else if (StrEquals(fieldType, "string"))
{
StreamPrintf(implFile, " if (JsonValueType(v) != %s)\n", JsonTypeToStr(jsonType));
StreamPrintf(implFile, " {\n");
StreamPrintf(implFile, " *errp = \"%s.%s[] contains an invalid value.\";\n", type, key);
StreamPrintf(implFile, " return false;\n");
StreamPrintf(implFile, " }\n");
StreamPrintf(implFile, " HashMapSet(out->%s, objKey, StrDuplicate(JsonValueAsString(v)));\n", key);
}
else if (StrEquals(fieldType, "object"))
{
StreamPrintf(implFile, " if (JsonValueType(v) != %s)\n", JsonTypeToStr(jsonType));
StreamPrintf(implFile, " {\n");
StreamPrintf(implFile, " *errp = \"%s.%s[] contains an invalid value.\";\n", type, key);
StreamPrintf(implFile, " return false;\n");
StreamPrintf(implFile, " }\n");
StreamPrintf(implFile, " HashMapSet(out->%s, objKey, JsonDuplicate(JsonValueAsObject(v)));\n", key);
}
else
{
if (isEnum)
{
StreamPrintf(implFile, " int parseResult;\n");
}
StreamPrintf(implFile, " %s *parsed;\n", fieldType);
StreamPrintf(implFile, " if (JsonValueType(v) != %s)\n", JsonTypeToStr(jsonType));
StreamPrintf(implFile, " {\n");
StreamPrintf(implFile, " *errp = \"%s.%s[] contains an invalid value.\";\n", type, key);
StreamPrintf(implFile, " return false;\n");
StreamPrintf(implFile, " }\n");
StreamPrintf(implFile, " parsed = Malloc(sizeof(%s));\n", fieldType);
StreamPrintf(implFile, " if (!parsed)\n");
StreamPrintf(implFile, " {\n");
StreamPrintf(implFile, " *errp = \"Unable to allocate memory for array value.\";\n");
StreamPrintf(implFile, " return false;\n");
StreamPrintf(implFile, " }\n");
if (isEnum)
{
StreamPrintf(implFile, " parseResult = %sFromStr(JsonValueAsString(v));\n", fieldType);
StreamPrintf(implFile, " *parsed = parseResult;\n");
StreamPrintf(implFile, " if (parseResult == -1)\n");
}
else
{
StreamPrintf(implFile, " if (!%sFromJson(JsonValueAsObject(v), parsed, errp))\n", fieldType);
}
StreamPrintf(implFile, " {\n");
if (!isEnum)
{
StreamPrintf(implFile, " %sFree(parsed);\n", fieldType);
}
StreamPrintf(implFile, " Free(parsed);\n");
StreamPrintf(implFile, " return false;\n");
StreamPrintf(implFile, " }\n");
StreamPrintf(implFile, " HashMapSet(out->%s, objKey, parsed);\n", key);
}
StreamPrintf(implFile, " }\n");
StreamPrintf(implFile, " }\n");
fieldType[strlen(fieldType)] = '}';
}
else if (IsDelimited(fieldType, '[', ']'))
{
fieldType++;
fieldType[strlen(fieldType) - 1] = '\0';
@ -915,7 +1073,73 @@ Main(Array * args)
{
StreamPrintf(implFile, " HashMapSet(json, \"%s\", JsonValueObject(JsonDuplicate(val->%s)));\n", Trim('_', key), key);
}
else if (*fieldType == '[' && fieldType[strlen(fieldType) - 1] == ']')
else if (IsDelimited(fieldType, '{', '}'))
{
int isPrimitive;
fieldType++;
fieldType[strlen(fieldType) - 1] = '\0';
isEnum = StrEquals(JsonValueAsString(JsonGet(types, 2, fieldType, "type")), "enum");
isPrimitive = StrEquals(fieldType, "integer") ||
StrEquals(fieldType, "boolean") ||
StrEquals(fieldType, "float");
StreamPrintf(implFile, " if (val->%s)\n", key);
StreamPrintf(implFile, " {\n");
StreamPrintf(implFile, " char *objKey;\n");
StreamPrintf(implFile, " void *objVal;\n");
StreamPrintf(implFile, " HashMap *jsonObj = HashMapCreate();\n");
StreamPrintf(implFile, " if (!jsonObj)\n");
StreamPrintf(implFile, " {\n");
StreamPrintf(implFile, " JsonFree(json);\n");
StreamPrintf(implFile, " return NULL;\n");
StreamPrintf(implFile, " }\n");
StreamPrintf(implFile, " while (HashMapIterate(val->%s, &objKey, &objVal))\n", key);
StreamPrintf(implFile, " {\n");
if (StrEquals(fieldType, "string"))
{
StreamPrintf(implFile, " HashMapSet(jsonObj, objKey, JsonValueString(objVal));\n", key);
}
else if (!isPrimitive)
{
StreamPrintf(implFile, " HashMapSet(jsonObj, objKey, JsonValueObject(%sToJson(objVal)));\n", fieldType, key);
}
else
{
char *cType;
if (StrEquals(fieldType, "integer"))
{
cType = "int64_t";
}
else if (StrEquals(fieldType, "float"))
{
cType = "double";
}
else if (StrEquals(fieldType, "boolean"))
{
cType = "bool";
}
else
{
/* Should never happen */
cType = NULL;
}
*fieldType = toupper(*fieldType);
StreamPrintf(implFile, " HashMapSet(jsonObj, objKey, (JsonValue%s(*((%s *) objVal))));\n", fieldType, cType, key);
*fieldType = tolower(*fieldType);
}
StreamPrintf(implFile, " }\n");
StreamPrintf(implFile, " HashMapSet(json, \"%s\", JsonValueObject(jsonObj));\n", Trim('_', key));
StreamPrintf(implFile, " }\n");
fieldType[strlen(fieldType)] = '}';
}
else if (IsDelimited(fieldType, '[', ']'))
{
int isPrimitive;
@ -1036,7 +1260,36 @@ Main(Array * args)
{
StreamPrintf(implFile, " Free(val->%s);\n", key);
}
else if (*fieldType == '[' && fieldType[strlen(fieldType) - 1] == ']')
else if (IsDelimited(fieldType, '{', '}'))
{
int isPrimitive;
fieldType++;
fieldType[strlen(fieldType) - 1] = '\0';
isEnum = StrEquals(JsonValueAsString(JsonGet(types, 2, fieldType, "type")), "enum");
isPrimitive = StrEquals(fieldType, "boolean") ||
StrEquals(fieldType, "float") ||
StrEquals(fieldType, "integer") ||
StrEquals(fieldType, "string");
StreamPrintf(implFile, " if (val->%s)\n", key);
StreamPrintf(implFile, " {\n");
StreamPrintf(implFile, " char *objKey;\n");
StreamPrintf(implFile, " void *objVal;\n");
StreamPrintf(implFile, " while (HashMapIterate(val->%s, &objKey, &objVal))\n", key);
StreamPrintf(implFile, " {\n");
StreamPrintf(implFile, " %sFree(objVal);\n", (!isEnum && !isPrimitive) ? fieldType : "", key);
if (!isEnum && !isPrimitive)
{
StreamPrintf(implFile, " Free(objVal);\n", key);
}
StreamPrintf(implFile, " }\n");
StreamPrintf(implFile, " HashMapFree(val->%s);\n", key);
StreamPrintf(implFile, " }\n");
fieldType[strlen(fieldType)] = '}';
}
else if (IsDelimited(fieldType, '[', ']'))
{
int isPrimitive;