forked from Telodendria/Cytoplasm
Compare commits
96 commits
meta-j2s-t
...
master
Author | SHA1 | Date | |
---|---|---|---|
32f31fe6d6 | |||
f3838e775c | |||
|
100d61050f | ||
b4841fffaa | |||
|
39e81139f0 | ||
8df5f0f1c1 | |||
|
708c5daad9 | ||
4f316ff7b3 | |||
6827d4fc39 | |||
63bd879101 | |||
f7c51ee019 | |||
af4a142261 | |||
10c8784f25 | |||
4a21567bc5 | |||
ff094b50f2 | |||
8987802437 | |||
|
9fed42d2ac | ||
9c6781c458 | |||
|
33139510b9 | ||
4831f2e03d | |||
|
cc665ac7fc | ||
a121793795 | |||
9e1026d893 | |||
|
5889bec95f | ||
d0b5c441dd | |||
|
d3379d8157 | ||
|
5df458f568 | ||
|
adb7322823 | ||
f5ce4f5238 | |||
|
7752ea7b86 | ||
|
d00dcc9b50 | ||
494be7b4dc | |||
b6b915530c | |||
56257fb3da | |||
1d0eb9d49a | |||
39c25e5a17 | |||
|
c7204f316c | ||
|
e8543bdb2a | ||
|
3843a8d114 | ||
|
cf1b78b224 | ||
|
e133eebef3 | ||
|
3df1e4ab7b | ||
|
f32cdb7d89 | ||
|
20bb7a20ad | ||
|
f6af2cd782 | ||
|
2b061f1226 | ||
4903c075e8 | |||
|
a90c66736c | ||
|
004c53a028 | ||
|
0743401955 | ||
|
5cb51a4d58 | ||
|
59dbfae1ae | ||
|
b87979e9a2 | ||
|
87d9421f11 | ||
|
da857a3d53 | ||
|
0d122976d4 | ||
c3646294f5 | |||
61b5430efe | |||
|
b3be10e112 | ||
|
b284fb607a | ||
|
402d73c866 | ||
dd99759e39 | |||
bd310e62ac | |||
|
d7faff734c | ||
|
bec672c92c | ||
|
5dc1ec49eb | ||
|
eca717d90c | ||
|
5fac67a674 | ||
|
8b2bdbe220 | ||
|
1ad6f0d976 | ||
|
9f706102c4 | ||
5003ddc281 | |||
b9dc169917 | |||
d983296dd2 | |||
007b8f6d43 | |||
9108fef701 | |||
138ea1c8e9 | |||
cdf4430a9e | |||
346b912a06 | |||
5d87da31cd | |||
461357b526 | |||
cd7df7bbb4 | |||
c489eff517 | |||
039a487bdf | |||
31c9bc7f1c | |||
11e0a34c02 | |||
08166dd3a7 | |||
c3429f035c | |||
662696ce12 | |||
d0969d0dd7 | |||
d7da8e0a54 | |||
|
44b7f45bb7 | ||
3788d044e6 | |||
928e9c8223 | |||
17f1a41519 | |||
b6388eb7fe |
79 changed files with 2845 additions and 3049 deletions
18
.forgejo/workflows/compile.yaml
Normal file
18
.forgejo/workflows/compile.yaml
Normal 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
|
10
CHANGELOG.md
10
CHANGELOG.md
|
@ -7,6 +7,9 @@ releases. Final change log entries are published as [Releases](releases).
|
|||
|
||||
## v0.4.1
|
||||
|
||||
Cytoplasm is now a C99 library! Upgrading from C89 to C99 makes Cytoplasm more portable
|
||||
than ever.
|
||||
|
||||
### New Features
|
||||
|
||||
- Added an option to `j2s` to allow additional fields in structures and ignore them in
|
||||
|
@ -15,6 +18,11 @@ are not even initialized to a default value.
|
|||
- Fixed a memory leak that would occur in code generated by `j2s` under
|
||||
specific circumstances.
|
||||
- Added `JsonMerge()` to the JSON API to merge two JSON objects together.
|
||||
- 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
|
||||
|
||||
|
@ -26,4 +34,4 @@ project with its own independent releases. This allows it to develop at a much m
|
|||
rapid pace than Telodendria.
|
||||
|
||||
Changes in future releases will be reported here. Since this is the first release,
|
||||
there are no changes to show.
|
||||
there are no changes to show.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
||||
|
|
34
README.md
34
README.md
|
@ -1,15 +1,15 @@
|
|||
<p align="center"><img src="https://telodendria.io/assets/Cytoplasm.png"></p>
|
||||
<p align="center"><img src="https://telodendria.io/user/themes/bancino/images/logo/Cytoplasm.png"></p>
|
||||
<h1 align="center">Cytoplasm (<code>libcytoplasm</code>)</h1>
|
||||
|
||||
Cytoplasm is a general-purpose C library for creating high-level (particularly networked and multi-threaded) C applications. It allows applications to take advantage of the speed, flexibility, and simplicity of the C programming language, while providing helpful code to allow applications to perform various complex tasks with minimal effort. Cytoplasm provides high-level data structures, a basic logging facility, an HTTP client and server, and more. It also reports memory leaks, which can aid in debugging.
|
||||
Cytoplasm is a general-purpose C library for creating high-level (particularly networked and multi-threaded) C applications. It allows applications to take advantage of the speed, flexibility, and simplicity of the C programming language, while providing helpful code to allow applications to perform various complex tasks with minimal effort. Cytoplasm provides high-level data structures, a basic logging facility, an HTTP client and server, and more. It also reports memory leaks, which can aid in debugging, particularly on systems that don't have advanced tools like `valgrind`.
|
||||
|
||||
Cytoplasm aims not to only do one thing well, but to do many things good enough. This is in contrast to other libraries, which only do one thing and thus require the developer to pull in many different libraries. The primary target of Cytoplasm is simple yet higher level C applications that have to perform relatively complex tasks, but don't want to depend on a large number of dependencies.
|
||||
Cytoplasm aims not to only do one thing well, but to do many things good enough. This is in contrast to other libraries, which only do one thing and thus require the developer to pull in many different libraries for a broad range of functionality. The primary target of Cytoplasm is simple yet higher level C applications that have to perform relatively complex tasks, but don't want to depend on a large number of dependencies.
|
||||
|
||||
Cytoplasm is extremely opinionated on the way programs using it are written. It strives to create a comprehensive and tightly-integrated programming environment, while also maintaining C programming correctness. It doesn't do any macro magic or make C look like anything other than C. It is written entirely in C89, and depends only on a POSIX environment. This differentiates it from other general-purpose libraries that often require modern compilers and non-standard language and environment features. Cytoplasm is intended to be extremely portable and simple, while still providing some of the functionality expected in higher-level programming languages in a platform-agnostic manner. In the case of TLS, Cytoplasm wraps low-level TLS libraries to offer a single, unified interface to TLS so that programs do not have to care about the underlying implementation.
|
||||
Cytoplasm is extremely opinionated on the way programs using it are written. It strives to create a comprehensive and tightly-integrated programming environment, while also maintaining C programming correctness. It doesn't do any macro magic or make C look like anything other than C. It is written entirely in C99, and depends only on a POSIX environment. This differentiates it from other general-purpose libraries that often require more modern compilers and non-standard language and environment features. Cytoplasm is intended to be extremely portable and simple, while still providing some of the functionality expected in higher-level programming languages in a platform-agnostic manner. In the case of TLS, Cytoplasm wraps low-level TLS libraries to offer a single, unified interface to TLS so that programs do not have to care about the underlying implementation.
|
||||
|
||||
Cytoplasm is probably not suitable for embedded programming. It makes liberal use of the heap, and while data structures are designed to conserve memory where possible and practical, minimal memory usage is not really a design goal for Cytoplasm, although Cytoplasm takes care not to use any more memory than it absolutely needs. Cytoplasm also wraps a few standard libraries with additional logic and checking. While this ensures better runtime safety, this inevitably adds a little overhead, which may be unsuitable for time- or space-critical tasks.
|
||||
|
||||
Originally a part of Telodendria (https://telodendria.io), a Matrix homeserver written in C, Cytoplasm was split off into its own project due to the desire of some Telodendria developers to use Telodendria's code in other projects. Cytoplasm is still an official Telodendria project, but it is designed specifically to be distributed and used totally independent of Telodendria.
|
||||
Originally a part of Telodendria ([Website](https://telodendria.io), [Repo](/Telodendria/Telodendria)), a Matrix homeserver written in C, Cytoplasm was split off into its own project due to the desire of some Telodendria developers to use Telodendria's code in other projects. Cytoplasm is still an official Telodendria project, but it is designed specifically to be distributed and used totally independent of Telodendria.
|
||||
|
||||
The name "Cytoplasm" was chosen for a few reasons. It plays off the precedent set up by the Matrix organization in naming projects after the parts of a neuron. It also speaks to the function of Cytoplasm. The cytoplasm of a cell is the supporting material. It is what gives the cell its shape, and it facilitates the movement of materials to the other cell parts. Likewise, Cytoplasm aims to provide a support mechanism for C applications that have to perform complex tasks beyond what the C standard library provides.
|
||||
|
||||
|
@ -17,14 +17,7 @@ Cytoplasm also starts with a C, which I think is a nice touch for C libraries. I
|
|||
|
||||
## Requirements
|
||||
|
||||
Cytoplasm makes the following assumptions about the underlying hardware:
|
||||
|
||||
- It has words sizes that are powers of 2, and a native 32-bit integer type exists.
|
||||
- Integers are represented using two's compliment for negatives.
|
||||
|
||||
The ANSI C standard requires an integer type of at least 32 bits, but does not require any more. If Cytoplasm is built on 32-bit platforms that don't provide a native 64-bit integer type, Cytoplasm emulates 64-bit integers. This can make it more portable.
|
||||
|
||||
Cytoplasm aims to have zero software dependencies beyond what is mandated by POSIX. You only need the standard math and pthread libraries to build it. TLS support can optionally be enabled with the configuration script. The supported TLS implementations are as follows:
|
||||
Cytoplasm aims to have zero software dependencies beyond what is mandated by POSIX. You only need a standard C99 compiler, and the standard `math` and `pthread` libraries to build Cytoplasm. TLS support can optionally be enabled with the configuration script. The supported TLS implementations are as follows:
|
||||
|
||||
- OpenSSL
|
||||
- LibreSSL
|
||||
|
@ -60,14 +53,11 @@ The `configure` script has a number of optional flags, which are as follows:
|
|||
- `--with-(openssl|libressl)`: Select the TLS implementation to use. OpenSSL is selected by default.
|
||||
- `--disable-tls`: Disable TLS altogether.
|
||||
- `--prefix=<path>`: Set the install prefix to set by default in the `Makefile`. This defaults to `/usr/local`, which should be appropriate for most Unix-like systems.
|
||||
- `--(enable|disable)-ld-extra`: Control whether or not to enable additional linking flags that create a more optimized binary. For large compilers such as GCC and Clang, these flags should be enabled. However, if you are using a small or more obscure compiler, then these flags may not be supported, so you can disable them with this option.
|
||||
- `--(enable|disable)-debug`: Control whether or not to enable debug mode. This sets the optimization level to 0 and builds with debug symbols. Useful for running with a debugger.
|
||||
- `--static` and `--no-static`: Controls whether static binaries for tools are built by default. On BSD systems, `--static` is perfectly acceptable, but on GNU systems, `--no-static` is often desirable to silence warnings about static binaries emitted by the GNU linker.
|
||||
|
||||
Cytoplasm can be customized with the following options:
|
||||
|
||||
- `--lib-name=<name>`: The output name of the library. This defaults to `Cytoplasm` and should in most cases not be changed.
|
||||
- `--lib-version=<version>`: The version string to embed in the library binaries. This can be used to indicate build customizations or non-release versions of Cytoplasm.
|
||||
|
||||
The following recipes are available in the generated `Makefile`:
|
||||
|
||||
|
@ -96,13 +86,13 @@ Cytoplasm provides the typical .so and .a files, which can be used to link progr
|
|||
Here is the canonical Hello World written with Cytoplasm:
|
||||
|
||||
```c
|
||||
#include <Cytoplasm/Log.h>
|
||||
#include <Cytoplasm/Log.h>
|
||||
|
||||
int Main(void)
|
||||
{
|
||||
Log(LOG_INFO, "Hello World!");
|
||||
return 0;
|
||||
}
|
||||
int Main(void)
|
||||
{
|
||||
Log(LOG_INFO, "Hello World!");
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
If this file is `Hello.c`, then you can compile it by doing this:
|
||||
|
|
127
configure
vendored
127
configure
vendored
|
@ -10,26 +10,47 @@ echo "-------------------"
|
|||
BUILD="build"
|
||||
OUT="out"
|
||||
SRC="src"
|
||||
INCLUDE="src/include"
|
||||
INCLUDE="include/Cytoplasm"
|
||||
TOOLS="tools"
|
||||
|
||||
CFLAGS="-Wall -Wextra -pedantic -std=c89 -O3 -pipe -D_DEFAULT_SOURCE -I${INCLUDE}"
|
||||
LIBS="-lm -pthread"
|
||||
# Default compiler flags. These must be supported by all POSIX C compilers.
|
||||
# "Fancy" compilers that have additional options must be detected and set below.
|
||||
CFLAGS="-O1 -D_DEFAULT_SOURCE -I${INCLUDE} -I${SRC}";
|
||||
LIBS="-lm -lpthread"
|
||||
|
||||
# Default args for all platforms.
|
||||
SCRIPT_ARGS="--prefix=/usr/local --lib-name=Cytoplasm"
|
||||
|
||||
# Set default args for all platforms
|
||||
SCRIPT_ARGS="--cc=cc --prefix=/usr/local --enable-ld-extra --lib-name=Cytoplasm --lib-version=0.4.1 --static $@"
|
||||
|
||||
# Set platform specific args
|
||||
# Set SSL flags depending on the platform.
|
||||
case "$(uname)" in
|
||||
OpenBSD)
|
||||
SCRIPT_ARGS="--with-libressl $SCRIPT_ARGS"
|
||||
SCRIPT_ARGS="${SCRIPT_ARGS} --with-libressl --disable-lmdb"
|
||||
;;
|
||||
*)
|
||||
SCRIPT_ARGS="--with-openssl $SCRIPT_ARGS"
|
||||
SCRIPT_ARGS="${SCRIPT_ARGS} --with-openssl --disable-lmdb"
|
||||
;;
|
||||
esac
|
||||
|
||||
# Set compiler depending on the platform.
|
||||
case "$(uname)" in
|
||||
Linux|NetBSD)
|
||||
# These systems typically use GCC.
|
||||
SCRIPT_ARGS="${SCRIPT_ARGS} --cc=gcc"
|
||||
;;
|
||||
OpenBSD|FreeBSD|Darwin)
|
||||
# These systems typically use Clang.
|
||||
SCRIPT_ARGS="${SCRIPT_ARGS} --cc=clang"
|
||||
;;
|
||||
*)
|
||||
# Use default compiler which is required to be present on
|
||||
# all POSIX-compliant systems.
|
||||
SCRIPT_ARGS="${SCRIPT_ARGS} --cc=c99"
|
||||
;;
|
||||
esac
|
||||
|
||||
# Append any additional args specified by user
|
||||
SCRIPT_ARGS="${SCRIPT_ARGS} $@"
|
||||
|
||||
echo "Processing options..."
|
||||
echo "Ran with arguments: $SCRIPT_ARGS"
|
||||
|
||||
|
@ -38,6 +59,14 @@ for arg in $SCRIPT_ARGS; do
|
|||
case "$arg" in
|
||||
--cc=*)
|
||||
CC=$(echo "$arg" | cut -d '=' -f 2-)
|
||||
case "${CC}" in
|
||||
gcc*|clang*)
|
||||
# "Fancy" compilers that support a plethora of additional flags we
|
||||
# want to enable if present.
|
||||
CFLAGS="-Wall -Wextra -Werror -pedantic -std=c99 -O3 ${CFLAGS}"
|
||||
LDFLAGS="-flto -fdata-sections -ffunction-sections -s -Wl,-gc-sections"
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
--with-openssl)
|
||||
TLS_IMPL="TLS_OPENSSL"
|
||||
|
@ -51,38 +80,26 @@ for arg in $SCRIPT_ARGS; do
|
|||
TLS_IMPL=""
|
||||
TLS_LIBS=""
|
||||
;;
|
||||
--with-lmdb)
|
||||
EDB_IMPL="EDB_LMDB"
|
||||
EDB_LIBS="-llmdb"
|
||||
;;
|
||||
--disable-lmdb)
|
||||
EDB_IMPL=""
|
||||
EDB_LIBS=""
|
||||
;;
|
||||
--prefix=*)
|
||||
PREFIX=$(echo "$arg" | cut -d '=' -f 2-)
|
||||
;;
|
||||
--enable-ld-extra)
|
||||
LD_EXTRA="-flto -fdata-sections -ffunction-sections -s -Wl,-gc-sections"
|
||||
;;
|
||||
--disable-ld-extra)
|
||||
LD_EXTRA=""
|
||||
;;
|
||||
--lib-name=*)
|
||||
LIB_NAME=$(echo "$arg" | cut -d '=' -f 2-)
|
||||
;;
|
||||
--lib-version=*)
|
||||
LIB_VERSION=$(echo "$arg" | cut -d '=' -f 2-)
|
||||
;;
|
||||
--enable-debug)
|
||||
DEBUG="-O0 -g"
|
||||
echo "Notice: --enable-debug implies --disable-ld-extra and --no-static."
|
||||
echo "You must explicitly provide --enable-ld-extra and/or --static after"
|
||||
echo "specifying --enable-debug if you wish to enable these features in debug mode."
|
||||
LD_EXTRA=""
|
||||
STATIC=""
|
||||
;;
|
||||
--disable-debug)
|
||||
DEBUG=""
|
||||
;;
|
||||
--static)
|
||||
STATIC="-static -Wl,-static"
|
||||
;;
|
||||
--no-static)
|
||||
STATIC=""
|
||||
;;
|
||||
*)
|
||||
echo "Invalid argument: $arg"
|
||||
exit 1
|
||||
|
@ -95,8 +112,13 @@ if [ -n "$TLS_IMPL" ]; then
|
|||
LIBS="${LIBS} ${TLS_LIBS}"
|
||||
fi
|
||||
|
||||
CFLAGS="${CFLAGS} '-DLIB_NAME=\"${LIB_NAME}\"' '-DLIB_VERSION=\"${LIB_VERSION}\"' ${DEBUG}"
|
||||
LDFLAGS="${LIBS} ${LD_EXTRA}"
|
||||
if [ -n "$EDB_IMPL" ]; then
|
||||
CFLAGS="${CFLAGS} -D${EDB_IMPL}"
|
||||
LIBS="${LIBS} ${EDB_LIBS}"
|
||||
fi
|
||||
|
||||
CFLAGS="${CFLAGS} '-DLIB_NAME=\"${LIB_NAME}\"' ${DEBUG}"
|
||||
LDFLAGS="${LIBS} ${LDFLAGS}"
|
||||
|
||||
#
|
||||
# Makefile generation
|
||||
|
@ -128,11 +150,27 @@ print_obj() {
|
|||
printf '%s ' "$2"
|
||||
}
|
||||
|
||||
get_deps() {
|
||||
src="$1"
|
||||
|
||||
${CC} -I${SRC} -I${INCLUDE} -E "$src" \
|
||||
| grep '^#' \
|
||||
| awk '{print $3}' \
|
||||
| cut -d '"' -f 2 \
|
||||
| sort \
|
||||
| uniq \
|
||||
| grep -v '^[/<]' \
|
||||
| grep "^${SRC}/" \
|
||||
| while IFS= read -r dep; do
|
||||
printf "%s " "$dep"
|
||||
done
|
||||
}
|
||||
|
||||
compile_obj() {
|
||||
src="$1"
|
||||
obj="$2"
|
||||
|
||||
${CC} -I${INCLUDE} -MM -MT "${obj}" "${src}"
|
||||
echo "${obj}: $(get_deps ${src})"
|
||||
echo "${TAB}@mkdir -p $(dirname ${obj})"
|
||||
echo "${TAB}\$(CC) \$(CFLAGS) -fPIC -c -o \"${obj}\" \"${src}\""
|
||||
}
|
||||
|
@ -143,16 +181,13 @@ compile_bin() {
|
|||
|
||||
echo "${out}: ${OUT}/lib/lib${LIB_NAME}.a ${OUT}/lib/lib${LIB_NAME}.so ${src}"
|
||||
echo "${TAB}@mkdir -p ${OUT}/bin"
|
||||
echo "${TAB}\$(CC) \$(CFLAGS) -o \"${out}\" \"${src}\" -L${OUT}/lib \$(LDFLAGS) -l${LIB_NAME} ${STATIC}"
|
||||
echo "${TAB}\$(CC) \$(CFLAGS) -o \"${out}\" \"${src}\" -L${OUT}/lib \$(LDFLAGS) -l${LIB_NAME}"
|
||||
}
|
||||
|
||||
compile_doc() {
|
||||
src="$1"
|
||||
out="$2"
|
||||
|
||||
if [ -z "${STATIC}" ]; then
|
||||
pref="LD_LIBRARY_PATH=${OUT}/lib "
|
||||
fi
|
||||
pref="LD_LIBRARY_PATH=${OUT}/lib "
|
||||
|
||||
echo "${out}: ${OUT}/bin/hdoc ${src}"
|
||||
echo "${TAB}@mkdir -p ${OUT}/man/man3"
|
||||
|
@ -162,22 +197,28 @@ compile_doc() {
|
|||
install_out() {
|
||||
src="$1"
|
||||
out="$2"
|
||||
dir=$(dirname "$out")
|
||||
|
||||
echo "${TAB}install -D \"$src\" \"$out\""
|
||||
echo "${TAB}mkdir -p \"$dir\""
|
||||
echo "${TAB}cp \"$src\" \"$out\""
|
||||
}
|
||||
|
||||
install_man() {
|
||||
src="${OUT}/man/man3/${LIB_NAME}-$(basename $1 .h).3"
|
||||
out="$2"
|
||||
dir=$(dirname "$out")
|
||||
|
||||
echo "${TAB}install -D \"$src\" \"$out\""
|
||||
echo "${TAB}mkdir -p \"$dir\""
|
||||
echo "${TAB}cp \"$src\" \"$out\""
|
||||
}
|
||||
|
||||
install_tool() {
|
||||
src=${OUT}/bin/$(basename "$1" .c)
|
||||
out="$2"
|
||||
dir=$(dirname "$out")
|
||||
|
||||
echo "${TAB}install -D \"$src\" \"$out\""
|
||||
echo "${TAB}mkdir -p \"$dir\""
|
||||
echo "${TAB}cp \"$src\" \"$out\""
|
||||
}
|
||||
|
||||
uninstall_out() {
|
||||
|
@ -228,8 +269,10 @@ ${TAB}done
|
|||
${LIB_NAME}: ${OUT}/lib/lib${LIB_NAME}.a ${OUT}/lib/lib${LIB_NAME}.so
|
||||
|
||||
install: ${LIB_NAME}
|
||||
${TAB}install -D ${OUT}/lib/lib${LIB_NAME}.a \$(PREFIX)/lib/lib${LIB_NAME}.a
|
||||
${TAB}install -D ${OUT}/lib/lib${LIB_NAME}.so \$(PREFIX)/lib/lib${LIB_NAME}.so
|
||||
${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)
|
||||
$(collect ${INCLUDE}/ .h .3 \$\(PREFIX\)/man/man3/${LIB_NAME}- install_man)
|
||||
$(collect ${TOOLS}/ '.c' '' \$\(PREFIX\)/bin/ install_tool)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
||||
|
@ -52,6 +52,7 @@
|
|||
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/**
|
||||
* The functions in this API operate on an array structure which is
|
||||
|
@ -98,7 +99,7 @@ extern void *ArrayGet(Array *, size_t);
|
|||
* This function returns a boolean value indicating whether or not it
|
||||
* suceeded.
|
||||
*/
|
||||
extern int ArrayInsert(Array *, size_t, void *);
|
||||
extern bool ArrayInsert(Array *, size_t, void *);
|
||||
|
||||
/**
|
||||
* Set the value at the specified index in the specified array to the
|
||||
|
@ -115,7 +116,7 @@ extern void *ArraySet(Array *, size_t, void *);
|
|||
* return value as
|
||||
* .Fn ArrayInsert .
|
||||
*/
|
||||
extern int ArrayAdd(Array *, void *);
|
||||
extern bool ArrayAdd(Array *, void *);
|
||||
|
||||
/**
|
||||
* Remove the element at the specified index from the specified array.
|
||||
|
@ -146,7 +147,7 @@ extern void ArraySort(Array *, int (*) (void *, void *));
|
|||
* .P
|
||||
* This is a relatively expensive operation. The array must first be
|
||||
* duplicated. Then it is sorted, then it is iterated from beginning
|
||||
* to end to remove duplicate entires. Note that the comparison
|
||||
* to end to remove duplicate entries. Note that the comparison
|
||||
* function is executed on each element at least twice.
|
||||
*/
|
||||
extern Array *ArrayUnique(Array *, int (*) (void *, void *));
|
||||
|
@ -167,7 +168,7 @@ extern Array *ArrayReverse(Array *);
|
|||
* array. This function is intended to be used by functions that return
|
||||
* relatively read-only arrays that will be long-lived.
|
||||
*/
|
||||
extern int ArrayTrim(Array *);
|
||||
extern bool ArrayTrim(Array *);
|
||||
|
||||
/**
|
||||
* Convert a variadic arguments list into an Array. In most cases, the
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
||||
|
@ -39,6 +39,7 @@
|
|||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/**
|
||||
* This function computes the amount of bytes needed to store a message
|
||||
|
@ -93,7 +94,7 @@ extern void
|
|||
* this means it will only fail if a bigger string is necessary, but it
|
||||
* could not be automatically allocated on the heap.
|
||||
*/
|
||||
extern int
|
||||
extern bool
|
||||
Base64Pad(char **, size_t);
|
||||
|
||||
#endif /* CYTOPLASM_BASE64_H */
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
||||
|
@ -24,6 +24,8 @@
|
|||
#ifndef CYTOPLASM_CRON_H
|
||||
#define CYTOPLASM_CRON_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/***
|
||||
* @Nm Cron
|
||||
* @Nd Basic periodic job scheduler.
|
||||
|
@ -56,8 +58,6 @@
|
|||
* by any means.
|
||||
*/
|
||||
|
||||
#include "Int.h"
|
||||
|
||||
/**
|
||||
* All functions defined here operate on a structure opaque to the
|
||||
* caller.
|
||||
|
@ -82,7 +82,7 @@ typedef void (JobFunc) (void *);
|
|||
* .Pp
|
||||
* This function takes the tick interval in milliseconds.
|
||||
*/
|
||||
extern Cron * CronCreate(UInt32);
|
||||
extern Cron * CronCreate(uint64_t);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
extern void
|
||||
CronEvery(Cron *, unsigned long, JobFunc *, void *);
|
||||
CronEvery(Cron *, uint64_t, JobFunc *, void *);
|
||||
|
||||
/**
|
||||
* Start ticking the clock and executing registered jobs.
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
||||
|
@ -24,6 +24,18 @@
|
|||
#ifndef CYTOPLASM_CYTOPLASM_H
|
||||
#define CYTOPLASM_CYTOPLASM_H
|
||||
|
||||
#define CYTOPLASM_VERSION_MAJOR 0
|
||||
#define CYTOPLASM_VERSION_MINOR 4
|
||||
#define CYTOPLASM_VERSION_PATCH 1
|
||||
#define CYTOPLASM_VERSION ((CYTOPLASM_VERSION_MAJOR * 10000) + (CYTOPLASM_VERSION_MINOR * 100) + (CYTOPLASM_VERSION_PATCH))
|
||||
|
||||
#define CYTOPLASM_VERSION_ALPHA 1
|
||||
#define CYTOPLASM_VERSION_BETA 0
|
||||
#define CYTOPLASM_VERSION_STABLE (!CYTOPLASM_VERSION_ALPHA && !CYTOPLASM_VERSION_BETA)
|
||||
|
||||
#define XSTRINGIFY(x) #x
|
||||
#define STRINGIFY(x) XSTRINGIFY(x)
|
||||
|
||||
/***
|
||||
* @Nm Cytoplasm
|
||||
* @Nd A simple API that provides metadata on the library itself.
|
||||
|
@ -34,18 +46,8 @@
|
|||
* currently loaded library.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Get the name that this library was compiled with. In most cases,
|
||||
* this will be hard-coded to "Cytoplasm", but it may differ if, for
|
||||
* some reason, there exists another ABI-compatible library that
|
||||
* wishes to report its name.
|
||||
*
|
||||
* This function really only exists because the information is
|
||||
* available along side of the version information so for
|
||||
* consistency, it made sense to include both.
|
||||
*/
|
||||
extern char * CytoplasmGetName(void);
|
||||
/** */
|
||||
extern int CytoplasmGetVersion(void);
|
||||
|
||||
/**
|
||||
* Get the library version. This will be useful mostly for printing
|
||||
|
@ -55,6 +57,6 @@ extern char * CytoplasmGetName(void);
|
|||
* This function returns a string, which should usually be able to be
|
||||
* parsed using sscanf() if absolutely necessary.
|
||||
*/
|
||||
extern char * CytoplasmGetVersion(void);
|
||||
extern const char * CytoplasmGetVersionStr(void);
|
||||
|
||||
#endif /* CYTOPLASM_CYTOPLASM_H */
|
||||
#endif /* CYTOPLASM_CYTOPLASM_H */
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
||||
|
@ -37,6 +37,7 @@
|
|||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "HashMap.h"
|
||||
#include "Array.h"
|
||||
|
@ -47,6 +48,17 @@
|
|||
*/
|
||||
typedef struct Db Db;
|
||||
|
||||
/**
|
||||
* Some "hints" for the database backend for operations.
|
||||
* Hints are a way for the program to declare what to except of it
|
||||
* (and the program MUST adhere to these hints, but the backend
|
||||
* MAY adhere).
|
||||
*/
|
||||
typedef enum DbHint {
|
||||
DB_HINT_READONLY, /* The database reference is treated as read-only */
|
||||
DB_HINT_WRITE /* The database reference is treated as read/write */
|
||||
} DbHint;
|
||||
|
||||
/**
|
||||
* When an object is locked, a reference is returned. This reference
|
||||
* is owned by the current thread, and the database is inaccessible to
|
||||
|
@ -67,6 +79,15 @@ typedef struct DbRef DbRef;
|
|||
*/
|
||||
extern Db * DbOpen(char *, size_t);
|
||||
|
||||
/**
|
||||
* Open a LMDB data directory. This function is similar to
|
||||
* .Fn DbOpen ,
|
||||
* but works with a LMDB-based backend, with the second argument
|
||||
* being the maximum filesize. This function fails with NULL if ever
|
||||
* called without LMDB enabled at compiletime.
|
||||
*/
|
||||
extern Db * DbOpenLMDB(char *, size_t);
|
||||
|
||||
/**
|
||||
* Close the database. This function will flush anything in the cache
|
||||
* to the disk, and then close the data directory. It assumes that
|
||||
|
@ -98,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
|
||||
|
@ -108,12 +136,42 @@ 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 ,
|
||||
* but with hints on the reference itself, as
|
||||
* .Fn DbLock
|
||||
* itself is read/write.
|
||||
*/
|
||||
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
|
||||
* behavior will result.
|
||||
*/
|
||||
extern int 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
|
||||
|
@ -121,7 +179,7 @@ extern int DbDelete(Db *, size_t,...);
|
|||
* read cache; writes are always immediate to ensure data integrity in
|
||||
* the event of a system failure.
|
||||
*/
|
||||
extern int DbUnlock(Db *, DbRef *);
|
||||
extern bool DbUnlock(Db *, DbRef *);
|
||||
|
||||
/**
|
||||
* Check the existence of the given database object in a more efficient
|
||||
|
@ -130,7 +188,14 @@ extern int DbUnlock(Db *, DbRef *);
|
|||
* This function does not lock the object, nor does it load it into
|
||||
* memory if it exists.
|
||||
*/
|
||||
extern int 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
|
||||
|
@ -144,6 +209,13 @@ extern int 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 .
|
||||
|
@ -164,6 +236,6 @@ extern HashMap * DbJson(DbRef *);
|
|||
* replace it with new JSON. This is more efficient than duplicating
|
||||
* a separate object into the database reference.
|
||||
*/
|
||||
extern int DbJsonSet(DbRef *, HashMap *);
|
||||
extern bool DbJsonSet(DbRef *, HashMap *);
|
||||
|
||||
#endif
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
||||
|
@ -150,7 +150,7 @@ extern void * HashMapDelete(HashMap *, const char *);
|
|||
* insertions or deletions occur during the iteration. This
|
||||
* functionality has not been tested, and will likely not work.
|
||||
*/
|
||||
extern int HashMapIterate(HashMap *, char **, void **);
|
||||
extern bool HashMapIterate(HashMap *, char **, void **);
|
||||
|
||||
/**
|
||||
* A reentrant version of
|
||||
|
@ -163,7 +163,7 @@ extern int HashMapIterate(HashMap *, char **, void **);
|
|||
* .Pp
|
||||
* The cursor should be initialized to 0 at the start of iteration.
|
||||
*/
|
||||
extern int
|
||||
extern bool
|
||||
HashMapIterateReentrant(HashMap *, char **, void **, size_t *);
|
||||
|
||||
/**
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
||||
|
@ -74,7 +74,7 @@ extern void HttpRouterFree(HttpRouter *);
|
|||
* .Pa /some/path/(.*)/parts
|
||||
* to work as one would expect.
|
||||
*/
|
||||
extern int HttpRouterAdd(HttpRouter *, char *, HttpRouteFunc *);
|
||||
extern bool HttpRouterAdd(HttpRouter *, char *, HttpRouteFunc *);
|
||||
|
||||
/**
|
||||
* Route the specified request path using the specified routing
|
||||
|
@ -86,6 +86,6 @@ extern int HttpRouterAdd(HttpRouter *, char *, HttpRouteFunc *);
|
|||
* how to handle, and the pointer to a void pointer is where the
|
||||
* route function's response will be placed.
|
||||
*/
|
||||
extern int HttpRouterRoute(HttpRouter *, char *, void *, void **);
|
||||
extern bool HttpRouterRoute(HttpRouter *, char *, void *, void **);
|
||||
|
||||
#endif /* CYTOPLASM_HTTPROUTER_H */
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
||||
|
@ -47,6 +47,7 @@
|
|||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "Http.h"
|
||||
#include "HashMap.h"
|
||||
|
@ -133,7 +134,7 @@ extern void HttpServerFree(HttpServer *);
|
|||
* caller can continue working while the HTTP server is running in a
|
||||
* separate thread and managing a pool of threads to handle responses.
|
||||
*/
|
||||
extern int HttpServerStart(HttpServer *);
|
||||
extern bool HttpServerStart(HttpServer *);
|
||||
|
||||
/**
|
||||
* Typically, at some point after calling
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
||||
|
@ -71,10 +71,10 @@
|
|||
#include "HashMap.h"
|
||||
#include "Array.h"
|
||||
#include "Stream.h"
|
||||
#include "Int64.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define JSON_DEFAULT -1
|
||||
#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
|
||||
* an array.
|
||||
*/
|
||||
extern JsonValue * JsonValueInteger(Int64);
|
||||
extern JsonValue * JsonValueInteger(uint64_t);
|
||||
|
||||
/**
|
||||
* Unwrap a JSON value that represents a number. This function will
|
||||
|
@ -160,7 +160,7 @@ extern JsonValue * JsonValueInteger(Int64);
|
|||
* misleading. Check the type of the value before making assumptions
|
||||
* about its value.
|
||||
*/
|
||||
extern Int64 JsonValueAsInteger(JsonValue *);
|
||||
extern uint64_t JsonValueAsInteger(JsonValue *);
|
||||
|
||||
/**
|
||||
* 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
|
||||
* array.
|
||||
*/
|
||||
extern JsonValue * JsonValueBoolean(int);
|
||||
extern JsonValue * JsonValueBoolean(bool);
|
||||
|
||||
/**
|
||||
* Unwrap a JSON value that represents a boolean. This function will
|
||||
|
@ -189,7 +189,7 @@ extern JsonValue * JsonValueBoolean(int);
|
|||
* misleading. Check the type of the value before making assumptions
|
||||
* about its type.
|
||||
*/
|
||||
extern int JsonValueAsBoolean(JsonValue *);
|
||||
extern bool JsonValueAsBoolean(JsonValue *);
|
||||
|
||||
/**
|
||||
* 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
|
||||
* been written.
|
||||
*/
|
||||
extern int JsonEncodeString(const char *, Stream *);
|
||||
extern size_t JsonEncodeString(const char *, Stream *);
|
||||
|
||||
/**
|
||||
* Serialize a JSON value as it would appear in JSON output. This is
|
||||
|
@ -277,7 +277,7 @@ extern int JsonEncodeString(const char *, Stream *);
|
|||
* or if the stream is NULL, the number of bytes that would have
|
||||
* been written.
|
||||
*/
|
||||
extern int JsonEncodeValue(JsonValue *, Stream *, int);
|
||||
extern size_t JsonEncodeValue(JsonValue *, Stream *, int);
|
||||
|
||||
/**
|
||||
* Encode a JSON object as it would appear in JSON output, writing it
|
||||
|
@ -289,7 +289,7 @@ extern int JsonEncodeValue(JsonValue *, Stream *, int);
|
|||
* or if the stream is NULL, the number of bytes that would have
|
||||
* been written.
|
||||
*/
|
||||
extern int JsonEncode(HashMap *, Stream *, int);
|
||||
extern size_t JsonEncode(HashMap *, Stream *, int);
|
||||
|
||||
/**
|
||||
* Decode a JSON object from the given input stream and parse it into
|
||||
|
@ -297,6 +297,12 @@ extern int 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
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
||||
|
@ -76,6 +76,7 @@
|
|||
* macros.
|
||||
*/
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* These values are passed into the memory hook function to indicate
|
69
include/Cytoplasm/Platform.h
Normal file
69
include/Cytoplasm/Platform.h
Normal 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 */
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
||||
|
@ -46,6 +46,7 @@
|
|||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/**
|
||||
* These functions operate on a queue structure that is opaque to the
|
||||
|
@ -73,7 +74,7 @@ extern void QueueFree(Queue *);
|
|||
* value indicating whether or not the push succeeded. Pushing items
|
||||
* into the queue will fail if the queue is full.
|
||||
*/
|
||||
extern int QueuePush(Queue *, void *);
|
||||
extern bool QueuePush(Queue *, void *);
|
||||
|
||||
/**
|
||||
* Pop an element out of the queue. This function returns NULL if the
|
||||
|
@ -95,11 +96,11 @@ extern void * QueuePeek(Queue *);
|
|||
/**
|
||||
* Determine whether or not the queue is full.
|
||||
*/
|
||||
extern int QueueFull(Queue *);
|
||||
extern bool QueueFull(Queue *);
|
||||
|
||||
/**
|
||||
* Determine whether or not the queue is empty.
|
||||
*/
|
||||
extern int QueueEmpty(Queue *);
|
||||
extern bool QueueEmpty(Queue *);
|
||||
|
||||
#endif
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
||||
|
@ -58,11 +58,13 @@
|
|||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* Generate a single random integer between 0 and the passed value.
|
||||
* Generate a single random 32-bit integer between 0 and the
|
||||
* passed value.
|
||||
*/
|
||||
extern int RandInt(unsigned int);
|
||||
extern uint32_t RandInt(uint32_t);
|
||||
|
||||
/**
|
||||
* Generate the number of integers specified by the second argument
|
||||
|
@ -76,6 +78,6 @@ extern int RandInt(unsigned int);
|
|||
* has to lock and unlock a mutex. It is therefore better to obtain
|
||||
* multiple random numbers in one pass if multiple are needed.
|
||||
*/
|
||||
extern void RandIntN(int *, size_t, unsigned int);
|
||||
extern void RandIntN(uint32_t *, size_t, uint32_t);
|
||||
|
||||
#endif /* CYTOPLASM_RAND_H */
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
||||
|
@ -41,6 +41,16 @@
|
|||
* due to the lack of a 64-bit integer type, so that hash
|
||||
* function has been omitted.
|
||||
*/
|
||||
#include <stddef.h>
|
||||
|
||||
/**
|
||||
* This is an enum to be used to identify the type of SHA used.
|
||||
*/
|
||||
typedef enum HashType
|
||||
{
|
||||
HASH_SHA1,
|
||||
HASH_SHA256
|
||||
} HashType;
|
||||
|
||||
/**
|
||||
* This function takes a pointer to a NULL-terminated C string, and
|
||||
|
@ -64,6 +74,20 @@ extern unsigned char * Sha256(char *);
|
|||
*/
|
||||
extern unsigned char * Sha1(char *);
|
||||
|
||||
/**
|
||||
* This function behaves just like
|
||||
* .Fn Sha256 ,
|
||||
* except that it allows for a generic byte array, instead of a string.
|
||||
*/
|
||||
extern unsigned char * Sha256Raw(unsigned char *, size_t);
|
||||
|
||||
/**
|
||||
* This function behaves just like
|
||||
* .Fn Sha1 ,
|
||||
* except that it allows for a generic byte array, instead of a string.
|
||||
*/
|
||||
extern unsigned char * Sha1Raw(unsigned char *, size_t);
|
||||
|
||||
/**
|
||||
* Convert a SHA byte buffer into a hex string. These hex strings
|
||||
* are typically what is transmitted, stored, and compared, however
|
||||
|
@ -71,6 +95,6 @@ extern unsigned char * Sha1(char *);
|
|||
* bytes directly, which is why the conversion to a hex string is
|
||||
* a separate step.
|
||||
*/
|
||||
extern char * ShaToHex(unsigned char *);
|
||||
extern char * ShaToHex(unsigned char *, HashType);
|
||||
|
||||
#endif /* CYTOPLASM_SHA_H */
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
||||
|
@ -39,21 +39,21 @@
|
|||
* is a standard library header.
|
||||
*/
|
||||
|
||||
#include "Int.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/**
|
||||
* Convert UTF-16 into a Unicode codepoint.
|
||||
*/
|
||||
extern UInt32 StrUtf16Decode(UInt16, UInt16);
|
||||
extern uint32_t StrUtf16Decode(uint16_t, uint16_t);
|
||||
|
||||
/**
|
||||
* Take a Unicode codepoint and encode it into a string buffer containing
|
||||
* between 1 and 4 bytes. The string buffer is allocated on the heap,
|
||||
* so it should be freed when it is no longer needed.
|
||||
*/
|
||||
extern char * StrUtf8Encode(UInt32);
|
||||
extern char * StrUtf8Encode(uint32_t);
|
||||
|
||||
/**
|
||||
* 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
|
||||
* .Xr isblank 3 .
|
||||
*/
|
||||
extern int StrBlank(const char *str);
|
||||
extern bool StrBlank(const char *str);
|
||||
|
||||
/**
|
||||
* 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
|
||||
* strcmp() returned 0.
|
||||
*/
|
||||
extern int StrEquals(const char *, const char *);
|
||||
extern bool StrEquals(const char *, const char *);
|
||||
|
||||
#endif /* CYTOPLASM_STR_H */
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
||||
|
@ -39,6 +39,8 @@
|
|||
#include "Io.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/**
|
||||
* An opaque structure analogous to C's FILE pointers.
|
||||
|
@ -172,7 +174,7 @@ extern off_t StreamSeek(Stream *, off_t, int);
|
|||
* .Xr feof 3
|
||||
* function.
|
||||
*/
|
||||
extern int StreamEof(Stream *);
|
||||
extern bool StreamEof(Stream *);
|
||||
|
||||
/**
|
||||
* Test the stream for an error condition, returning a boolean value
|
||||
|
@ -181,7 +183,7 @@ extern int StreamEof(Stream *);
|
|||
* .Xr ferror 3
|
||||
* function.
|
||||
*/
|
||||
extern int StreamError(Stream *);
|
||||
extern bool StreamError(Stream *);
|
||||
|
||||
/**
|
||||
* Clear the error condition associated with the given stream, allowing
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
||||
|
@ -43,7 +43,6 @@
|
|||
#include <sys/types.h>
|
||||
|
||||
#include "Stream.h"
|
||||
#include "UInt64.h"
|
||||
|
||||
/**
|
||||
* Get the current timestamp in milliseconds since the Unix epoch. This
|
||||
|
@ -61,7 +60,7 @@
|
|||
* overflow before it even gets to this function, which will cause this
|
||||
* function to produce unexpected results.
|
||||
*/
|
||||
extern UInt64 UtilServerTs(void);
|
||||
extern uint64_t UtilTsMillis(void);
|
||||
|
||||
/**
|
||||
* Use
|
||||
|
@ -70,7 +69,7 @@ extern UInt64 UtilServerTs(void);
|
|||
* was an error getting the last modified time of a file. This is
|
||||
* primarily useful for caching file data.
|
||||
*/
|
||||
extern UInt64 UtilLastModified(char *);
|
||||
extern uint64_t UtilLastModified(char *);
|
||||
|
||||
/**
|
||||
* This function behaves just like the system call
|
||||
|
@ -86,7 +85,7 @@ extern int UtilMkdir(const char *, const mode_t);
|
|||
* .Xr nanosleep 2
|
||||
* to make its usage much, much simpler.
|
||||
*/
|
||||
extern int UtilSleepMillis(UInt64);
|
||||
extern int UtilSleepMillis(uint64_t);
|
||||
|
||||
/**
|
||||
* This function works identically to the POSIX
|
||||
|
@ -112,6 +111,6 @@ extern ssize_t UtilGetLine(char **, size_t *, Stream *);
|
|||
* .Fn pthread_self
|
||||
* to a number.
|
||||
*/
|
||||
extern UInt32 UtilThreadNo(void);
|
||||
extern uint32_t UtilThreadNo(void);
|
||||
|
||||
#endif /* CYTOPLASM_UTIL_H */
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
||||
|
|
25
src/Array.c
25
src/Array.c
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
||||
|
@ -38,12 +38,12 @@ struct Array
|
|||
size_t size; /* Elements actually filled */
|
||||
};
|
||||
|
||||
int
|
||||
bool
|
||||
ArrayAdd(Array * array, void *value)
|
||||
{
|
||||
if (!array)
|
||||
{
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
return ArrayInsert(array, array->size, value);
|
||||
|
@ -122,14 +122,14 @@ ArrayGet(Array * array, size_t index)
|
|||
}
|
||||
|
||||
|
||||
extern int
|
||||
bool
|
||||
ArrayInsert(Array * array, size_t index, void *value)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (!array || !value || index > array->size)
|
||||
{
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (array->size >= array->allocated)
|
||||
|
@ -145,7 +145,7 @@ ArrayInsert(Array * array, size_t index, void *value)
|
|||
if (!array->entries)
|
||||
{
|
||||
array->entries = tmp;
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
array->allocated = newSize;
|
||||
|
@ -160,7 +160,7 @@ ArrayInsert(Array * array, size_t index, void *value)
|
|||
|
||||
array->entries[index] = value;
|
||||
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
extern void *
|
||||
|
@ -200,14 +200,14 @@ ArraySize(Array * array)
|
|||
return array->size;
|
||||
}
|
||||
|
||||
int
|
||||
bool
|
||||
ArrayTrim(Array * array)
|
||||
{
|
||||
void **tmp;
|
||||
|
||||
if (!array)
|
||||
{
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
tmp = array->entries;
|
||||
|
@ -218,10 +218,10 @@ ArrayTrim(Array * array)
|
|||
if (!array->entries)
|
||||
{
|
||||
array->entries = tmp;
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -267,8 +267,9 @@ ArrayQuickSort(Array * array, size_t low, size_t high, int (*compare) (void *, v
|
|||
void
|
||||
ArraySort(Array * array, int (*compare) (void *, void *))
|
||||
{
|
||||
if (!array)
|
||||
if (!ArraySize(array))
|
||||
{
|
||||
// If a NULL ptr was given, or the array has no elements, do nothing.
|
||||
return;
|
||||
}
|
||||
ArrayQuickSort(array, 0, array->size - 1, compare);
|
||||
|
|
10
src/Base64.c
10
src/Base64.c
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
||||
|
@ -212,7 +212,7 @@ Base64Unpad(char *base64, size_t length)
|
|||
base64[length] = '\0';
|
||||
}
|
||||
|
||||
extern int
|
||||
bool
|
||||
Base64Pad(char **base64Ptr, size_t length)
|
||||
{
|
||||
char *tmp;
|
||||
|
@ -221,7 +221,7 @@ Base64Pad(char **base64Ptr, size_t length)
|
|||
|
||||
if (length % 4 == 0)
|
||||
{
|
||||
return length; /* Success: no padding needed */
|
||||
return true; /* Success: no padding needed */
|
||||
}
|
||||
|
||||
newSize = length + (4 - (length % 4));
|
||||
|
@ -229,7 +229,7 @@ Base64Pad(char **base64Ptr, size_t length)
|
|||
tmp = Realloc(*base64Ptr, newSize + 100);;
|
||||
if (!tmp)
|
||||
{
|
||||
return 0; /* Memory error */
|
||||
return false; /* Memory error */
|
||||
}
|
||||
*base64Ptr = tmp;
|
||||
|
||||
|
@ -240,5 +240,5 @@ Base64Pad(char **base64Ptr, size_t length)
|
|||
|
||||
(*base64Ptr)[newSize] = '\0';
|
||||
|
||||
return newSize;
|
||||
return true;
|
||||
}
|
||||
|
|
57
src/Cron.c
57
src/Cron.c
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
||||
|
@ -23,32 +23,33 @@
|
|||
*/
|
||||
#include <Cron.h>
|
||||
|
||||
#include <UInt64.h>
|
||||
#include <Array.h>
|
||||
#include <Memory.h>
|
||||
#include <Util.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
struct Cron
|
||||
{
|
||||
UInt64 tick;
|
||||
uint64_t tick;
|
||||
Array *jobs;
|
||||
pthread_mutex_t lock;
|
||||
volatile unsigned int stop:1;
|
||||
pthread_t thread;
|
||||
volatile bool stop;
|
||||
};
|
||||
|
||||
typedef struct Job
|
||||
{
|
||||
UInt64 interval;
|
||||
UInt64 lastExec;
|
||||
uint64_t interval;
|
||||
uint64_t lastExec;
|
||||
JobFunc *func;
|
||||
void *args;
|
||||
} Job;
|
||||
|
||||
static Job *
|
||||
JobCreate(UInt32 interval, JobFunc * func, void *args)
|
||||
JobCreate(uint64_t interval, JobFunc * func, void *args)
|
||||
{
|
||||
Job *job;
|
||||
|
||||
|
@ -63,8 +64,8 @@ JobCreate(UInt32 interval, JobFunc * func, void *args)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
job->interval = UInt64Create(0, interval);
|
||||
job->lastExec = UInt64Create(0, 0);
|
||||
job->interval = interval;
|
||||
job->lastExec = 0;
|
||||
job->func = func;
|
||||
job->args = args;
|
||||
|
||||
|
@ -79,51 +80,51 @@ CronThread(void *args)
|
|||
while (!cron->stop)
|
||||
{
|
||||
size_t i;
|
||||
UInt64 ts; /* tick start */
|
||||
UInt64 te; /* tick end */
|
||||
uint64_t ts; /* tick start */
|
||||
uint64_t te; /* tick end */
|
||||
|
||||
pthread_mutex_lock(&cron->lock);
|
||||
|
||||
ts = UtilServerTs();
|
||||
ts = UtilTsMillis();
|
||||
|
||||
for (i = 0; i < ArraySize(cron->jobs); i++)
|
||||
{
|
||||
Job *job = ArrayGet(cron->jobs, i);
|
||||
|
||||
if (UInt64Gt(UInt64Sub(ts, job->lastExec), job->interval))
|
||||
if ((ts - job->lastExec) > job->interval)
|
||||
{
|
||||
job->func(job->args);
|
||||
job->lastExec = ts;
|
||||
}
|
||||
|
||||
if (UInt64Eq(job->interval, UInt64Create(0, 0)))
|
||||
if (!job->interval)
|
||||
{
|
||||
ArrayDelete(cron->jobs, i);
|
||||
Free(job);
|
||||
}
|
||||
}
|
||||
te = UtilServerTs();
|
||||
te = UtilTsMillis();
|
||||
|
||||
pthread_mutex_unlock(&cron->lock);
|
||||
|
||||
/* Only sleep if the jobs didn't overrun the tick */
|
||||
if (UInt64Gt(cron->tick, UInt64Sub(te, ts)))
|
||||
// Only sleep if the jobs didn't overrun the tick
|
||||
if (cron->tick > (te - ts))
|
||||
{
|
||||
const UInt64 microTick = UInt64Create(0, 100);
|
||||
const uint64_t microTick = 100;
|
||||
|
||||
UInt64 remainingTick = UInt64Sub(cron->tick, UInt64Sub(te, ts));
|
||||
uint64_t remainingTick = cron->tick - (te - ts);
|
||||
|
||||
/* Only sleep for microTick ms at a time because if the job
|
||||
* scheduler is supposed to stop before the tick is up, we
|
||||
* don't want to be stuck in a long sleep */
|
||||
while (UInt64Geq(remainingTick, microTick) && !cron->stop)
|
||||
while (remainingTick >= microTick && !cron->stop)
|
||||
{
|
||||
UtilSleepMillis(microTick);
|
||||
|
||||
remainingTick = UInt64Sub(remainingTick, microTick);
|
||||
remainingTick -= microTick;
|
||||
}
|
||||
|
||||
if (UInt64Neq(remainingTick, UInt64Create(0, 0)) && !cron->stop)
|
||||
if (remainingTick && !cron->stop)
|
||||
{
|
||||
UtilSleepMillis(remainingTick);
|
||||
}
|
||||
|
@ -134,7 +135,7 @@ CronThread(void *args)
|
|||
}
|
||||
|
||||
Cron *
|
||||
CronCreate(UInt32 tick)
|
||||
CronCreate(uint64_t tick)
|
||||
{
|
||||
Cron *cron = Malloc(sizeof(Cron));
|
||||
|
||||
|
@ -150,8 +151,8 @@ CronCreate(UInt32 tick)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
cron->tick = UInt64Create(0, tick);
|
||||
cron->stop = 1;
|
||||
cron->tick = tick;
|
||||
cron->stop = true;
|
||||
|
||||
pthread_mutex_init(&cron->lock, NULL);
|
||||
|
||||
|
@ -180,7 +181,7 @@ CronOnce(Cron * cron, JobFunc * func, void *args)
|
|||
}
|
||||
|
||||
void
|
||||
CronEvery(Cron * cron, unsigned long interval, JobFunc * func, void *args)
|
||||
CronEvery(Cron * cron, uint64_t interval, JobFunc * func, void *args)
|
||||
{
|
||||
Job *job;
|
||||
|
||||
|
@ -208,7 +209,7 @@ CronStart(Cron * cron)
|
|||
return;
|
||||
}
|
||||
|
||||
cron->stop = 0;
|
||||
cron->stop = false;
|
||||
|
||||
pthread_create(&cron->thread, NULL, CronThread, cron);
|
||||
}
|
||||
|
@ -221,7 +222,7 @@ CronStop(Cron * cron)
|
|||
return;
|
||||
}
|
||||
|
||||
cron->stop = 1;
|
||||
cron->stop = true;
|
||||
|
||||
pthread_join(cron->thread, NULL);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
||||
|
@ -24,12 +24,22 @@
|
|||
|
||||
#include <Cytoplasm.h>
|
||||
|
||||
char *CytoplasmGetName()
|
||||
int
|
||||
CytoplasmGetVersion(void)
|
||||
{
|
||||
return LIB_NAME;
|
||||
return CYTOPLASM_VERSION;
|
||||
}
|
||||
|
||||
char *CytoplasmGetVersion()
|
||||
const char *
|
||||
CytoplasmGetVersionStr(void)
|
||||
{
|
||||
return LIB_VERSION;
|
||||
}
|
||||
return "v" STRINGIFY(CYTOPLASM_VERSION_MAJOR)
|
||||
"." STRINGIFY(CYTOPLASM_VERSION_MINOR)
|
||||
"." STRINGIFY(CYTOPLASM_VERSION_PATCH)
|
||||
#if CYTOPLASM_VERSION_ALPHA
|
||||
"-alpha" STRINGIFY(CYTOPLASM_VERSION_ALPHA)
|
||||
#elif CYTOPLASM_VERSION_BETA
|
||||
"-beta" STRINGIFY(CYTOPLASM_VERSION_BETA)
|
||||
#endif
|
||||
;
|
||||
}
|
||||
|
|
969
src/Db.c
969
src/Db.c
|
@ -1,969 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#include <Db.h>
|
||||
|
||||
#include <UInt64.h>
|
||||
#include <Memory.h>
|
||||
#include <Json.h>
|
||||
#include <Util.h>
|
||||
#include <Str.h>
|
||||
#include <Stream.h>
|
||||
#include <Log.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#include <pthread.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
struct Db
|
||||
{
|
||||
char *dir;
|
||||
pthread_mutex_t lock;
|
||||
|
||||
size_t cacheSize;
|
||||
size_t maxCache;
|
||||
HashMap *cache;
|
||||
|
||||
/*
|
||||
* The cache uses a double linked list (see DbRef
|
||||
* below) to know which objects are most and least
|
||||
* recently used. The following diagram helps me
|
||||
* know what way all the pointers go, because it
|
||||
* can get very confusing sometimes. For example,
|
||||
* there's nothing stopping "next" from pointing to
|
||||
* least recent, and "prev" from pointing to most
|
||||
* recent, so hopefully this clarifies the pointer
|
||||
* terminology used when dealing with the linked
|
||||
* list:
|
||||
*
|
||||
* mostRecent leastRecent
|
||||
* | prev prev | prev
|
||||
* +---+ ---> +---+ ---> +---+ ---> NULL
|
||||
* |ref| |ref| |ref|
|
||||
* NULL <--- +---+ <--- +---+ <--- +---+
|
||||
* next next next
|
||||
*/
|
||||
DbRef *mostRecent;
|
||||
DbRef *leastRecent;
|
||||
};
|
||||
|
||||
struct DbRef
|
||||
{
|
||||
HashMap *json;
|
||||
|
||||
UInt64 ts;
|
||||
size_t size;
|
||||
|
||||
Array *name;
|
||||
|
||||
DbRef *prev;
|
||||
DbRef *next;
|
||||
|
||||
int fd;
|
||||
Stream *stream;
|
||||
};
|
||||
|
||||
static void
|
||||
StringArrayFree(Array * arr)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < ArraySize(arr); i++)
|
||||
{
|
||||
Free(ArrayGet(arr, i));
|
||||
}
|
||||
|
||||
ArrayFree(arr);
|
||||
}
|
||||
|
||||
static ssize_t DbComputeSize(HashMap *);
|
||||
|
||||
static ssize_t
|
||||
DbComputeSizeOfValue(JsonValue * val)
|
||||
{
|
||||
MemoryInfo *a;
|
||||
ssize_t total = 0;
|
||||
|
||||
size_t i;
|
||||
|
||||
union
|
||||
{
|
||||
char *str;
|
||||
Array *arr;
|
||||
} u;
|
||||
|
||||
if (!val)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
a = MemoryInfoGet(val);
|
||||
if (a)
|
||||
{
|
||||
total += MemoryInfoGetSize(a);
|
||||
}
|
||||
|
||||
switch (JsonValueType(val))
|
||||
{
|
||||
case JSON_OBJECT:
|
||||
total += DbComputeSize(JsonValueAsObject(val));
|
||||
break;
|
||||
case JSON_ARRAY:
|
||||
u.arr = JsonValueAsArray(val);
|
||||
a = MemoryInfoGet(u.arr);
|
||||
|
||||
if (a)
|
||||
{
|
||||
total += MemoryInfoGetSize(a);
|
||||
}
|
||||
|
||||
for (i = 0; i < ArraySize(u.arr); i++)
|
||||
{
|
||||
total += DbComputeSizeOfValue(ArrayGet(u.arr, i));
|
||||
}
|
||||
break;
|
||||
case JSON_STRING:
|
||||
u.str = JsonValueAsString(val);
|
||||
a = MemoryInfoGet(u.str);
|
||||
if (a)
|
||||
{
|
||||
total += MemoryInfoGetSize(a);
|
||||
}
|
||||
break;
|
||||
case JSON_NULL:
|
||||
case JSON_INTEGER:
|
||||
case JSON_FLOAT:
|
||||
case JSON_BOOLEAN:
|
||||
default:
|
||||
/* These don't use any extra heap space */
|
||||
break;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
DbComputeSize(HashMap * json)
|
||||
{
|
||||
char *key;
|
||||
JsonValue *val;
|
||||
MemoryInfo *a;
|
||||
size_t total;
|
||||
|
||||
if (!json)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
total = 0;
|
||||
|
||||
a = MemoryInfoGet(json);
|
||||
if (a)
|
||||
{
|
||||
total += MemoryInfoGetSize(a);
|
||||
}
|
||||
|
||||
while (HashMapIterate(json, &key, (void **) &val))
|
||||
{
|
||||
a = MemoryInfoGet(key);
|
||||
if (a)
|
||||
{
|
||||
total += MemoryInfoGetSize(a);
|
||||
}
|
||||
|
||||
total += DbComputeSizeOfValue(val);
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
static char *
|
||||
DbHashKey(Array * args)
|
||||
{
|
||||
size_t i;
|
||||
char *str = NULL;
|
||||
|
||||
for (i = 0; i < ArraySize(args); i++)
|
||||
{
|
||||
char *tmp = StrConcat(2, str, ArrayGet(args, i));
|
||||
|
||||
Free(str);
|
||||
str = tmp;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
static char *
|
||||
DbDirName(Db * db, Array * args, size_t strip)
|
||||
{
|
||||
size_t i;
|
||||
char *str = StrConcat(2, db->dir, "/");
|
||||
|
||||
for (i = 0; i < ArraySize(args) - strip; i++)
|
||||
{
|
||||
char *tmp;
|
||||
|
||||
tmp = StrConcat(3, str, ArrayGet(args, i), "/");
|
||||
|
||||
Free(str);
|
||||
|
||||
str = tmp;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
static char *
|
||||
DbFileName(Db * db, Array * args)
|
||||
{
|
||||
size_t i;
|
||||
char *str = StrConcat(2, db->dir, "/");
|
||||
|
||||
for (i = 0; i < ArraySize(args); i++)
|
||||
{
|
||||
char *tmp;
|
||||
char *arg = StrDuplicate(ArrayGet(args, i));
|
||||
size_t j = 0;
|
||||
|
||||
/* Sanitize name to prevent directory traversal attacks */
|
||||
while (arg[j])
|
||||
{
|
||||
switch (arg[j])
|
||||
{
|
||||
case '/':
|
||||
arg[j] = '_';
|
||||
break;
|
||||
case '.':
|
||||
arg[j] = '-';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
j++;
|
||||
}
|
||||
|
||||
tmp = StrConcat(3, str, arg,
|
||||
(i < ArraySize(args) - 1) ? "/" : ".json");
|
||||
|
||||
Free(arg);
|
||||
Free(str);
|
||||
|
||||
str = tmp;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
static void
|
||||
DbCacheEvict(Db * db)
|
||||
{
|
||||
DbRef *ref = db->leastRecent;
|
||||
DbRef *tmp;
|
||||
|
||||
while (ref && db->cacheSize > db->maxCache)
|
||||
{
|
||||
char *hash;
|
||||
|
||||
JsonFree(ref->json);
|
||||
|
||||
hash = DbHashKey(ref->name);
|
||||
HashMapDelete(db->cache, hash);
|
||||
Free(hash);
|
||||
|
||||
StringArrayFree(ref->name);
|
||||
|
||||
db->cacheSize -= ref->size;
|
||||
|
||||
if (ref->next)
|
||||
{
|
||||
ref->next->prev = ref->prev;
|
||||
}
|
||||
else
|
||||
{
|
||||
db->mostRecent = ref->prev;
|
||||
}
|
||||
|
||||
if (ref->prev)
|
||||
{
|
||||
ref->prev->next = ref->next;
|
||||
}
|
||||
else
|
||||
{
|
||||
db->leastRecent = ref->next;
|
||||
}
|
||||
|
||||
tmp = ref->next;
|
||||
Free(ref);
|
||||
|
||||
ref = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
Db *
|
||||
DbOpen(char *dir, size_t cache)
|
||||
{
|
||||
Db *db;
|
||||
pthread_mutexattr_t attr;
|
||||
|
||||
if (!dir)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
db = Malloc(sizeof(Db));
|
||||
if (!db)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
db->dir = dir;
|
||||
db->maxCache = cache;
|
||||
|
||||
pthread_mutexattr_init(&attr);
|
||||
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
|
||||
pthread_mutex_init(&db->lock, &attr);
|
||||
pthread_mutexattr_destroy(&attr);
|
||||
|
||||
db->mostRecent = NULL;
|
||||
db->leastRecent = NULL;
|
||||
db->cacheSize = 0;
|
||||
|
||||
if (db->maxCache)
|
||||
{
|
||||
db->cache = HashMapCreate();
|
||||
if (!db->cache)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
db->cache = NULL;
|
||||
}
|
||||
|
||||
return db;
|
||||
}
|
||||
|
||||
void
|
||||
DbMaxCacheSet(Db * db, size_t cache)
|
||||
{
|
||||
if (!db)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&db->lock);
|
||||
|
||||
db->maxCache = cache;
|
||||
if (db->maxCache && !db->cache)
|
||||
{
|
||||
db->cache = HashMapCreate();
|
||||
db->cacheSize = 0;
|
||||
}
|
||||
|
||||
DbCacheEvict(db);
|
||||
|
||||
pthread_mutex_unlock(&db->lock);
|
||||
}
|
||||
|
||||
void
|
||||
DbClose(Db * db)
|
||||
{
|
||||
if (!db)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&db->lock);
|
||||
|
||||
DbMaxCacheSet(db, 0);
|
||||
DbCacheEvict(db);
|
||||
HashMapFree(db->cache);
|
||||
|
||||
pthread_mutex_unlock(&db->lock);
|
||||
pthread_mutex_destroy(&db->lock);
|
||||
|
||||
Free(db);
|
||||
}
|
||||
|
||||
static DbRef *
|
||||
DbLockFromArr(Db * db, Array * args)
|
||||
{
|
||||
char *file;
|
||||
char *hash;
|
||||
DbRef *ref;
|
||||
struct flock lock;
|
||||
|
||||
int fd;
|
||||
Stream *stream;
|
||||
|
||||
if (!db || !args)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ref = NULL;
|
||||
hash = NULL;
|
||||
|
||||
pthread_mutex_lock(&db->lock);
|
||||
|
||||
/* Check if the item is in the cache */
|
||||
hash = DbHashKey(args);
|
||||
ref = HashMapGet(db->cache, hash);
|
||||
file = DbFileName(db, args);
|
||||
|
||||
fd = open(file, O_RDWR);
|
||||
if (fd == -1)
|
||||
{
|
||||
if (ref)
|
||||
{
|
||||
HashMapDelete(db->cache, hash);
|
||||
JsonFree(ref->json);
|
||||
StringArrayFree(ref->name);
|
||||
|
||||
db->cacheSize -= ref->size;
|
||||
|
||||
if (ref->next)
|
||||
{
|
||||
ref->next->prev = ref->prev;
|
||||
}
|
||||
else
|
||||
{
|
||||
db->mostRecent = ref->prev;
|
||||
}
|
||||
|
||||
if (ref->prev)
|
||||
{
|
||||
ref->prev->next = ref->next;
|
||||
}
|
||||
else
|
||||
{
|
||||
db->leastRecent = ref->next;
|
||||
}
|
||||
|
||||
if (!db->leastRecent)
|
||||
{
|
||||
db->leastRecent = db->mostRecent;
|
||||
}
|
||||
|
||||
Free(ref);
|
||||
}
|
||||
ref = NULL;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
stream = StreamFd(fd);
|
||||
|
||||
lock.l_start = 0;
|
||||
lock.l_len = 0;
|
||||
lock.l_type = F_WRLCK;
|
||||
lock.l_whence = SEEK_SET;
|
||||
|
||||
/* Lock the file on the disk */
|
||||
if (fcntl(fd, F_SETLK, &lock) < 0)
|
||||
{
|
||||
StreamClose(stream);
|
||||
ref = NULL;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (ref) /* In cache */
|
||||
{
|
||||
UInt64 diskTs = UtilLastModified(file);
|
||||
|
||||
ref->fd = fd;
|
||||
ref->stream = stream;
|
||||
|
||||
if (UInt64Gt(diskTs, ref->ts))
|
||||
{
|
||||
/* File was modified on disk since it was cached */
|
||||
HashMap *json = JsonDecode(ref->stream);
|
||||
|
||||
if (!json)
|
||||
{
|
||||
StreamClose(ref->stream);
|
||||
ref = NULL;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
JsonFree(ref->json);
|
||||
ref->json = json;
|
||||
ref->ts = diskTs;
|
||||
ref->size = DbComputeSize(ref->json);
|
||||
}
|
||||
|
||||
/* Float this ref to mostRecent */
|
||||
if (ref->next)
|
||||
{
|
||||
ref->next->prev = ref->prev;
|
||||
|
||||
if (!ref->prev)
|
||||
{
|
||||
db->leastRecent = ref->next;
|
||||
}
|
||||
else
|
||||
{
|
||||
ref->prev->next = ref->next;
|
||||
}
|
||||
|
||||
ref->prev = db->mostRecent;
|
||||
ref->next = NULL;
|
||||
if (db->mostRecent)
|
||||
{
|
||||
db->mostRecent->next = ref;
|
||||
}
|
||||
db->mostRecent = ref;
|
||||
}
|
||||
|
||||
/* If there is no least recent, this is the only thing in the
|
||||
* cache, so it is also least recent. */
|
||||
if (!db->leastRecent)
|
||||
{
|
||||
db->leastRecent = ref;
|
||||
}
|
||||
|
||||
/* The file on disk may be larger than what we have in memory,
|
||||
* which may require items in cache to be evicted. */
|
||||
DbCacheEvict(db);
|
||||
}
|
||||
else
|
||||
{
|
||||
Array *name;
|
||||
size_t i;
|
||||
|
||||
/* Not in cache; load from disk */
|
||||
|
||||
ref = Malloc(sizeof(DbRef));
|
||||
if (!ref)
|
||||
{
|
||||
StreamClose(stream);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
ref->json = JsonDecode(stream);
|
||||
|
||||
if (!ref->json)
|
||||
{
|
||||
Free(ref);
|
||||
StreamClose(stream);
|
||||
ref = NULL;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
ref->fd = fd;
|
||||
ref->stream = stream;
|
||||
|
||||
name = ArrayCreate();
|
||||
for (i = 0; i < ArraySize(args); i++)
|
||||
{
|
||||
ArrayAdd(name, StrDuplicate(ArrayGet(args, i)));
|
||||
}
|
||||
ref->name = name;
|
||||
|
||||
if (db->cache)
|
||||
{
|
||||
ref->ts = UtilServerTs();
|
||||
ref->size = DbComputeSize(ref->json);
|
||||
HashMapSet(db->cache, hash, ref);
|
||||
db->cacheSize += ref->size;
|
||||
|
||||
ref->next = NULL;
|
||||
ref->prev = db->mostRecent;
|
||||
if (db->mostRecent)
|
||||
{
|
||||
db->mostRecent->next = ref;
|
||||
}
|
||||
db->mostRecent = ref;
|
||||
|
||||
if (!db->leastRecent)
|
||||
{
|
||||
db->leastRecent = ref;
|
||||
}
|
||||
|
||||
/* Adding this item to the cache may case it to grow too
|
||||
* large, requiring some items to be evicted */
|
||||
DbCacheEvict(db);
|
||||
}
|
||||
}
|
||||
|
||||
finish:
|
||||
if (!ref)
|
||||
{
|
||||
pthread_mutex_unlock(&db->lock);
|
||||
}
|
||||
|
||||
Free(file);
|
||||
Free(hash);
|
||||
|
||||
return ref;
|
||||
}
|
||||
|
||||
DbRef *
|
||||
DbCreate(Db * db, size_t nArgs,...)
|
||||
{
|
||||
Stream *fp;
|
||||
char *file;
|
||||
char *dir;
|
||||
va_list ap;
|
||||
Array *args;
|
||||
DbRef *ret;
|
||||
|
||||
if (!db)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
va_start(ap, nArgs);
|
||||
args = ArrayFromVarArgs(nArgs, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (!args)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&db->lock);
|
||||
|
||||
file = DbFileName(db, args);
|
||||
|
||||
if (UInt64Neq(UtilLastModified(file), UInt64Create(0, 0)))
|
||||
{
|
||||
Free(file);
|
||||
ArrayFree(args);
|
||||
pthread_mutex_unlock(&db->lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dir = DbDirName(db, args, 1);
|
||||
if (UtilMkdir(dir, 0750) < 0)
|
||||
{
|
||||
Free(file);
|
||||
ArrayFree(args);
|
||||
Free(dir);
|
||||
pthread_mutex_unlock(&db->lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Free(dir);
|
||||
|
||||
fp = StreamOpen(file, "w");
|
||||
Free(file);
|
||||
if (!fp)
|
||||
{
|
||||
ArrayFree(args);
|
||||
pthread_mutex_unlock(&db->lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
StreamPuts(fp, "{}");
|
||||
StreamClose(fp);
|
||||
|
||||
/* DbLockFromArr() will lock again for us */
|
||||
pthread_mutex_unlock(&db->lock);
|
||||
|
||||
ret = DbLockFromArr(db, args);
|
||||
|
||||
ArrayFree(args);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
DbDelete(Db * db, size_t nArgs,...)
|
||||
{
|
||||
va_list ap;
|
||||
Array *args;
|
||||
char *file;
|
||||
char *hash;
|
||||
int ret = 1;
|
||||
DbRef *ref;
|
||||
|
||||
if (!db)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
va_start(ap, nArgs);
|
||||
args = ArrayFromVarArgs(nArgs, ap);
|
||||
va_end(ap);
|
||||
|
||||
pthread_mutex_lock(&db->lock);
|
||||
|
||||
hash = DbHashKey(args);
|
||||
file = DbFileName(db, args);
|
||||
|
||||
ref = HashMapGet(db->cache, hash);
|
||||
if (ref)
|
||||
{
|
||||
HashMapDelete(db->cache, hash);
|
||||
JsonFree(ref->json);
|
||||
StringArrayFree(ref->name);
|
||||
|
||||
db->cacheSize -= ref->size;
|
||||
|
||||
if (ref->next)
|
||||
{
|
||||
ref->next->prev = ref->prev;
|
||||
}
|
||||
else
|
||||
{
|
||||
db->mostRecent = ref->prev;
|
||||
}
|
||||
|
||||
if (ref->prev)
|
||||
{
|
||||
ref->prev->next = ref->next;
|
||||
}
|
||||
else
|
||||
{
|
||||
db->leastRecent = ref->next;
|
||||
}
|
||||
|
||||
if (!db->leastRecent)
|
||||
{
|
||||
db->leastRecent = db->mostRecent;
|
||||
}
|
||||
|
||||
Free(ref);
|
||||
}
|
||||
|
||||
Free(hash);
|
||||
|
||||
if (UInt64Neq(UtilLastModified(file), UInt64Create(0, 0)))
|
||||
{
|
||||
ret = remove(file) == 0;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&db->lock);
|
||||
|
||||
ArrayFree(args);
|
||||
Free(file);
|
||||
return ret;
|
||||
}
|
||||
|
||||
DbRef *
|
||||
DbLock(Db * db, size_t nArgs,...)
|
||||
{
|
||||
va_list ap;
|
||||
Array *args;
|
||||
DbRef *ret;
|
||||
|
||||
va_start(ap, nArgs);
|
||||
args = ArrayFromVarArgs(nArgs, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (!args)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = DbLockFromArr(db, args);
|
||||
|
||||
ArrayFree(args);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
DbUnlock(Db * db, DbRef * ref)
|
||||
{
|
||||
int destroy;
|
||||
|
||||
if (!db || !ref)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
lseek(ref->fd, 0L, SEEK_SET);
|
||||
if (ftruncate(ref->fd, 0) < 0)
|
||||
{
|
||||
pthread_mutex_unlock(&db->lock);
|
||||
Log(LOG_ERR, "Failed to truncate file on disk.");
|
||||
Log(LOG_ERR, "Error on fd %d: %s", ref->fd, strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
JsonEncode(ref->json, ref->stream, JSON_DEFAULT);
|
||||
StreamClose(ref->stream);
|
||||
|
||||
if (db->cache)
|
||||
{
|
||||
char *key = DbHashKey(ref->name);
|
||||
|
||||
if (HashMapGet(db->cache, key))
|
||||
{
|
||||
db->cacheSize -= ref->size;
|
||||
ref->size = DbComputeSize(ref->json);
|
||||
db->cacheSize += ref->size;
|
||||
|
||||
/* If this ref has grown significantly since we last
|
||||
* computed its size, it may have filled the cache and
|
||||
* require some items to be evicted. */
|
||||
DbCacheEvict(db);
|
||||
|
||||
destroy = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
destroy = 1;
|
||||
}
|
||||
|
||||
Free(key);
|
||||
}
|
||||
else
|
||||
{
|
||||
destroy = 1;
|
||||
}
|
||||
|
||||
if (destroy)
|
||||
{
|
||||
JsonFree(ref->json);
|
||||
StringArrayFree(ref->name);
|
||||
|
||||
Free(ref);
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&db->lock);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
DbExists(Db * db, size_t nArgs,...)
|
||||
{
|
||||
va_list ap;
|
||||
Array *args;
|
||||
char *file;
|
||||
int ret;
|
||||
|
||||
va_start(ap, nArgs);
|
||||
args = ArrayFromVarArgs(nArgs, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (!args)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&db->lock);
|
||||
|
||||
file = DbFileName(db, args);
|
||||
ret = UInt64Neq(UtilLastModified(file), UInt64Create(0, 0));
|
||||
|
||||
pthread_mutex_unlock(&db->lock);
|
||||
|
||||
Free(file);
|
||||
ArrayFree(args);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Array *
|
||||
DbList(Db * db, size_t nArgs,...)
|
||||
{
|
||||
Array *result;
|
||||
Array *path;
|
||||
DIR *files;
|
||||
struct dirent *file;
|
||||
char *dir;
|
||||
va_list ap;
|
||||
|
||||
if (!db || !nArgs)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
result = ArrayCreate();
|
||||
if (!result)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
va_start(ap, nArgs);
|
||||
path = ArrayFromVarArgs(nArgs, ap);
|
||||
dir = DbDirName(db, path, 0);
|
||||
|
||||
pthread_mutex_lock(&db->lock);
|
||||
|
||||
files = opendir(dir);
|
||||
if (!files)
|
||||
{
|
||||
ArrayFree(path);
|
||||
ArrayFree(result);
|
||||
Free(dir);
|
||||
pthread_mutex_unlock(&db->lock);
|
||||
return NULL;
|
||||
}
|
||||
while ((file = readdir(files)))
|
||||
{
|
||||
size_t namlen = strlen(file->d_name);
|
||||
|
||||
if (namlen > 5)
|
||||
{
|
||||
int nameOffset = namlen - 5;
|
||||
|
||||
if (StrEquals(file->d_name + nameOffset, ".json"))
|
||||
{
|
||||
file->d_name[nameOffset] = '\0';
|
||||
ArrayAdd(result, StrDuplicate(file->d_name));
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir(files);
|
||||
|
||||
ArrayFree(path);
|
||||
Free(dir);
|
||||
pthread_mutex_unlock(&db->lock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
DbListFree(Array * arr)
|
||||
{
|
||||
StringArrayFree(arr);
|
||||
}
|
||||
|
||||
HashMap *
|
||||
DbJson(DbRef * ref)
|
||||
{
|
||||
return ref ? ref->json : NULL;
|
||||
}
|
||||
|
||||
int
|
||||
DbJsonSet(DbRef * ref, HashMap * json)
|
||||
{
|
||||
if (!ref || !json)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
JsonFree(ref->json);
|
||||
ref->json = JsonDuplicate(json);
|
||||
return 1;
|
||||
}
|
562
src/Db/Db.c
Normal file
562
src/Db/Db.c
Normal file
|
@ -0,0 +1,562 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
#include <Db.h>
|
||||
|
||||
#include <Memory.h>
|
||||
#include <Json.h>
|
||||
#include <Util.h>
|
||||
#include <Str.h>
|
||||
#include <Stream.h>
|
||||
#include <Log.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#include <pthread.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "Db/Internal.h"
|
||||
|
||||
void
|
||||
StringArrayFree(Array * arr)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < ArraySize(arr); i++)
|
||||
{
|
||||
Free(ArrayGet(arr, i));
|
||||
}
|
||||
|
||||
ArrayFree(arr);
|
||||
}
|
||||
|
||||
static ssize_t DbComputeSize(HashMap *);
|
||||
|
||||
static ssize_t
|
||||
DbComputeSizeOfValue(JsonValue * val)
|
||||
{
|
||||
MemoryInfo *a;
|
||||
ssize_t total = 0;
|
||||
|
||||
size_t i;
|
||||
|
||||
union
|
||||
{
|
||||
char *str;
|
||||
Array *arr;
|
||||
} u;
|
||||
|
||||
if (!val)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
a = MemoryInfoGet(val);
|
||||
if (a)
|
||||
{
|
||||
total += MemoryInfoGetSize(a);
|
||||
}
|
||||
|
||||
switch (JsonValueType(val))
|
||||
{
|
||||
case JSON_OBJECT:
|
||||
total += DbComputeSize(JsonValueAsObject(val));
|
||||
break;
|
||||
case JSON_ARRAY:
|
||||
u.arr = JsonValueAsArray(val);
|
||||
a = MemoryInfoGet(u.arr);
|
||||
|
||||
if (a)
|
||||
{
|
||||
total += MemoryInfoGetSize(a);
|
||||
}
|
||||
|
||||
for (i = 0; i < ArraySize(u.arr); i++)
|
||||
{
|
||||
total += DbComputeSizeOfValue(ArrayGet(u.arr, i));
|
||||
}
|
||||
break;
|
||||
case JSON_STRING:
|
||||
u.str = JsonValueAsString(val);
|
||||
a = MemoryInfoGet(u.str);
|
||||
if (a)
|
||||
{
|
||||
total += MemoryInfoGetSize(a);
|
||||
}
|
||||
break;
|
||||
case JSON_NULL:
|
||||
case JSON_INTEGER:
|
||||
case JSON_FLOAT:
|
||||
case JSON_BOOLEAN:
|
||||
default:
|
||||
/* These don't use any extra heap space */
|
||||
break;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
DbComputeSize(HashMap * json)
|
||||
{
|
||||
char *key;
|
||||
JsonValue *val;
|
||||
MemoryInfo *a;
|
||||
size_t total;
|
||||
|
||||
if (!json)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
total = 0;
|
||||
|
||||
a = MemoryInfoGet(json);
|
||||
if (a)
|
||||
{
|
||||
total += MemoryInfoGetSize(a);
|
||||
}
|
||||
|
||||
while (HashMapIterate(json, &key, (void **) &val))
|
||||
{
|
||||
a = MemoryInfoGet(key);
|
||||
if (a)
|
||||
{
|
||||
total += MemoryInfoGetSize(a);
|
||||
}
|
||||
|
||||
total += DbComputeSizeOfValue(val);
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
static char *
|
||||
DbHashKey(Array * args)
|
||||
{
|
||||
size_t i;
|
||||
char *str = NULL;
|
||||
|
||||
for (i = 0; i < ArraySize(args); i++)
|
||||
{
|
||||
char *tmp = StrConcat(2, str, ArrayGet(args, i));
|
||||
|
||||
Free(str);
|
||||
str = tmp;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
static void
|
||||
DbCacheEvict(Db * db)
|
||||
{
|
||||
DbRef *ref = db->leastRecent;
|
||||
DbRef *tmp;
|
||||
|
||||
while (ref && db->cacheSize > db->maxCache)
|
||||
{
|
||||
char *hash;
|
||||
|
||||
JsonFree(ref->json);
|
||||
|
||||
hash = DbHashKey(ref->name);
|
||||
HashMapDelete(db->cache, hash);
|
||||
Free(hash);
|
||||
|
||||
StringArrayFree(ref->name);
|
||||
|
||||
db->cacheSize -= ref->size;
|
||||
|
||||
if (ref->next)
|
||||
{
|
||||
ref->next->prev = ref->prev;
|
||||
}
|
||||
else
|
||||
{
|
||||
db->mostRecent = ref->prev;
|
||||
}
|
||||
|
||||
if (ref->prev)
|
||||
{
|
||||
ref->prev->next = ref->next;
|
||||
}
|
||||
else
|
||||
{
|
||||
db->leastRecent = ref->next;
|
||||
}
|
||||
|
||||
tmp = ref->next;
|
||||
Free(ref);
|
||||
|
||||
ref = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DbMaxCacheSet(Db * db, size_t cache)
|
||||
{
|
||||
if (!db)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&db->lock);
|
||||
|
||||
db->maxCache = cache;
|
||||
if (db->maxCache && !db->cache)
|
||||
{
|
||||
db->cache = HashMapCreate();
|
||||
db->cacheSize = 0;
|
||||
}
|
||||
|
||||
DbCacheEvict(db);
|
||||
|
||||
pthread_mutex_unlock(&db->lock);
|
||||
}
|
||||
|
||||
void
|
||||
DbClose(Db * db)
|
||||
{
|
||||
if (!db)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&db->lock);
|
||||
if (db->close)
|
||||
{
|
||||
db->close(db);
|
||||
}
|
||||
DbMaxCacheSet(db, 0);
|
||||
DbCacheEvict(db);
|
||||
HashMapFree(db->cache);
|
||||
|
||||
pthread_mutex_unlock(&db->lock);
|
||||
pthread_mutex_destroy(&db->lock);
|
||||
|
||||
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,...)
|
||||
{
|
||||
va_list ap;
|
||||
Array *args;
|
||||
DbRef *ret;
|
||||
|
||||
if (!db || !db->create)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
va_start(ap, nArgs);
|
||||
args = ArrayFromVarArgs(nArgs, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (!args)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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,...)
|
||||
{
|
||||
va_list ap;
|
||||
Array *args;
|
||||
bool ret = true;
|
||||
|
||||
if (!db)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
va_start(ap, nArgs);
|
||||
args = ArrayFromVarArgs(nArgs, ap);
|
||||
va_end(ap);
|
||||
|
||||
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,...)
|
||||
{
|
||||
va_list ap;
|
||||
Array *args;
|
||||
DbRef *ret;
|
||||
|
||||
if (!db)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
va_start(ap, nArgs);
|
||||
args = ArrayFromVarArgs(nArgs, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (!args || !db->lockFunc)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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,...)
|
||||
{
|
||||
va_list ap;
|
||||
Array *args;
|
||||
DbRef *ret;
|
||||
|
||||
va_start(ap, nArgs);
|
||||
args = ArrayFromVarArgs(nArgs, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (!args || !db->lockFunc)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = DbLockIntentArgs(db, hint, args);
|
||||
|
||||
ArrayFree(args);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
DbUnlock(Db * db, DbRef * ref)
|
||||
{
|
||||
if (!db || !ref || !db->unlock)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
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,...)
|
||||
{
|
||||
va_list ap;
|
||||
Array *args;
|
||||
bool ret;
|
||||
if (!db || !nArgs || !db->exists)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
va_start(ap, nArgs);
|
||||
args = ArrayFromVarArgs(nArgs, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (!args)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
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,...)
|
||||
{
|
||||
Array *result;
|
||||
Array *path;
|
||||
va_list ap;
|
||||
|
||||
if (!db || !nArgs || !db->list)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
va_start(ap, nArgs);
|
||||
path = ArrayFromVarArgs(nArgs, ap);
|
||||
va_end(ap);
|
||||
|
||||
result = DbListArgs(db, path);
|
||||
|
||||
ArrayFree(path);
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
DbListFree(Array * arr)
|
||||
{
|
||||
StringArrayFree(arr);
|
||||
}
|
||||
|
||||
HashMap *
|
||||
DbJson(DbRef * ref)
|
||||
{
|
||||
return ref ? ref->json : NULL;
|
||||
}
|
||||
|
||||
bool
|
||||
DbJsonSet(DbRef * ref, HashMap * json)
|
||||
{
|
||||
if (!ref || !json)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
JsonFree(ref->json);
|
||||
ref->json = JsonDuplicate(json);
|
||||
return true;
|
||||
}
|
||||
void
|
||||
DbInit(Db *db)
|
||||
{
|
||||
pthread_mutexattr_t attr;
|
||||
if (!db)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
pthread_mutexattr_init(&attr);
|
||||
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
|
||||
pthread_mutex_init(&db->lock, &attr);
|
||||
pthread_mutexattr_destroy(&attr);
|
||||
|
||||
db->mostRecent = NULL;
|
||||
db->leastRecent = NULL;
|
||||
db->cacheSize = 0;
|
||||
db->maxCache = 0;
|
||||
|
||||
if (db->maxCache)
|
||||
{
|
||||
db->cache = HashMapCreate();
|
||||
}
|
||||
else
|
||||
{
|
||||
db->cache = NULL;
|
||||
}
|
||||
}
|
||||
void
|
||||
DbRefInit(Db *db, DbRef *ref)
|
||||
{
|
||||
if (!db || !ref)
|
||||
{
|
||||
return;
|
||||
}
|
||||
ref->prev = db->mostRecent;
|
||||
ref->next = NULL;
|
||||
ref->json = NULL;
|
||||
ref->name = NULL;
|
||||
|
||||
/* As default values to be overwritten by impls */
|
||||
ref->ts = UtilTsMillis();
|
||||
ref->size = 0;
|
||||
|
||||
/* TODO: Append the ref to the cache list.
|
||||
* I removed it because it broke everything and crashed all the time.
|
||||
* My bad! */
|
||||
}
|
||||
void
|
||||
StringArrayAppend(Array *arr, char *str)
|
||||
{
|
||||
if (!arr || !str)
|
||||
{
|
||||
return;
|
||||
}
|
||||
ArrayAdd(arr, StrDuplicate(str));
|
||||
}
|
375
src/Db/Flat.c
Normal file
375
src/Db/Flat.c
Normal file
|
@ -0,0 +1,375 @@
|
|||
#include <Db.h>
|
||||
|
||||
#include <Memory.h>
|
||||
#include <Json.h>
|
||||
#include <Util.h>
|
||||
#include <Str.h>
|
||||
#include <Stream.h>
|
||||
#include <Log.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#include <pthread.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "Db/Internal.h"
|
||||
|
||||
typedef struct FlatDb {
|
||||
Db base;
|
||||
char *dir;
|
||||
/* Theres not much to do here. */
|
||||
} FlatDb;
|
||||
typedef struct FlatDbRef {
|
||||
DbRef base;
|
||||
|
||||
Stream *stream;
|
||||
int fd;
|
||||
} FlatDbRef;
|
||||
|
||||
static char
|
||||
DbSanitiseChar(char input)
|
||||
{
|
||||
switch (input)
|
||||
{
|
||||
case '/':
|
||||
return '_';
|
||||
case '.':
|
||||
return '-';
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
static char *
|
||||
DbDirName(FlatDb * db, Array * args, size_t strip)
|
||||
{
|
||||
size_t i, j;
|
||||
char *str = StrConcat(2, db->dir, "/");
|
||||
|
||||
for (i = 0; i < ArraySize(args) - strip; i++)
|
||||
{
|
||||
char *tmp;
|
||||
char *sanitise = StrDuplicate(ArrayGet(args, i));
|
||||
size_t len = strlen(sanitise);
|
||||
for (j = 0; j < len; j++)
|
||||
{
|
||||
sanitise[j] = DbSanitiseChar(sanitise[j]);
|
||||
}
|
||||
|
||||
tmp = StrConcat(3, str, sanitise, "/");
|
||||
|
||||
Free(str);
|
||||
Free(sanitise);
|
||||
|
||||
str = tmp;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
static char *
|
||||
DbFileName(FlatDb * db, Array * args)
|
||||
{
|
||||
size_t i;
|
||||
char *str = StrConcat(2, db->dir, "/");
|
||||
|
||||
for (i = 0; i < ArraySize(args); i++)
|
||||
{
|
||||
char *tmp;
|
||||
char *arg = StrDuplicate(ArrayGet(args, i));
|
||||
size_t j = 0;
|
||||
|
||||
/* Sanitize name to prevent directory traversal attacks */
|
||||
while (arg[j])
|
||||
{
|
||||
arg[j] = DbSanitiseChar(arg[j]);
|
||||
j++;
|
||||
}
|
||||
|
||||
tmp = StrConcat(3, str, arg,
|
||||
(i < ArraySize(args) - 1) ? "/" : ".json");
|
||||
|
||||
Free(arg);
|
||||
Free(str);
|
||||
|
||||
str = tmp;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
static DbRef *
|
||||
FlatLock(Db *d, DbHint hint, Array *dir)
|
||||
{
|
||||
FlatDb *db = (FlatDb *) d;
|
||||
FlatDbRef *ref = NULL;
|
||||
size_t i;
|
||||
char *path = NULL;
|
||||
if (!d || !dir)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&d->lock);
|
||||
path = DbFileName(db, dir);
|
||||
/* TODO: Caching */
|
||||
{
|
||||
int fd = open(path, O_RDWR);
|
||||
Stream *stream;
|
||||
struct flock lock;
|
||||
if (fd == -1)
|
||||
{
|
||||
ref = NULL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
stream = StreamFd(fd);
|
||||
if (!stream)
|
||||
{
|
||||
ref = NULL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
lock.l_start = 0;
|
||||
lock.l_len = 0;
|
||||
lock.l_type = F_WRLCK;
|
||||
lock.l_whence = SEEK_SET;
|
||||
|
||||
if (fcntl(fd, F_SETLK, &lock) < 0)
|
||||
{
|
||||
StreamClose(stream);
|
||||
ref = NULL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
ref = Malloc(sizeof(*ref));
|
||||
DbRefInit(d, (DbRef *) ref);
|
||||
/* TODO: Hints */
|
||||
ref->base.hint = hint;
|
||||
ref->base.ts = UtilLastModified(path);
|
||||
ref->base.json = JsonDecode(stream);
|
||||
ref->stream = stream;
|
||||
ref->fd = fd;
|
||||
if (!ref->base.json)
|
||||
{
|
||||
Free(ref);
|
||||
StreamClose(stream);
|
||||
ref = NULL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
|
||||
ref->base.name = ArrayCreate();
|
||||
for (i = 0; i < ArraySize(dir); i++)
|
||||
{
|
||||
StringArrayAppend(ref->base.name, ArrayGet(dir, i));
|
||||
}
|
||||
}
|
||||
end:
|
||||
Free(path);
|
||||
if (!ref)
|
||||
{
|
||||
pthread_mutex_unlock(&d->lock);
|
||||
}
|
||||
return (DbRef *) ref;
|
||||
}
|
||||
|
||||
static bool
|
||||
FlatUnlock(Db *d, DbRef *r)
|
||||
{
|
||||
FlatDbRef *ref = (FlatDbRef *) r;
|
||||
|
||||
if (!d || !r)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
lseek(ref->fd, 0L, SEEK_SET);
|
||||
if (ftruncate(ref->fd, 0) < 0)
|
||||
{
|
||||
pthread_mutex_unlock(&d->lock);
|
||||
Log(LOG_ERR, "Failed to truncate file on disk.");
|
||||
Log(LOG_ERR, "Error on fd %d: %s", ref->fd, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
JsonEncode(ref->base.json, ref->stream, JSON_DEFAULT);
|
||||
StreamClose(ref->stream);
|
||||
|
||||
JsonFree(ref->base.json);
|
||||
StringArrayFree(ref->base.name);
|
||||
Free(ref);
|
||||
|
||||
pthread_mutex_unlock(&d->lock);
|
||||
return true;
|
||||
}
|
||||
static DbRef *
|
||||
FlatCreate(Db *d, Array *dir)
|
||||
{
|
||||
FlatDb *db = (FlatDb *) d;
|
||||
char *path, *dirPath;
|
||||
Stream *fp;
|
||||
DbRef *ret;
|
||||
|
||||
if (!d || !dir)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
pthread_mutex_lock(&d->lock);
|
||||
|
||||
path = DbFileName(db, dir);
|
||||
if (UtilLastModified(path))
|
||||
{
|
||||
Free(path);
|
||||
pthread_mutex_unlock(&d->lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dirPath = DbDirName(db, dir, 1);
|
||||
if (UtilMkdir(dirPath, 0750) < 0)
|
||||
{
|
||||
Free(path);
|
||||
Free(dirPath);
|
||||
pthread_mutex_unlock(&d->lock);
|
||||
return NULL;
|
||||
}
|
||||
Free(dirPath);
|
||||
|
||||
fp = StreamOpen(path, "w");
|
||||
Free(path);
|
||||
if (!fp)
|
||||
{
|
||||
pthread_mutex_unlock(&d->lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
StreamPuts(fp, "{}");
|
||||
StreamClose(fp);
|
||||
|
||||
/* FlatLock() will lock again for us */
|
||||
pthread_mutex_unlock(&d->lock);
|
||||
ret = FlatLock(d, DB_HINT_WRITE, dir);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool
|
||||
FlatDelete(Db *d, Array *dir)
|
||||
{
|
||||
bool ret = false;
|
||||
char *file;
|
||||
FlatDb *db = (FlatDb *) d;
|
||||
if (!d || !dir)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&d->lock);
|
||||
file = DbFileName(db, dir);
|
||||
|
||||
/* TODO: Unlink the entry from the linkedlist */
|
||||
if (UtilLastModified(file))
|
||||
{
|
||||
ret = remove(file) == 0;
|
||||
}
|
||||
|
||||
Free(file);
|
||||
pthread_mutex_unlock(&d->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static Array *
|
||||
FlatList(Db *d, Array *dir)
|
||||
{
|
||||
FlatDb *db = (FlatDb *) d;
|
||||
struct dirent *file;
|
||||
Array *ret;
|
||||
DIR *files;
|
||||
char *path;
|
||||
|
||||
if (!d || !dir)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&d->lock);
|
||||
|
||||
path = DbDirName(db, dir, 0);
|
||||
files = opendir(path);
|
||||
if (!files)
|
||||
{
|
||||
Free(path);
|
||||
pthread_mutex_unlock(&d->lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = ArrayCreate();
|
||||
while ((file = readdir(files)))
|
||||
{
|
||||
size_t namlen = strlen(file->d_name);
|
||||
|
||||
if (namlen > 5)
|
||||
{
|
||||
int nameOffset = namlen - 5;
|
||||
|
||||
if (StrEquals(file->d_name + nameOffset, ".json"))
|
||||
{
|
||||
file->d_name[nameOffset] = '\0';
|
||||
StringArrayAppend(ret, file->d_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir(files);
|
||||
Free(path);
|
||||
|
||||
pthread_mutex_unlock(&d->lock);
|
||||
return ret;
|
||||
}
|
||||
static bool
|
||||
FlatExists(Db *d, Array *dir)
|
||||
{
|
||||
FlatDb *db = (FlatDb *) d;
|
||||
char *path;
|
||||
bool ret;
|
||||
if (!d || !dir)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&d->lock);
|
||||
|
||||
path = DbFileName(db, dir);
|
||||
ret = UtilLastModified(path) != 0;
|
||||
Free(path);
|
||||
|
||||
pthread_mutex_unlock(&d->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Db *
|
||||
DbOpen(char *dir, size_t cache)
|
||||
{
|
||||
FlatDb *db;
|
||||
if (!dir)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
db = Malloc(sizeof(*db));
|
||||
DbInit((Db *) db);
|
||||
db->dir = dir;
|
||||
db->base.cacheSize = cache;
|
||||
|
||||
db->base.lockFunc = FlatLock;
|
||||
db->base.unlock = FlatUnlock;
|
||||
db->base.create = FlatCreate;
|
||||
db->base.delete = FlatDelete;
|
||||
db->base.exists = FlatExists;
|
||||
db->base.list = FlatList;
|
||||
db->base.close = NULL;
|
||||
|
||||
return (Db *) db;
|
||||
}
|
101
src/Db/Internal.h
Normal file
101
src/Db/Internal.h
Normal file
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* 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_DB_INTERNAL_H
|
||||
#define CYTOPLASM_DB_INTERNAL_H
|
||||
|
||||
#include <HashMap.h>
|
||||
#include <Db.h>
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
/* TODO: Define the base structures to define a database internally.
|
||||
* All "implementations" shall have them as a first element, so that
|
||||
* basic, general functions can work on them properly. */
|
||||
|
||||
/* The base structure of a database */
|
||||
struct Db
|
||||
{
|
||||
pthread_mutex_t lock;
|
||||
|
||||
size_t cacheSize;
|
||||
size_t maxCache;
|
||||
HashMap *cache;
|
||||
|
||||
/*
|
||||
* The cache uses a double linked list (see DbRef
|
||||
* below) to know which objects are most and least
|
||||
* recently used. The following diagram helps me
|
||||
* know what way all the pointers go, because it
|
||||
* can get very confusing sometimes. For example,
|
||||
* there's nothing stopping "next" from pointing to
|
||||
* least recent, and "prev" from pointing to most
|
||||
* recent, so hopefully this clarifies the pointer
|
||||
* terminology used when dealing with the linked
|
||||
* list:
|
||||
*
|
||||
* mostRecent leastRecent
|
||||
* | prev prev | prev
|
||||
* +---+ ---> +---+ ---> +---+ ---> NULL
|
||||
* |ref| |ref| |ref|
|
||||
* NULL <--- +---+ <--- +---+ <--- +---+
|
||||
* next next next
|
||||
*/
|
||||
DbRef *mostRecent;
|
||||
DbRef *leastRecent;
|
||||
|
||||
/* Functions for implementation-specific operations
|
||||
* (opening a ref, closing a db, removing an entry, ...) */
|
||||
DbRef * (*lockFunc)(Db *, DbHint, Array *);
|
||||
DbRef * (*create)(Db *, Array *);
|
||||
Array * (*list)(Db *, Array *);
|
||||
bool (*unlock)(Db *, DbRef *);
|
||||
bool (*delete)(Db *, Array *);
|
||||
bool (*exists)(Db *, Array *);
|
||||
void (*close)(Db *);
|
||||
|
||||
/* Implementation-specific constructs */
|
||||
};
|
||||
|
||||
struct DbRef
|
||||
{
|
||||
HashMap *json;
|
||||
|
||||
uint64_t ts;
|
||||
size_t size;
|
||||
|
||||
Array *name;
|
||||
|
||||
DbRef *prev;
|
||||
DbRef *next;
|
||||
|
||||
DbHint hint;
|
||||
/* Implementation-specific constructs */
|
||||
};
|
||||
|
||||
extern void DbInit(Db *);
|
||||
extern void DbRefInit(Db *, DbRef *);
|
||||
extern void StringArrayFree(Array *);
|
||||
extern void StringArrayAppend(Array *, char *);
|
||||
|
||||
#endif
|
593
src/Db/LMDB.c
Normal file
593
src/Db/LMDB.c
Normal file
|
@ -0,0 +1,593 @@
|
|||
#include <Memory.h>
|
||||
#include <Json.h>
|
||||
#include <Log.h>
|
||||
#include <Db.h>
|
||||
|
||||
#include "Db/Internal.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef EDB_LMDB
|
||||
|
||||
#include <lmdb.h>
|
||||
|
||||
typedef struct LMDB {
|
||||
Db base; /* The base implementation required to pass */
|
||||
|
||||
MDB_env *environ;
|
||||
MDB_dbi dbi;
|
||||
} LMDB;
|
||||
typedef struct LMDBRef {
|
||||
DbRef base;
|
||||
|
||||
/* TODO: LMDB-dependent stuff */
|
||||
MDB_txn *transaction;
|
||||
MDB_dbi dbi;
|
||||
} LMDBRef;
|
||||
|
||||
/* Helper functions */
|
||||
static MDB_val
|
||||
LMDBTranslateKey(Array *key)
|
||||
{
|
||||
MDB_val ret = { 0 };
|
||||
char *data = NULL;
|
||||
size_t length = 0;
|
||||
size_t i;
|
||||
if (!key || ArraySize(key) > 255)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
data = Realloc(data, ++length);
|
||||
data[0] = ArraySize(key);
|
||||
|
||||
/* Now, let's push every item */
|
||||
for (i = 0; i < ArraySize(key); i++)
|
||||
{
|
||||
char *entry = ArrayGet(key, i);
|
||||
size_t offset = length;
|
||||
|
||||
data = Realloc(data, (length += strlen(entry) + 1));
|
||||
memcpy(data + offset, entry, strlen(entry));
|
||||
data[length - 1] = '\0';
|
||||
}
|
||||
|
||||
/* We now have every key */
|
||||
ret.mv_size = length;
|
||||
ret.mv_data = data;
|
||||
return ret;
|
||||
}
|
||||
static void
|
||||
LMDBKillKey(MDB_val key)
|
||||
{
|
||||
if (!key.mv_data || !key.mv_size)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Free(key.mv_data);
|
||||
}
|
||||
static HashMap *
|
||||
LMDBDecode(MDB_val val)
|
||||
{
|
||||
FILE *fakefile;
|
||||
Stream *fakestream;
|
||||
HashMap *ret;
|
||||
if (!val.mv_data || !val.mv_size)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fakefile = fmemopen(val.mv_data, val.mv_size, "r");
|
||||
fakestream = StreamFile(fakefile);
|
||||
ret = JsonDecode(fakestream);
|
||||
StreamClose(fakestream);
|
||||
|
||||
return ret;
|
||||
}
|
||||
static bool
|
||||
LMDBKeyStartsWith(MDB_val key, MDB_val starts)
|
||||
{
|
||||
size_t i;
|
||||
if (!key.mv_size || !starts.mv_size)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (key.mv_size < starts.mv_size)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (i = 0; i < starts.mv_size; i++)
|
||||
{
|
||||
char keyC = ((char *) key.mv_data)[i];
|
||||
char startC = ((char *) starts.mv_data)[i];
|
||||
|
||||
if (keyC != startC)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
static char *
|
||||
LMDBKeyHead(MDB_val key)
|
||||
{
|
||||
char *end;
|
||||
if (!key.mv_size || !key.mv_data)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
/* -2 because we have a NULL byte in there */
|
||||
end = ((char *) key.mv_data) + key.mv_size - 1;
|
||||
if ((void *) end < key.mv_data)
|
||||
{
|
||||
/* Uh oh. */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* 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--;
|
||||
}
|
||||
return end;
|
||||
}
|
||||
|
||||
static DbRef *
|
||||
LMDBLock(Db *d, DbHint hint, Array *k)
|
||||
{
|
||||
LMDB *db = (LMDB *) d;
|
||||
MDB_txn *transaction;
|
||||
LMDBRef *ret = NULL;
|
||||
MDB_val key, json_val;
|
||||
int code, flags;
|
||||
if (!d || !k)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&d->lock);
|
||||
key = LMDBTranslateKey(k);
|
||||
|
||||
/* Create a transaction, honoring hints. */
|
||||
/* TODO: Do we want to create a "main" transaction that everyone inherits
|
||||
* from? */
|
||||
flags = hint == DB_HINT_READONLY ? MDB_RDONLY : 0;
|
||||
if ((code = mdb_txn_begin(db->environ, NULL, flags, &transaction)) != 0)
|
||||
{
|
||||
/* Very bad! */
|
||||
Log(LOG_ERR,
|
||||
"%s: could not begin transaction: %s",
|
||||
__func__, mdb_strerror(code)
|
||||
);
|
||||
goto end;
|
||||
}
|
||||
|
||||
json_val.mv_size = 0;
|
||||
json_val.mv_data = NULL;
|
||||
code = mdb_get(transaction, db->dbi, &key, &json_val);
|
||||
if (code == MDB_NOTFOUND)
|
||||
{
|
||||
mdb_txn_abort(transaction);
|
||||
goto end;
|
||||
}
|
||||
else if (code != 0)
|
||||
{
|
||||
Log(LOG_ERR,
|
||||
"%s: mdb_get failure: %s",
|
||||
__func__, mdb_strerror(code)
|
||||
);
|
||||
mdb_txn_abort(transaction);
|
||||
goto end;
|
||||
}
|
||||
|
||||
ret = Malloc(sizeof(*ret));
|
||||
DbRefInit(d, (DbRef *) ret);
|
||||
/* TODO: Timestamp */
|
||||
{
|
||||
size_t i;
|
||||
ret->base.name = ArrayCreate();
|
||||
for (i = 0; i < ArraySize(k); i++)
|
||||
{
|
||||
char *ent = ArrayGet(k, i);
|
||||
StringArrayAppend(ret->base.name, ent);
|
||||
}
|
||||
}
|
||||
ret->base.json = LMDBDecode(json_val);
|
||||
ret->base.hint = hint;
|
||||
ret->transaction = NULL;
|
||||
|
||||
if (hint == DB_HINT_WRITE)
|
||||
{
|
||||
ret->transaction = transaction;
|
||||
}
|
||||
else
|
||||
{
|
||||
mdb_txn_abort(transaction);
|
||||
}
|
||||
end:
|
||||
if (!ret || hint == DB_HINT_READONLY)
|
||||
{
|
||||
pthread_mutex_unlock(&d->lock);
|
||||
}
|
||||
LMDBKillKey(key);
|
||||
return (DbRef *) ret;
|
||||
}
|
||||
static bool
|
||||
LMDBExists(Db *d, Array *k)
|
||||
{
|
||||
MDB_val key, empty;
|
||||
LMDB *db = (LMDB *) d;
|
||||
MDB_txn *transaction;
|
||||
int code;
|
||||
bool ret = false;
|
||||
if (!d || !k)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&d->lock);
|
||||
key = LMDBTranslateKey(k);
|
||||
|
||||
/* create a txn */
|
||||
if ((code = mdb_txn_begin(db->environ, NULL, 0, &transaction)) != 0)
|
||||
{
|
||||
/* Very bad! */
|
||||
Log(LOG_ERR,
|
||||
"%s: could not begin transaction: %s",
|
||||
__func__, mdb_strerror(code)
|
||||
);
|
||||
goto end;
|
||||
}
|
||||
|
||||
ret = mdb_get(transaction, db->dbi, &key, &empty) == 0;
|
||||
mdb_txn_abort(transaction);
|
||||
end:
|
||||
LMDBKillKey(key);
|
||||
pthread_mutex_unlock(&d->lock);
|
||||
return ret;
|
||||
}
|
||||
static bool
|
||||
LMDBDelete(Db *d, Array *k)
|
||||
{
|
||||
MDB_val key, empty;
|
||||
LMDB *db = (LMDB *) d;
|
||||
MDB_txn *transaction;
|
||||
int code;
|
||||
bool ret = false;
|
||||
if (!d || !k)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&d->lock);
|
||||
key = LMDBTranslateKey(k);
|
||||
|
||||
/* create a txn */
|
||||
if ((code = mdb_txn_begin(db->environ, NULL, 0, &transaction)) != 0)
|
||||
{
|
||||
/* Very bad! */
|
||||
Log(LOG_ERR,
|
||||
"%s: could not begin transaction: %s",
|
||||
__func__, mdb_strerror(code)
|
||||
);
|
||||
goto end;
|
||||
}
|
||||
|
||||
ret = mdb_del(transaction, db->dbi, &key, &empty) == 0;
|
||||
mdb_txn_commit(transaction);
|
||||
end:
|
||||
LMDBKillKey(key);
|
||||
pthread_mutex_unlock(&d->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool
|
||||
LMDBUnlock(Db *d, DbRef *r)
|
||||
{
|
||||
LMDBRef *ref = (LMDBRef *) r;
|
||||
LMDB *db = (LMDB *) d;
|
||||
FILE *fakestream;
|
||||
Stream *stream;
|
||||
MDB_val key, val;
|
||||
bool ret = true;
|
||||
DbHint hint = r ? r->hint : 0;
|
||||
|
||||
if (!d || !r)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
val.mv_data = NULL;
|
||||
val.mv_size = 0;
|
||||
if (ref->transaction && r->hint == DB_HINT_WRITE)
|
||||
{
|
||||
key = LMDBTranslateKey(r->name);
|
||||
|
||||
fakestream = open_memstream((char **) &val.mv_data, &val.mv_size);
|
||||
stream = StreamFile(fakestream);
|
||||
JsonEncode(r->json, stream, JSON_DEFAULT);
|
||||
StreamFlush(stream);
|
||||
StreamClose(stream);
|
||||
|
||||
ret = mdb_put(ref->transaction, db->dbi, &key, &val, 0) == 0;
|
||||
|
||||
mdb_txn_commit(ref->transaction);
|
||||
LMDBKillKey(key);
|
||||
}
|
||||
StringArrayFree(ref->base.name);
|
||||
JsonFree(ref->base.json);
|
||||
Free(ref);
|
||||
|
||||
if (val.mv_data)
|
||||
{
|
||||
free(val.mv_data);
|
||||
}
|
||||
if (ret && hint == DB_HINT_WRITE)
|
||||
{
|
||||
pthread_mutex_unlock(&d->lock);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
static DbRef *
|
||||
LMDBCreate(Db *d, Array *k)
|
||||
{
|
||||
LMDB *db = (LMDB *) d;
|
||||
MDB_txn *transaction;
|
||||
LMDBRef *ret = NULL;
|
||||
MDB_val key, empty_json;
|
||||
int code;
|
||||
if (!d || !k)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&d->lock);
|
||||
key = LMDBTranslateKey(k);
|
||||
|
||||
/* create a txn */
|
||||
if ((code = mdb_txn_begin(db->environ, NULL, 0, &transaction)) != 0)
|
||||
{
|
||||
/* Very bad! */
|
||||
Log(LOG_ERR,
|
||||
"%s: could not begin transaction: %s",
|
||||
__func__, mdb_strerror(code)
|
||||
);
|
||||
goto end;
|
||||
}
|
||||
|
||||
empty_json.mv_size = 2;
|
||||
empty_json.mv_data = "{}";
|
||||
/* put data in it */
|
||||
code = mdb_put(transaction, db->dbi, &key, &empty_json, MDB_NOOVERWRITE);
|
||||
if (code == MDB_KEYEXIST)
|
||||
{
|
||||
mdb_txn_abort(transaction);
|
||||
goto end;
|
||||
}
|
||||
else if (code == MDB_MAP_FULL)
|
||||
{
|
||||
Log(LOG_ERR, "%s: db is full", __func__);
|
||||
mdb_txn_abort(transaction);
|
||||
goto end;
|
||||
}
|
||||
else if (code != 0)
|
||||
{
|
||||
Log(LOG_ERR, "%s: mdb_put failure: %s", __func__, mdb_strerror(code));
|
||||
mdb_txn_abort(transaction);
|
||||
goto end;
|
||||
}
|
||||
|
||||
ret = Malloc(sizeof(*ret));
|
||||
DbRefInit(d, (DbRef *) ret);
|
||||
/* TODO: Timestamp */
|
||||
{
|
||||
size_t i;
|
||||
ret->base.name = ArrayCreate();
|
||||
for (i = 0; i < ArraySize(k); i++)
|
||||
{
|
||||
char *ent = ArrayGet(k, i);
|
||||
StringArrayAppend(ret->base.name, ent);
|
||||
}
|
||||
}
|
||||
ret->base.hint = DB_HINT_WRITE;
|
||||
ret->base.json = HashMapCreate();
|
||||
ret->transaction = transaction;
|
||||
end:
|
||||
if (!ret)
|
||||
{
|
||||
pthread_mutex_unlock(&d->lock);
|
||||
}
|
||||
LMDBKillKey(key);
|
||||
return (DbRef *) ret;
|
||||
}
|
||||
|
||||
static Array *
|
||||
LMDBList(Db *d, Array *k)
|
||||
{
|
||||
LMDB *db = (LMDB *) d;
|
||||
MDB_val key = { 0 };
|
||||
MDB_val subKey;
|
||||
MDB_val val;
|
||||
Array *ret = NULL;
|
||||
|
||||
MDB_cursor *cursor;
|
||||
MDB_cursor_op op = MDB_SET_RANGE;
|
||||
MDB_txn *txn;
|
||||
int code;
|
||||
|
||||
if (!d || !k)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&d->lock);
|
||||
|
||||
/* Marked as read-only, as we just don't need to write anything
|
||||
* when listing */
|
||||
if ((code = mdb_txn_begin(db->environ, NULL, MDB_RDONLY, &txn)) != 0)
|
||||
{
|
||||
/* Very bad! */
|
||||
Log(LOG_ERR,
|
||||
"%s: could not begin transaction: %s",
|
||||
__func__, mdb_strerror(code)
|
||||
);
|
||||
goto end;
|
||||
}
|
||||
if ((code = mdb_cursor_open(txn, db->dbi, &cursor)) != 0)
|
||||
{
|
||||
Log(LOG_ERR,
|
||||
"%s: could not get cursor: %s",
|
||||
__func__, mdb_strerror(code)
|
||||
);
|
||||
mdb_txn_abort(txn);
|
||||
goto end;
|
||||
}
|
||||
|
||||
key = LMDBTranslateKey(k);
|
||||
|
||||
/* Small hack to get it to list subitems */
|
||||
((char *) key.mv_data)[0]++;
|
||||
|
||||
ret = ArrayCreate();
|
||||
subKey = key;
|
||||
while (mdb_cursor_get(cursor, &subKey, &val, op) == 0)
|
||||
{
|
||||
/* This searches by *increasing* order. The problem is that it may
|
||||
* extend to unwanted points. Since the values are sorted, we can
|
||||
* just exit if the subkey is incorrect. */
|
||||
char *head = LMDBKeyHead(subKey);
|
||||
if (!LMDBKeyStartsWith(subKey, key))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
StringArrayAppend(ret, head);
|
||||
op = MDB_NEXT;
|
||||
}
|
||||
|
||||
mdb_cursor_close(cursor);
|
||||
mdb_txn_abort(txn);
|
||||
|
||||
end:
|
||||
LMDBKillKey(key);
|
||||
pthread_mutex_unlock(&d->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Implementation functions */
|
||||
static void
|
||||
LMDBClose(Db *d)
|
||||
{
|
||||
LMDB *db = (LMDB *) d;
|
||||
if (!d)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
mdb_dbi_close(db->environ, db->dbi);
|
||||
mdb_env_close(db->environ);
|
||||
}
|
||||
|
||||
Db *
|
||||
DbOpenLMDB(char *dir, size_t size)
|
||||
{
|
||||
/* TODO */
|
||||
MDB_env *env = NULL;
|
||||
MDB_txn *txn;
|
||||
MDB_dbi dbi;
|
||||
int code;
|
||||
LMDB *db;
|
||||
if (!dir || !size)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Try initialising LMDB */
|
||||
if ((code = mdb_env_create(&env)) != 0)
|
||||
{
|
||||
Log(LOG_ERR,
|
||||
"%s: could not create LMDB env: %s",
|
||||
__func__, mdb_strerror(code)
|
||||
);
|
||||
return NULL;
|
||||
}
|
||||
if ((code = mdb_env_set_mapsize(env, size) != 0))
|
||||
{
|
||||
Log(LOG_ERR,
|
||||
"%s: could not set mapsize to %lu: %s",
|
||||
__func__, (unsigned long) size,
|
||||
mdb_strerror(code)
|
||||
);
|
||||
mdb_env_close(env);
|
||||
return NULL;
|
||||
}
|
||||
mdb_env_set_maxdbs(env, 4);
|
||||
if ((code = mdb_env_open(env, dir, MDB_NOTLS, 0644)) != 0)
|
||||
{
|
||||
Log(LOG_ERR,
|
||||
"%s: could not open LMDB env: %s",
|
||||
__func__, mdb_strerror(code)
|
||||
);
|
||||
mdb_env_close(env);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Initialise a DBI */
|
||||
{
|
||||
if ((code = mdb_txn_begin(env, NULL, 0, &txn)) != 0)
|
||||
{
|
||||
Log(LOG_ERR,
|
||||
"%s: could not begin transaction: %s",
|
||||
__func__, mdb_strerror(code)
|
||||
);
|
||||
mdb_env_close(env);
|
||||
return NULL;
|
||||
}
|
||||
if ((code = mdb_dbi_open(txn, "db", MDB_CREATE, &dbi)) != 0)
|
||||
{
|
||||
Log(LOG_ERR,
|
||||
"%s: could not get transaction dbi: %s",
|
||||
__func__, mdb_strerror(code)
|
||||
);
|
||||
mdb_txn_abort(txn);
|
||||
mdb_env_close(env);
|
||||
return NULL;
|
||||
}
|
||||
mdb_txn_commit(txn);
|
||||
}
|
||||
|
||||
|
||||
db = Malloc(sizeof(*db));
|
||||
DbInit((Db *) db);
|
||||
db->environ = env;
|
||||
db->dbi = dbi;
|
||||
|
||||
db->base.lockFunc = LMDBLock;
|
||||
db->base.create = LMDBCreate;
|
||||
db->base.unlock = LMDBUnlock;
|
||||
db->base.delete = LMDBDelete;
|
||||
db->base.exists = LMDBExists;
|
||||
db->base.close = LMDBClose;
|
||||
db->base.list = LMDBList;
|
||||
|
||||
return (Db *) db;
|
||||
}
|
||||
|
||||
#else
|
||||
Db *
|
||||
DbOpenLMDB(char *dir, size_t size)
|
||||
{
|
||||
/* Unimplemented function */
|
||||
Log(LOG_ERR,
|
||||
"LMDB support is not enabled. Please compile with --use-lmdb"
|
||||
);
|
||||
(void) size;
|
||||
(void) dir;
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
||||
|
@ -184,6 +184,8 @@ HashMapDelete(HashMap * map, const char *key)
|
|||
if (bucket->hash == hash)
|
||||
{
|
||||
bucket->hash = 0;
|
||||
Free(bucket->key);
|
||||
bucket->key = NULL;
|
||||
return bucket->value;
|
||||
}
|
||||
|
||||
|
@ -247,12 +249,12 @@ HashMapGet(HashMap * map, const char *key)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
int
|
||||
bool
|
||||
HashMapIterateReentrant(HashMap * map, char **key, void **value, size_t * i)
|
||||
{
|
||||
if (!map)
|
||||
{
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (*i >= map->capacity)
|
||||
|
@ -260,7 +262,7 @@ HashMapIterateReentrant(HashMap * map, char **key, void **value, size_t * i)
|
|||
*i = 0;
|
||||
*key = NULL;
|
||||
*value = NULL;
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
while (*i < map->capacity)
|
||||
|
@ -273,20 +275,20 @@ HashMapIterateReentrant(HashMap * map, char **key, void **value, size_t * i)
|
|||
{
|
||||
*key = bucket->key;
|
||||
*value = bucket->value;
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
*i = 0;
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
int
|
||||
bool
|
||||
HashMapIterate(HashMap * map, char **key, void **value)
|
||||
{
|
||||
if (!map)
|
||||
{
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
||||
|
@ -621,7 +621,7 @@ HttpParseHeaders(Stream * fp)
|
|||
|
||||
strncpy(headerValue, headerPtr, len);
|
||||
|
||||
HashMapSet(headers, headerKey, headerValue);
|
||||
Free(HashMapSet(headers, headerKey, headerValue));
|
||||
Free(headerKey);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
||||
|
@ -34,6 +34,7 @@
|
|||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
|
||||
#include <Cytoplasm.h>
|
||||
#include <Http.h>
|
||||
#include <Memory.h>
|
||||
#include <Util.h>
|
||||
|
@ -154,7 +155,7 @@ HttpRequest(HttpRequestMethod method, int flags, unsigned short port, char *host
|
|||
HttpRequestMethodToString(method), path);
|
||||
|
||||
HttpRequestHeader(context, "Connection", "close");
|
||||
HttpRequestHeader(context, "User-Agent", LIB_NAME "/" LIB_VERSION);
|
||||
HttpRequestHeader(context, "User-Agent", LIB_NAME "/" STRINGIFY(CYTOPLASM_VERSION));
|
||||
HttpRequestHeader(context, "Host", host);
|
||||
|
||||
return context;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
||||
|
@ -143,7 +143,7 @@ HttpRouterFree(HttpRouter * router)
|
|||
Free(router);
|
||||
}
|
||||
|
||||
int
|
||||
bool
|
||||
HttpRouterAdd(HttpRouter * router, char *regPath, HttpRouteFunc * exec)
|
||||
{
|
||||
RouteNode *node;
|
||||
|
@ -152,19 +152,19 @@ HttpRouterAdd(HttpRouter * router, char *regPath, HttpRouteFunc * exec)
|
|||
|
||||
if (!router || !regPath || !exec)
|
||||
{
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (StrEquals(regPath, "/"))
|
||||
{
|
||||
router->root->exec = exec;
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
regPath = StrDuplicate(regPath);
|
||||
if (!regPath)
|
||||
{
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
tmp = regPath;
|
||||
|
@ -187,10 +187,10 @@ HttpRouterAdd(HttpRouter * router, char *regPath, HttpRouteFunc * exec)
|
|||
|
||||
Free(regPath);
|
||||
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
int
|
||||
bool
|
||||
HttpRouterRoute(HttpRouter * router, char *path, void *args, void **ret)
|
||||
{
|
||||
RouteNode *node;
|
||||
|
@ -199,17 +199,17 @@ HttpRouterRoute(HttpRouter * router, char *path, void *args, void **ret)
|
|||
HttpRouteFunc *exec = NULL;
|
||||
Array *matches = NULL;
|
||||
size_t i;
|
||||
int retval;
|
||||
bool retval;
|
||||
|
||||
if (!router || !path)
|
||||
{
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
matches = ArrayCreate();
|
||||
if (!matches)
|
||||
{
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
node = router->root;
|
||||
|
@ -237,7 +237,6 @@ HttpRouterRoute(HttpRouter * router, char *path, void *args, void **ret)
|
|||
{
|
||||
if (regexec(&val->regex, pathPart, REG_MAX_SUB, pmatch, 0) == 0)
|
||||
{
|
||||
Free(pathPart);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -268,7 +267,6 @@ HttpRouterRoute(HttpRouter * router, char *path, void *args, void **ret)
|
|||
substr = StrSubstr(pathPart, cpmatch.rm_so, cpmatch.rm_eo);
|
||||
if (pmatch[i].rm_so == -1)
|
||||
{
|
||||
Free(pathPart);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -282,7 +280,7 @@ HttpRouterRoute(HttpRouter * router, char *path, void *args, void **ret)
|
|||
|
||||
if (!exec)
|
||||
{
|
||||
retval = 0;
|
||||
retval = false;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
|
@ -295,7 +293,7 @@ HttpRouterRoute(HttpRouter * router, char *path, void *args, void **ret)
|
|||
exec(matches, args);
|
||||
}
|
||||
|
||||
retval = 1;
|
||||
retval = true;
|
||||
|
||||
finish:
|
||||
for (i = 0; i < ArraySize(matches); i++)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
||||
|
@ -465,7 +465,7 @@ HttpServerWorkerThread(void *args)
|
|||
ssize_t i = 0;
|
||||
HttpRequestMethod requestMethod;
|
||||
|
||||
UInt64 firstRead;
|
||||
uint64_t firstRead;
|
||||
|
||||
fp = DequeueConnection(server);
|
||||
|
||||
|
@ -473,7 +473,7 @@ HttpServerWorkerThread(void *args)
|
|||
{
|
||||
/* Block for 1 millisecond before continuing so we don't
|
||||
* murder the CPU if the queue is empty. */
|
||||
UtilSleepMillis(UInt64Create(0, 1));
|
||||
UtilSleepMillis(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -483,21 +483,25 @@ HttpServerWorkerThread(void *args)
|
|||
* happens, UtilGetLine() sets errno to EAGAIN. If we get
|
||||
* EAGAIN, then clear the error on the stream and try again
|
||||
* after a few ms. This is typically more than enough time for
|
||||
* the client to send data. */
|
||||
firstRead = UtilServerTs();
|
||||
* the client to send data.
|
||||
*
|
||||
* 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
|
||||
&& errno == EAGAIN)
|
||||
{
|
||||
StreamClearError(fp);
|
||||
|
||||
/* If the server is stopped, or it's been a while, just
|
||||
* give up so we aren't wasting a thread on this client. */
|
||||
if (server->stop || UInt64Gt(UInt64Sub(UtilServerTs(), firstRead), UInt64Create(0, 1000 * 30)))
|
||||
// If the server is stopped, or it's been a while, just
|
||||
// give up so we aren't wasting a thread on this client.
|
||||
if (server->stop || (UtilTsMillis() - firstRead) > (1000 * 30))
|
||||
{
|
||||
goto finish;
|
||||
}
|
||||
|
||||
UtilSleepMillis(UInt64Create(0, 5));
|
||||
UtilSleepMillis(5);
|
||||
}
|
||||
|
||||
if (lineLen == -1)
|
||||
|
@ -711,25 +715,25 @@ HttpServerEventThread(void *args)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
int
|
||||
bool
|
||||
HttpServerStart(HttpServer * server)
|
||||
{
|
||||
if (!server)
|
||||
{
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (server->isRunning)
|
||||
{
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (pthread_create(&server->socketThread, NULL, HttpServerEventThread, server) != 0)
|
||||
{
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
399
src/Int64.c
399
src/Int64.c
|
@ -1,399 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#include <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
|
2
src/Io.c
2
src/Io.c
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
||||
|
|
80
src/Json.c
80
src/Json.c
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
||||
|
@ -26,14 +26,15 @@
|
|||
#include <Memory.h>
|
||||
#include <Str.h>
|
||||
#include <Util.h>
|
||||
#include <Int.h>
|
||||
#include <Int64.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <stdbool.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
struct JsonValue
|
||||
|
@ -44,9 +45,9 @@ struct JsonValue
|
|||
HashMap *object;
|
||||
Array *array;
|
||||
char *string;
|
||||
Int64 integer;
|
||||
uint64_t integer;
|
||||
double floating;
|
||||
int boolean:1;
|
||||
bool boolean;
|
||||
} as;
|
||||
};
|
||||
|
||||
|
@ -201,7 +202,7 @@ JsonValueAsString(JsonValue * value)
|
|||
}
|
||||
|
||||
JsonValue *
|
||||
JsonValueInteger(Int64 integer)
|
||||
JsonValueInteger(uint64_t integer)
|
||||
{
|
||||
JsonValue *value;
|
||||
|
||||
|
@ -217,12 +218,12 @@ JsonValueInteger(Int64 integer)
|
|||
return value;
|
||||
}
|
||||
|
||||
Int64
|
||||
uint64_t
|
||||
JsonValueAsInteger(JsonValue * value)
|
||||
{
|
||||
if (!value || value->type != JSON_INTEGER)
|
||||
{
|
||||
return Int64Create(0, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return value->as.integer;
|
||||
|
@ -258,7 +259,7 @@ JsonValueAsFloat(JsonValue * value)
|
|||
}
|
||||
|
||||
JsonValue *
|
||||
JsonValueBoolean(int boolean)
|
||||
JsonValueBoolean(bool boolean)
|
||||
{
|
||||
JsonValue *value;
|
||||
|
||||
|
@ -274,12 +275,12 @@ JsonValueBoolean(int boolean)
|
|||
return value;
|
||||
}
|
||||
|
||||
int
|
||||
bool
|
||||
JsonValueAsBoolean(JsonValue * value)
|
||||
{
|
||||
if (!value || value->type != JSON_BOOLEAN)
|
||||
{
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
return value->as.boolean;
|
||||
|
@ -335,12 +336,12 @@ JsonValueFree(JsonValue * value)
|
|||
Free(value);
|
||||
}
|
||||
|
||||
int
|
||||
size_t
|
||||
JsonEncodeString(const char *str, Stream * out)
|
||||
{
|
||||
size_t i;
|
||||
char c;
|
||||
int length = 0;
|
||||
size_t length = 0;
|
||||
|
||||
StreamPutc(out, '"');
|
||||
length++;
|
||||
|
@ -403,9 +404,9 @@ JsonDecodeString(Stream * in)
|
|||
int c;
|
||||
char a[5];
|
||||
|
||||
UInt32 codepoint;
|
||||
UInt16 high;
|
||||
UInt16 low;
|
||||
uint32_t codepoint;
|
||||
uint16_t high;
|
||||
uint16_t low;
|
||||
|
||||
char *utf8Ptr;
|
||||
|
||||
|
@ -422,7 +423,7 @@ JsonDecodeString(Stream * in)
|
|||
{
|
||||
if (c <= 0x001F)
|
||||
{
|
||||
/* Bad byte; these must be escaped */
|
||||
/* Bad byte; these must be escaped */
|
||||
Free(str);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -598,15 +599,13 @@ JsonDecodeString(Stream * in)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
int
|
||||
size_t
|
||||
JsonEncodeValue(JsonValue * value, Stream * out, int level)
|
||||
{
|
||||
size_t i;
|
||||
size_t len;
|
||||
Array *arr;
|
||||
int length = 0;
|
||||
|
||||
char ibuf[INT64_STRBUF];
|
||||
size_t length = 0;
|
||||
|
||||
switch (value->type)
|
||||
{
|
||||
|
@ -644,8 +643,7 @@ JsonEncodeValue(JsonValue * value, Stream * out, int level)
|
|||
length += JsonEncodeString(value->as.string, out);
|
||||
break;
|
||||
case JSON_INTEGER:
|
||||
Int64Str(value->as.integer, 10, ibuf, INT64_STRBUF);
|
||||
length += StreamPrintf(out, "%s", ibuf);
|
||||
length += StreamPrintf(out, "%" PRId64, value->as.integer);
|
||||
break;
|
||||
case JSON_FLOAT:
|
||||
length += StreamPrintf(out, "%f", value->as.floating);
|
||||
|
@ -673,14 +671,14 @@ JsonEncodeValue(JsonValue * value, Stream * out, int level)
|
|||
return length;
|
||||
}
|
||||
|
||||
int
|
||||
size_t
|
||||
JsonEncode(HashMap * object, Stream * out, int level)
|
||||
{
|
||||
size_t index;
|
||||
size_t count;
|
||||
char *key;
|
||||
JsonValue *value;
|
||||
int length;
|
||||
size_t length;
|
||||
|
||||
if (!object)
|
||||
{
|
||||
|
@ -863,6 +861,7 @@ JsonConsumeWhitespace(JsonParserState * state)
|
|||
break;
|
||||
}
|
||||
|
||||
// TODO: This logic should be moved into Stream as a sync function.
|
||||
if (StreamError(state->stream))
|
||||
{
|
||||
if (errno == EAGAIN)
|
||||
|
@ -876,7 +875,7 @@ JsonConsumeWhitespace(JsonParserState * state)
|
|||
}
|
||||
else
|
||||
{
|
||||
UtilSleepMillis(UInt64Create(0, delay));
|
||||
UtilSleepMillis(delay);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
@ -1123,7 +1122,7 @@ JsonDecodeValue(JsonParserState * state)
|
|||
JsonValue *value;
|
||||
char *strValue;
|
||||
|
||||
Int64 iValue;
|
||||
int64_t iValue;
|
||||
size_t i;
|
||||
int neg;
|
||||
|
||||
|
@ -1146,7 +1145,7 @@ JsonDecodeValue(JsonParserState * state)
|
|||
Free(strValue);
|
||||
break;
|
||||
case TOKEN_INTEGER:
|
||||
iValue = Int64Create(0, 0);
|
||||
iValue = 0;
|
||||
i = 0;
|
||||
neg = 0;
|
||||
|
||||
|
@ -1162,14 +1161,14 @@ JsonDecodeValue(JsonParserState * state)
|
|||
}
|
||||
|
||||
d = state->token[i] - '0';
|
||||
iValue = Int64Mul(iValue, Int64Create(0, 10));
|
||||
iValue = Int64Add(iValue, Int64Create(0, d));
|
||||
iValue *= 10;
|
||||
iValue += d;
|
||||
i++;
|
||||
}
|
||||
|
||||
if (neg)
|
||||
{
|
||||
iValue = Int64Neg(iValue);
|
||||
iValue *= -1;
|
||||
}
|
||||
value = JsonValueInteger(iValue);
|
||||
break;
|
||||
|
@ -1349,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,...)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
||||
|
|
282
src/Memory.c
282
src/Memory.c
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
||||
|
@ -32,8 +32,6 @@
|
|||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include <Int.h>
|
||||
|
||||
#ifndef MEMORY_TABLE_CHUNK
|
||||
#define MEMORY_TABLE_CHUNK 256
|
||||
#endif
|
||||
|
@ -42,29 +40,54 @@
|
|||
#define MEMORY_HEXDUMP_WIDTH 16
|
||||
#endif
|
||||
|
||||
#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;
|
||||
const char *file;
|
||||
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
|
||||
#define MEM_BOUND 0xDEADBEEF
|
||||
#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 *) p) + x)) + 1)
|
||||
#define MEM_SIZE_ACTUAL(x) (((x) * sizeof(UInt8)) + (2 * sizeof(MEM_BOUND_TYPE)))
|
||||
|
||||
static pthread_mutex_t lock;
|
||||
static void (*hook) (MemoryAction, MemoryInfo *, void *) = MemoryDefaultHook;
|
||||
static void *hookArgs = NULL;
|
||||
|
||||
static MemoryInfo **allocations = NULL;
|
||||
static size_t allocationsSize = 0;
|
||||
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)
|
||||
{
|
||||
|
@ -79,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);
|
||||
|
||||
|
@ -93,70 +118,27 @@ MemoryRuntimeDestroy(void)
|
|||
return pthread_mutex_destroy(&lock) == 0;
|
||||
}
|
||||
|
||||
static size_t
|
||||
MemoryHash(void *p)
|
||||
{
|
||||
return (((size_t) p) >> 2 * 7) % allocationsSize;
|
||||
}
|
||||
|
||||
static int
|
||||
MemoryInsert(MemoryInfo * a)
|
||||
{
|
||||
size_t hash;
|
||||
|
||||
if (!allocations)
|
||||
if (allocationTail)
|
||||
{
|
||||
allocationsSize = MEMORY_TABLE_CHUNK;
|
||||
allocations = calloc(allocationsSize, sizeof(void *));
|
||||
if (!allocations)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
allocationTail->next = a;
|
||||
}
|
||||
a->next = NULL;
|
||||
a->prev = allocationTail;
|
||||
a->magic = MEM_MAGIC;
|
||||
|
||||
if (!heapStart || heapStart > (void *) a)
|
||||
{
|
||||
heapStart = a;
|
||||
}
|
||||
if (!heapEnd || heapEnd < (void *) a)
|
||||
{
|
||||
heapEnd = a;
|
||||
}
|
||||
|
||||
/* If the next insertion would cause the table to be at least 3/4
|
||||
* full, re-allocate and re-hash. */
|
||||
if ((allocationsLen + 1) >= ((allocationsSize * 3) >> 2))
|
||||
{
|
||||
size_t i;
|
||||
size_t tmpAllocationsSize = allocationsSize;
|
||||
MemoryInfo **tmpAllocations;
|
||||
|
||||
allocationsSize += MEMORY_TABLE_CHUNK;
|
||||
tmpAllocations = calloc(allocationsSize, sizeof(void *));
|
||||
|
||||
if (!tmpAllocations)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < tmpAllocationsSize; i++)
|
||||
{
|
||||
if (allocations[i])
|
||||
{
|
||||
hash = MemoryHash(allocations[i]->pointer);
|
||||
|
||||
while (tmpAllocations[hash])
|
||||
{
|
||||
hash = (hash + 1) % allocationsSize;
|
||||
}
|
||||
|
||||
tmpAllocations[hash] = allocations[i];
|
||||
}
|
||||
}
|
||||
|
||||
free(allocations);
|
||||
allocations = tmpAllocations;
|
||||
}
|
||||
|
||||
hash = MemoryHash(a->pointer);
|
||||
|
||||
while (allocations[hash])
|
||||
{
|
||||
hash = (hash + 1) % allocationsSize;
|
||||
}
|
||||
|
||||
allocations[hash] = a;
|
||||
allocationTail = a;
|
||||
allocationsLen++;
|
||||
|
||||
return 1;
|
||||
|
@ -165,30 +147,32 @@ MemoryInsert(MemoryInfo * a)
|
|||
static void
|
||||
MemoryDelete(MemoryInfo * a)
|
||||
{
|
||||
size_t hash = MemoryHash(a->pointer);
|
||||
size_t count = 0;
|
||||
MemoryInfo *aPrev = a->prev;
|
||||
MemoryInfo *aNext = a->next;
|
||||
|
||||
while (count <= allocationsSize)
|
||||
if (aPrev)
|
||||
{
|
||||
if (allocations[hash] && allocations[hash] == a)
|
||||
{
|
||||
allocations[hash] = NULL;
|
||||
allocationsLen--;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
hash = (hash + 1) % allocationsSize;
|
||||
count++;
|
||||
}
|
||||
aPrev->next = aNext;
|
||||
}
|
||||
if (aNext)
|
||||
{
|
||||
aNext->prev = aPrev;
|
||||
}
|
||||
|
||||
if (a == allocationTail)
|
||||
{
|
||||
allocationTail = aPrev;
|
||||
}
|
||||
|
||||
a->magic = ~MEM_MAGIC;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
|
@ -205,38 +189,32 @@ MemoryAllocate(size_t size, const char *file, int line)
|
|||
void *p;
|
||||
MemoryInfo *a;
|
||||
|
||||
MemoryIterate(NULL, NULL);
|
||||
//MemoryIterate(NULL, NULL);
|
||||
|
||||
pthread_mutex_lock(&lock);
|
||||
|
||||
a = malloc(sizeof(MemoryInfo));
|
||||
a = malloc(sizeof(MemoryInfo) + MEM_SIZE_ACTUAL(size));
|
||||
if (!a)
|
||||
{
|
||||
pthread_mutex_unlock(&lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
p = malloc(MEM_SIZE_ACTUAL(size));
|
||||
if (!p)
|
||||
{
|
||||
free(a);
|
||||
pthread_mutex_unlock(&lock);
|
||||
return NULL;
|
||||
}
|
||||
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->file = file;
|
||||
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))
|
||||
{
|
||||
free(a);
|
||||
free(p);
|
||||
pthread_mutex_unlock(&lock);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -247,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 *
|
||||
|
@ -256,7 +234,7 @@ MemoryReallocate(void *p, size_t size, const char *file, int line)
|
|||
MemoryInfo *a;
|
||||
void *new = NULL;
|
||||
|
||||
MemoryIterate(NULL, NULL);
|
||||
//MemoryIterate(NULL, NULL);
|
||||
|
||||
if (!p)
|
||||
{
|
||||
|
@ -267,25 +245,28 @@ MemoryReallocate(void *p, size_t size, const char *file, int line)
|
|||
if (a)
|
||||
{
|
||||
pthread_mutex_lock(&lock);
|
||||
new = realloc(a->pointer, MEM_SIZE_ACTUAL(size));
|
||||
MemoryDelete(a);
|
||||
new = realloc(a, sizeof(MemoryInfo) + MEM_SIZE_ACTUAL(size));
|
||||
if (new)
|
||||
{
|
||||
MemoryDelete(a);
|
||||
a = new;
|
||||
a->unalignedSize = size;
|
||||
a->size = MEM_SIZE_ACTUAL(size);
|
||||
a->file = file;
|
||||
strncpy(a->file, file, MEMORY_FILE_SIZE - 1);
|
||||
a->line = line;
|
||||
a->magic = MEM_MAGIC;
|
||||
|
||||
a->pointer = new;
|
||||
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)
|
||||
{
|
||||
hook(MEMORY_REALLOCATE, a, hookArgs);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
pthread_mutex_unlock(&lock);
|
||||
}
|
||||
|
@ -295,7 +276,7 @@ MemoryReallocate(void *p, size_t size, const char *file, int line)
|
|||
if (a)
|
||||
{
|
||||
a->size = 0;
|
||||
a->file = file;
|
||||
strncpy(a->file, file, MEMORY_FILE_SIZE - 1);
|
||||
a->line = line;
|
||||
a->pointer = p;
|
||||
hook(MEMORY_BAD_POINTER, a, hookArgs);
|
||||
|
@ -303,7 +284,7 @@ MemoryReallocate(void *p, size_t size, const char *file, int line)
|
|||
}
|
||||
}
|
||||
|
||||
return ((MEM_BOUND_TYPE *) new) + 1;
|
||||
return a->pointer;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -311,7 +292,7 @@ MemoryFree(void *p, const char *file, int line)
|
|||
{
|
||||
MemoryInfo *a;
|
||||
|
||||
MemoryIterate(NULL, NULL);
|
||||
//MemoryIterate(NULL, NULL);
|
||||
|
||||
if (!p)
|
||||
{
|
||||
|
@ -324,12 +305,11 @@ MemoryFree(void *p, const char *file, int line)
|
|||
pthread_mutex_lock(&lock);
|
||||
if (hook)
|
||||
{
|
||||
a->file = file;
|
||||
strncpy(a->file, file, MEMORY_FILE_SIZE - 1);
|
||||
a->line = line;
|
||||
hook(MEMORY_FREE, a, hookArgs);
|
||||
}
|
||||
MemoryDelete(a);
|
||||
free(a->pointer);
|
||||
free(a);
|
||||
|
||||
pthread_mutex_unlock(&lock);
|
||||
|
@ -339,7 +319,7 @@ MemoryFree(void *p, const char *file, int line)
|
|||
a = malloc(sizeof(MemoryInfo));
|
||||
if (a)
|
||||
{
|
||||
a->file = file;
|
||||
strncpy(a->file, file, MEMORY_FILE_SIZE - 1);
|
||||
a->line = line;
|
||||
a->size = 0;
|
||||
a->pointer = p;
|
||||
|
@ -352,17 +332,15 @@ MemoryFree(void *p, const char *file, int line)
|
|||
size_t
|
||||
MemoryAllocated(void)
|
||||
{
|
||||
size_t i;
|
||||
size_t total = 0;
|
||||
MemoryInfo *cur;
|
||||
|
||||
pthread_mutex_lock(&lock);
|
||||
|
||||
for (i = 0; i < allocationsSize; i++)
|
||||
/* TODO */
|
||||
for (cur = allocationTail; cur; cur = cur->prev)
|
||||
{
|
||||
if (allocations[i])
|
||||
{
|
||||
total += MemoryInfoGetSize(allocations[i]);
|
||||
}
|
||||
total += MemoryInfoGetSize(cur);
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&lock);
|
||||
|
@ -373,55 +351,44 @@ MemoryAllocated(void)
|
|||
void
|
||||
MemoryFreeAll(void)
|
||||
{
|
||||
size_t i;
|
||||
MemoryInfo *cur;
|
||||
MemoryInfo *prev;
|
||||
|
||||
pthread_mutex_lock(&lock);
|
||||
|
||||
for (i = 0; i < allocationsSize; i++)
|
||||
/* TODO */
|
||||
for (cur = allocationTail; cur; cur = prev)
|
||||
{
|
||||
if (allocations[i])
|
||||
{
|
||||
free(allocations[i]->pointer);
|
||||
free(allocations[i]);
|
||||
}
|
||||
prev = cur->prev;
|
||||
free(cur);
|
||||
}
|
||||
|
||||
free(allocations);
|
||||
allocations = NULL;
|
||||
allocationsSize = 0;
|
||||
allocationsLen = 0;
|
||||
|
||||
pthread_mutex_unlock(&lock);
|
||||
}
|
||||
|
||||
MemoryInfo *
|
||||
MemoryInfoGet(void *p)
|
||||
MemoryInfoGet(void *po)
|
||||
{
|
||||
size_t hash, count;
|
||||
void *p = po;
|
||||
|
||||
pthread_mutex_lock(&lock);
|
||||
|
||||
p = ((MEM_BOUND_TYPE *) p) - 1;
|
||||
hash = MemoryHash(p);
|
||||
|
||||
count = 0;
|
||||
while (count <= allocationsSize)
|
||||
p = ((MemoryInfo *) p) - 1;
|
||||
if (p < heapStart || p > heapEnd)
|
||||
{
|
||||
if (!allocations[hash] || allocations[hash]->pointer != p)
|
||||
{
|
||||
hash = (hash + 1) % allocationsSize;
|
||||
count++;
|
||||
}
|
||||
else
|
||||
{
|
||||
MemoryCheck(allocations[hash]);
|
||||
pthread_mutex_unlock(&lock);
|
||||
return allocations[hash];
|
||||
}
|
||||
pthread_mutex_unlock(&lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (((MemoryInfo *)p)->magic != MEM_MAGIC)
|
||||
{
|
||||
p = NULL;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&lock);
|
||||
return NULL;
|
||||
return p;
|
||||
}
|
||||
|
||||
size_t
|
||||
|
@ -432,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 *
|
||||
|
@ -465,25 +432,22 @@ MemoryInfoGetPointer(MemoryInfo * a)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
return ((MEM_BOUND_TYPE *) a->pointer) + 1;
|
||||
return a->pointer;
|
||||
}
|
||||
|
||||
void
|
||||
MemoryIterate(void (*iterFunc) (MemoryInfo *, void *), void *args)
|
||||
MemoryIterate(void (*iterFunc) (MemoryInfo *, void *), void *args)
|
||||
{
|
||||
size_t i;
|
||||
MemoryInfo *cur;
|
||||
|
||||
pthread_mutex_lock(&lock);
|
||||
|
||||
for (i = 0; i < allocationsSize; i++)
|
||||
for (cur = allocationTail; cur; cur = cur->prev)
|
||||
{
|
||||
if (allocations[i])
|
||||
MemoryCheck(cur);
|
||||
if (iterFunc)
|
||||
{
|
||||
MemoryCheck(allocations[i]);
|
||||
if (iterFunc)
|
||||
{
|
||||
iterFunc(allocations[i], args);
|
||||
}
|
||||
iterFunc(cur, args);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
20
src/Queue.c
20
src/Queue.c
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
||||
|
@ -75,39 +75,39 @@ QueueFree(Queue * q)
|
|||
Free(q);
|
||||
}
|
||||
|
||||
int
|
||||
bool
|
||||
QueueFull(Queue * q)
|
||||
{
|
||||
if (!q)
|
||||
{
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
return ((q->front == q->rear + 1) || (q->front == 0 && q->rear == q->size - 1));
|
||||
}
|
||||
|
||||
int
|
||||
bool
|
||||
QueueEmpty(Queue * q)
|
||||
{
|
||||
if (!q)
|
||||
{
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
return q->front == q->size + 1;
|
||||
return (q->front == (q->size + 1));
|
||||
}
|
||||
|
||||
int
|
||||
bool
|
||||
QueuePush(Queue * q, void *element)
|
||||
{
|
||||
if (!q || !element)
|
||||
{
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (QueueFull(q))
|
||||
{
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (q->front == q->size + 1)
|
||||
|
@ -126,7 +126,7 @@ QueuePush(Queue * q, void *element)
|
|||
|
||||
q->items[q->rear] = element;
|
||||
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
void *
|
||||
|
|
36
src/Rand.c
36
src/Rand.c
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
||||
|
@ -23,12 +23,12 @@
|
|||
*/
|
||||
#include <Rand.h>
|
||||
|
||||
#include <Int.h>
|
||||
#include <UInt64.h>
|
||||
#include <Util.h>
|
||||
#include <Memory.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
@ -42,12 +42,12 @@
|
|||
|
||||
typedef struct RandState
|
||||
{
|
||||
UInt32 mt[RAND_STATE_VECTOR_LENGTH];
|
||||
uint32_t mt[RAND_STATE_VECTOR_LENGTH];
|
||||
int index;
|
||||
} RandState;
|
||||
|
||||
static void
|
||||
RandSeed(RandState * state, UInt32 seed)
|
||||
RandSeed(RandState * state, uint32_t seed)
|
||||
{
|
||||
state->mt[0] = seed & 0xFFFFFFFF;
|
||||
|
||||
|
@ -57,12 +57,12 @@ RandSeed(RandState * state, UInt32 seed)
|
|||
}
|
||||
}
|
||||
|
||||
static UInt32
|
||||
static uint32_t
|
||||
RandGenerate(RandState * state)
|
||||
{
|
||||
static const UInt32 mag[2] = {0x0, 0x9908B0DF};
|
||||
static const uint32_t mag[2] = {0x0, 0x9908B0DF};
|
||||
|
||||
UInt32 result;
|
||||
uint32_t result;
|
||||
|
||||
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)
|
||||
* time, while being less biased. */
|
||||
void
|
||||
RandIntN(int *buf, size_t size, unsigned int max)
|
||||
RandIntN(uint32_t *buf, size_t size, uint32_t max)
|
||||
{
|
||||
static pthread_key_t stateKey;
|
||||
static int createdKey = 0;
|
||||
static bool createdKey = false;
|
||||
|
||||
/* Limit the range to banish all previously biased results */
|
||||
const int allowed = RAND_MAX - RAND_MAX % max;
|
||||
const uint32_t allowed = RAND_MAX - RAND_MAX % max;
|
||||
|
||||
RandState *state;
|
||||
int tmp;
|
||||
uint32_t tmp;
|
||||
size_t i;
|
||||
|
||||
if (!createdKey)
|
||||
{
|
||||
pthread_key_create(&stateKey, RandDestructor);
|
||||
createdKey = 1;
|
||||
createdKey = true;
|
||||
}
|
||||
|
||||
state = pthread_getspecific(stateKey);
|
||||
|
@ -141,8 +141,8 @@ RandIntN(int *buf, size_t size, unsigned int max)
|
|||
if (!state)
|
||||
{
|
||||
/* Generate a seed from the system time, PID, and TID */
|
||||
UInt64 ts = UtilServerTs();
|
||||
UInt32 seed = UInt64Low(ts) ^ getpid() ^ (unsigned long) pthread_self();
|
||||
uint64_t ts = UtilTsMillis();
|
||||
uint32_t seed = ts ^ getpid() ^ (unsigned long) pthread_self();
|
||||
|
||||
state = Malloc(sizeof(RandState));
|
||||
RandSeed(state, seed);
|
||||
|
@ -164,10 +164,10 @@ RandIntN(int *buf, size_t size, unsigned int max)
|
|||
}
|
||||
|
||||
/* Generate just 1 random number */
|
||||
int
|
||||
RandInt(unsigned int max)
|
||||
uint32_t
|
||||
RandInt(uint32_t max)
|
||||
{
|
||||
int val = 0;
|
||||
uint32_t val = 0;
|
||||
|
||||
RandIntN(&val, 1, max);
|
||||
return val;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
||||
|
@ -38,11 +38,11 @@ HexDump(size_t off, char *hexBuf, char *asciiBuf, void *args)
|
|||
|
||||
if (hexBuf && asciiBuf)
|
||||
{
|
||||
fprintf(report, "%04lx: %s | %s |\n", off, hexBuf, asciiBuf);
|
||||
fprintf(report, "%04zx: %s | %s |\n", off, hexBuf, asciiBuf);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(report, "%04lx\n", off);
|
||||
fprintf(report, "%04zx\n", off);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -52,7 +52,7 @@ MemoryIterator(MemoryInfo * i, void *args)
|
|||
{
|
||||
FILE *report = args;
|
||||
|
||||
fprintf(report, "%s:%d: %lu bytes at %p\n",
|
||||
fprintf(report, "%s:%d: %zu bytes at %p\n",
|
||||
MemoryInfoGetFile(i), MemoryInfoGetLine(i),
|
||||
MemoryInfoGetSize(i), MemoryInfoGetPointer(i));
|
||||
|
||||
|
@ -107,7 +107,7 @@ GenerateMemoryReport(int argc, char **argv)
|
|||
fprintf(report, " '%s'", argv[i]);
|
||||
}
|
||||
fprintf(report, "\nDate: %s\n", tsBuffer);
|
||||
fprintf(report, "Total Bytes: %lu\n", MemoryAllocated());
|
||||
fprintf(report, "Total Bytes: %zu\n", MemoryAllocated());
|
||||
fprintf(report, "\n");
|
||||
|
||||
MemoryIterate(MemoryIterator, report);
|
||||
|
|
35
src/Sha.c
35
src/Sha.c
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
||||
|
@ -28,21 +28,44 @@
|
|||
#include <string.h>
|
||||
|
||||
char *
|
||||
ShaToHex(unsigned char *bytes)
|
||||
ShaToHex(unsigned char *bytes, HashType type)
|
||||
{
|
||||
size_t i = 0;
|
||||
char *str = Malloc(((strlen((char *) bytes) * 2) + 1) * sizeof(char));
|
||||
size_t i = 0, size;
|
||||
char *str;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case HASH_SHA1:
|
||||
size = 20;
|
||||
break;
|
||||
case HASH_SHA256:
|
||||
size = 32;
|
||||
break;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
str = Malloc(((size * 2) + 1) * sizeof(char));
|
||||
if (!str)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while (bytes[i] != '\0')
|
||||
for (i = 0; i < size; i++)
|
||||
{
|
||||
snprintf(str + (2 * i), 3, "%02x", bytes[i]);
|
||||
i++;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
unsigned char *
|
||||
Sha256(char *str)
|
||||
{
|
||||
return Sha256Raw((unsigned char *) str, str ? strlen(str) : 0);
|
||||
}
|
||||
|
||||
unsigned char *
|
||||
Sha1(char *str)
|
||||
{
|
||||
return Sha1Raw((unsigned char *) str, str ? strlen(str) : 0);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
||||
|
@ -23,18 +23,38 @@
|
|||
*/
|
||||
#include <Sha.h>
|
||||
#include <Memory.h>
|
||||
#include <Int.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
/* TODO: Verify LibreSSL support later */
|
||||
#if defined(TLS_IMPL) && (TLS_IMPL == TLS_OPENSSL)
|
||||
|
||||
#include <openssl/sha.h>
|
||||
|
||||
unsigned char *
|
||||
Sha1Raw(unsigned char *str, size_t len)
|
||||
{
|
||||
unsigned char *digest;
|
||||
if (!str)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
digest = Malloc(20 + 1);
|
||||
SHA1(str, len, digest);
|
||||
digest[20] = '\0';
|
||||
return digest;
|
||||
}
|
||||
#else
|
||||
|
||||
#define LOAD32H(x, y) \
|
||||
{ \
|
||||
x = ((UInt32)((y)[0] & 255) << 24) | \
|
||||
((UInt32)((y)[1] & 255) << 16) | \
|
||||
((UInt32)((y)[2] & 255) << 8) | \
|
||||
((UInt32)((y)[3] & 255)); \
|
||||
x = ((uint32_t)((y)[0] & 255) << 24) | \
|
||||
((uint32_t)((y)[1] & 255) << 16) | \
|
||||
((uint32_t)((y)[2] & 255) << 8) | \
|
||||
((uint32_t)((y)[3] & 255)); \
|
||||
}
|
||||
|
||||
#define ROL(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
|
||||
|
@ -49,22 +69,22 @@
|
|||
|
||||
typedef union
|
||||
{
|
||||
UInt8 c[64];
|
||||
UInt32 l[16];
|
||||
uint8_t c[64];
|
||||
uint32_t l[16];
|
||||
} Char64Long16;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
UInt32 state[5];
|
||||
UInt32 count[2];
|
||||
UInt8 buffer[64];
|
||||
uint32_t state[5];
|
||||
uint32_t count[2];
|
||||
uint8_t buffer[64];
|
||||
} Sha1Context;
|
||||
|
||||
static void
|
||||
Sha1Transform(UInt32 state[5], const UInt8 buffer[64])
|
||||
Sha1Transform(uint32_t state[5], const uint8_t *buffer)
|
||||
{
|
||||
UInt32 a, b, c, d, e, i;
|
||||
UInt8 workspace[64];
|
||||
uint32_t a, b, c, d, e, i;
|
||||
uint8_t workspace[64];
|
||||
Char64Long16 *block = (Char64Long16 *) workspace;
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
|
@ -180,9 +200,9 @@ Sha1Init(Sha1Context * ctx)
|
|||
}
|
||||
|
||||
static void
|
||||
Sha1Update(Sha1Context * ctx, const void *buf, UInt32 size)
|
||||
Sha1Update(Sha1Context * ctx, const void *buf, uint32_t size)
|
||||
{
|
||||
UInt32 i, j;
|
||||
uint32_t i, j;
|
||||
|
||||
j = (ctx->count[0] >> 3) & 63;
|
||||
|
||||
|
@ -202,7 +222,7 @@ Sha1Update(Sha1Context * ctx, const void *buf, UInt32 size)
|
|||
|
||||
for (; i + 63 < size; i += 64)
|
||||
{
|
||||
Sha1Transform(ctx->state, (UInt8 *) buf + i);
|
||||
Sha1Transform(ctx->state, (uint8_t *) buf + i);
|
||||
}
|
||||
|
||||
j = 0;
|
||||
|
@ -212,14 +232,14 @@ Sha1Update(Sha1Context * ctx, const void *buf, UInt32 size)
|
|||
i = 0;
|
||||
}
|
||||
|
||||
memcpy(&ctx->buffer[j], &((UInt8 *) buf)[i], size - i);
|
||||
memcpy(&ctx->buffer[j], &((uint8_t *) buf)[i], size - i);
|
||||
}
|
||||
|
||||
static void
|
||||
Sha1Calculate(Sha1Context * ctx, unsigned char *out)
|
||||
{
|
||||
UInt32 i;
|
||||
UInt8 count[8];
|
||||
uint32_t i;
|
||||
uint8_t count[8];
|
||||
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
|
@ -227,21 +247,21 @@ Sha1Calculate(Sha1Context * ctx, unsigned char *out)
|
|||
>> ((3 - (i & 3)) * 8)) & 255);
|
||||
}
|
||||
|
||||
Sha1Update(ctx, (UInt8 *) "\x80", 1);
|
||||
Sha1Update(ctx, (uint8_t *) "\x80", 1);
|
||||
while ((ctx->count[0] & 504) != 448)
|
||||
{
|
||||
Sha1Update(ctx, (UInt8 *) "\0", 1);
|
||||
Sha1Update(ctx, (uint8_t *) "\0", 1);
|
||||
}
|
||||
|
||||
Sha1Update(ctx, count, 8);
|
||||
for (i = 0; i < (160 / 8); i++)
|
||||
{
|
||||
out[i] = (UInt8) ((ctx->state[i >> 2] >> ((3 - (i & 3)) * 8)) & 255);
|
||||
out[i] = (uint8_t) ((ctx->state[i >> 2] >> ((3 - (i & 3)) * 8)) & 255);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned char *
|
||||
Sha1(char *str)
|
||||
Sha1Raw(unsigned char *str, size_t len)
|
||||
{
|
||||
Sha1Context ctx;
|
||||
unsigned char *out;
|
||||
|
@ -258,10 +278,11 @@ Sha1(char *str)
|
|||
}
|
||||
|
||||
Sha1Init(&ctx);
|
||||
Sha1Update(&ctx, str, strlen(str));
|
||||
Sha1Update(&ctx, str, len);
|
||||
Sha1Calculate(&ctx, out);
|
||||
|
||||
out[160 / 8] = '\0';
|
||||
|
||||
return out;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
||||
|
@ -21,20 +21,41 @@
|
|||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#include <Sha.h>
|
||||
#include <Memory.h>
|
||||
#include <Int.h>
|
||||
#include <Sha.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
|
||||
/* TODO: Verify LibreSSL support later */
|
||||
#if defined(TLS_IMPL) && (TLS_IMPL == TLS_OPENSSL)
|
||||
|
||||
#include <openssl/sha.h>
|
||||
|
||||
unsigned char *
|
||||
Sha256Raw(unsigned char *str, size_t len)
|
||||
{
|
||||
unsigned char *digest;
|
||||
if (!str)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
digest = Malloc(32 + 1);
|
||||
SHA256(str, len, digest);
|
||||
digest[32] = '\0';
|
||||
return digest;
|
||||
}
|
||||
#else
|
||||
|
||||
#define GET_UINT32(x) \
|
||||
(((UInt32)(x)[0] << 24) | \
|
||||
((UInt32)(x)[1] << 16) | \
|
||||
((UInt32)(x)[2] << 8) | \
|
||||
((UInt32)(x)[3]))
|
||||
(((uint32_t)(x)[0] << 24) | \
|
||||
((uint32_t)(x)[1] << 16) | \
|
||||
((uint32_t)(x)[2] << 8) | \
|
||||
((uint32_t)(x)[3]))
|
||||
|
||||
#define PUT_UINT32(dst, x) { \
|
||||
(dst)[0] = (x) >> 24; \
|
||||
|
@ -56,8 +77,8 @@
|
|||
#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) { \
|
||||
UInt32 tmp0 = h + T0(e) + CH(e, f, g) + k + w; \
|
||||
UInt32 tmp1 = T1(a) + MAJ(a, b, c); \
|
||||
uint32_t tmp0 = h + T0(e) + CH(e, f, g) + k + w; \
|
||||
uint32_t tmp1 = T1(a) + MAJ(a, b, c); \
|
||||
h = tmp0 + tmp1; \
|
||||
d += tmp0; \
|
||||
}
|
||||
|
@ -65,7 +86,7 @@
|
|||
typedef struct Sha256Context
|
||||
{
|
||||
size_t length;
|
||||
UInt32 state[8];
|
||||
uint32_t state[8];
|
||||
size_t bufLen;
|
||||
unsigned char buffer[64];
|
||||
} Sha256Context;
|
||||
|
@ -73,7 +94,7 @@ typedef struct Sha256Context
|
|||
static void
|
||||
Sha256Chunk(Sha256Context * context, unsigned char chunk[64])
|
||||
{
|
||||
const UInt32 rk[64] = {
|
||||
const uint32_t rk[64] = {
|
||||
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1,
|
||||
0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
|
||||
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786,
|
||||
|
@ -87,8 +108,8 @@ Sha256Chunk(Sha256Context * context, unsigned char chunk[64])
|
|||
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
|
||||
};
|
||||
|
||||
UInt32 w[64];
|
||||
UInt32 a, b, c, d, e, f, g, h;
|
||||
uint32_t w[64];
|
||||
uint32_t a, b, c, d, e, f, g, h;
|
||||
|
||||
int i;
|
||||
|
||||
|
@ -171,17 +192,17 @@ Sha256Process(Sha256Context * context, unsigned char *data, size_t length)
|
|||
}
|
||||
|
||||
unsigned char *
|
||||
Sha256(char *str)
|
||||
Sha256Raw(unsigned char *str, size_t len)
|
||||
{
|
||||
Sha256Context context;
|
||||
size_t i;
|
||||
unsigned char *out;
|
||||
|
||||
unsigned char fill[64];
|
||||
UInt32 fillLen;
|
||||
uint32_t fillLen;
|
||||
unsigned char buf[8];
|
||||
UInt32 hiLen;
|
||||
UInt32 loLen;
|
||||
uint32_t hiLen;
|
||||
uint32_t loLen;
|
||||
|
||||
if (!str)
|
||||
{
|
||||
|
@ -207,14 +228,14 @@ Sha256(char *str)
|
|||
context.length = 0;
|
||||
memset(context.buffer, 0, 64);
|
||||
|
||||
Sha256Process(&context, (unsigned char *) str, strlen(str));
|
||||
Sha256Process(&context, str, len);
|
||||
|
||||
memset(fill, 0, 64);
|
||||
fill[0] = 0x80;
|
||||
|
||||
fillLen = (context.bufLen < 56) ? 56 - context.bufLen : 120 - context.bufLen;
|
||||
hiLen = (UInt32) (context.length >> 29);
|
||||
loLen = (UInt32) (context.length << 3);
|
||||
hiLen = (uint32_t) (context.length >> 29);
|
||||
loLen = (uint32_t) (context.length << 3);
|
||||
|
||||
PUT_UINT32(&buf[0], hiLen);
|
||||
PUT_UINT32(&buf[4], loLen);
|
||||
|
@ -231,3 +252,4 @@ Sha256(char *str)
|
|||
|
||||
return out;
|
||||
}
|
||||
#endif
|
||||
|
|
25
src/Str.c
25
src/Str.c
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
||||
|
@ -26,7 +26,6 @@
|
|||
#include <Memory.h>
|
||||
#include <Util.h>
|
||||
#include <Rand.h>
|
||||
#include <Int.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
@ -35,8 +34,8 @@
|
|||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
|
||||
UInt32
|
||||
StrUtf16Decode(UInt16 high, UInt16 low)
|
||||
uint32_t
|
||||
StrUtf16Decode(uint16_t high, uint16_t low)
|
||||
{
|
||||
if (high <= 0xD7FF)
|
||||
{
|
||||
|
@ -56,7 +55,7 @@ StrUtf16Decode(UInt16 high, UInt16 low)
|
|||
}
|
||||
|
||||
char *
|
||||
StrUtf8Encode(UInt32 codepoint)
|
||||
StrUtf8Encode(uint32_t codepoint)
|
||||
{
|
||||
char *str;
|
||||
|
||||
|
@ -220,10 +219,10 @@ StrConcat(size_t nStr,...)
|
|||
return str;
|
||||
}
|
||||
|
||||
int
|
||||
bool
|
||||
StrBlank(const char *str)
|
||||
{
|
||||
int blank = 1;
|
||||
bool blank = true;
|
||||
size_t i = 0;
|
||||
|
||||
while (str[i])
|
||||
|
@ -245,7 +244,7 @@ StrRandom(size_t len)
|
|||
static const char charset[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
|
||||
char *str;
|
||||
int *nums;
|
||||
uint32_t *nums;
|
||||
size_t i;
|
||||
|
||||
if (!len)
|
||||
|
@ -260,7 +259,7 @@ StrRandom(size_t len)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
nums = Malloc(len * sizeof(int));
|
||||
nums = Malloc(len * sizeof(uint32_t));
|
||||
if (!nums)
|
||||
{
|
||||
Free(str);
|
||||
|
@ -323,21 +322,21 @@ StrLower(char *str)
|
|||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
bool
|
||||
StrEquals(const char *str1, const char *str2)
|
||||
{
|
||||
/* Both strings are NULL, they're equal */
|
||||
if (!str1 && !str2)
|
||||
{
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* One or the other is NULL, they're not equal */
|
||||
if (!str1 || !str2)
|
||||
{
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Neither are NULL, do a regular string comparison */
|
||||
return strcmp(str1, str2) == 0;
|
||||
return (strcmp(str1, str2) == 0);
|
||||
}
|
||||
|
|
13
src/Stream.c
13
src/Stream.c
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
||||
|
@ -26,7 +26,6 @@
|
|||
#include <Io.h>
|
||||
#include <Memory.h>
|
||||
#include <Util.h>
|
||||
#include <Int.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -50,11 +49,11 @@ struct Stream
|
|||
{
|
||||
Io *io;
|
||||
|
||||
UInt8 *rBuf;
|
||||
uint8_t *rBuf;
|
||||
size_t rLen;
|
||||
size_t rOff;
|
||||
|
||||
UInt8 *wBuf;
|
||||
uint8_t *wBuf;
|
||||
size_t wLen;
|
||||
|
||||
char *ugBuf;
|
||||
|
@ -550,13 +549,13 @@ StreamSeek(Stream * stream, off_t offset, int whence)
|
|||
return result;
|
||||
}
|
||||
|
||||
int
|
||||
bool
|
||||
StreamEof(Stream * stream)
|
||||
{
|
||||
return stream && (stream->flags & STREAM_EOF);
|
||||
}
|
||||
|
||||
int
|
||||
bool
|
||||
StreamError(Stream * stream)
|
||||
{
|
||||
return stream && (stream->flags & STREAM_ERR);
|
||||
|
@ -626,7 +625,7 @@ StreamCopy(Stream * in, Stream * out)
|
|||
}
|
||||
else
|
||||
{
|
||||
UtilSleepMillis(UInt64Create(0, STREAM_DELAY));
|
||||
UtilSleepMillis(STREAM_DELAY);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
||||
|
@ -23,7 +23,7 @@
|
|||
*/
|
||||
#include <Tls.h>
|
||||
|
||||
#if TLS_IMPL == TLS_LIBRESSL
|
||||
#if defined(TLS_IMPL) && (TLS_IMPL == TLS_LIBRESSL)
|
||||
|
||||
#include <Memory.h>
|
||||
#include <Log.h>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
||||
|
@ -23,7 +23,7 @@
|
|||
*/
|
||||
#include <Tls.h>
|
||||
|
||||
#if TLS_IMPL == TLS_OPENSSL
|
||||
#if defined(TLS_IMPL) && (TLS_IMPL == TLS_OPENSSL)
|
||||
|
||||
#include <Memory.h>
|
||||
#include <Log.h>
|
||||
|
@ -71,14 +71,6 @@ TlsInitClient(int fd, const char *serverName)
|
|||
OpenSSLCookie *cookie;
|
||||
char errorStr[256];
|
||||
|
||||
/*
|
||||
* TODO: Seems odd that this isn't needed to make the
|
||||
* connection... we should figure out how to verify the
|
||||
* certificate matches the server we think we're
|
||||
* connecting to.
|
||||
*/
|
||||
(void) serverName;
|
||||
|
||||
cookie = Malloc(sizeof(OpenSSLCookie));
|
||||
if (!cookie)
|
||||
{
|
||||
|
@ -89,12 +81,14 @@ TlsInitClient(int fd, const char *serverName)
|
|||
|
||||
cookie->method = TLS_client_method();
|
||||
cookie->ctx = SSL_CTX_new(cookie->method);
|
||||
cookie->fd = fd;
|
||||
if (!cookie->ctx)
|
||||
{
|
||||
goto error;
|
||||
}
|
||||
|
||||
cookie->ssl = SSL_new(cookie->ctx);
|
||||
SSL_set_tlsext_host_name(cookie->ssl, serverName);
|
||||
if (!cookie->ssl)
|
||||
{
|
||||
goto error;
|
||||
|
@ -295,9 +289,7 @@ TlsClose(void *cookie)
|
|||
SSL_free(ssl->ssl);
|
||||
SSL_CTX_free(ssl->ctx);
|
||||
|
||||
#if 0
|
||||
close(ssl->fd);
|
||||
#endif
|
||||
|
||||
Free(ssl);
|
||||
|
||||
|
|
265
src/UInt64.c
265
src/UInt64.c
|
@ -1,265 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#include <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
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
||||
|
|
92
src/Util.c
92
src/Util.c
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
||||
|
@ -24,6 +24,7 @@
|
|||
#include <Util.h>
|
||||
|
||||
#include <Memory.h>
|
||||
#include <Platform.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -40,8 +41,6 @@
|
|||
#include <limits.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include <UInt64.h>
|
||||
|
||||
#ifndef PATH_MAX
|
||||
#define PATH_MAX 256
|
||||
#endif
|
||||
|
@ -50,14 +49,14 @@
|
|||
#define SSIZE_MAX LONG_MAX
|
||||
#endif
|
||||
|
||||
UInt64
|
||||
UtilServerTs(void)
|
||||
uint64_t
|
||||
UtilTsMillis(void)
|
||||
{
|
||||
struct timeval tv;
|
||||
|
||||
UInt64 ts;
|
||||
UInt64 sec;
|
||||
UInt64 usec;
|
||||
uint64_t ts;
|
||||
uint64_t sec;
|
||||
uint64_t usec;
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
|
||||
|
@ -77,59 +76,44 @@ UtilServerTs(void)
|
|||
*
|
||||
* 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;
|
||||
|
||||
sec = UInt64Create(high, low);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* 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));
|
||||
}
|
||||
// Two separate steps because time_t might be 32-bit. In that
|
||||
// case, we want the multiplication to happen after the promotion
|
||||
// to uint64_t.
|
||||
sec = tv.tv_sec;
|
||||
sec *= 1000;
|
||||
|
||||
usec = UInt64Create(0, tv.tv_usec / 1000);
|
||||
ts = UInt64Add(sec, usec);
|
||||
usec = tv.tv_usec / 1000;
|
||||
|
||||
ts = sec + usec;
|
||||
|
||||
return ts;
|
||||
}
|
||||
|
||||
UInt64
|
||||
#ifdef PLATFORM_DARWIN
|
||||
#define st_mtim st_mtimespec
|
||||
#endif
|
||||
|
||||
uint64_t
|
||||
UtilLastModified(char *path)
|
||||
{
|
||||
struct stat st;
|
||||
UInt64 ts = UInt64Create(0, 0);
|
||||
uint64_t ts = 0;
|
||||
|
||||
if (stat(path, &st) == 0)
|
||||
{
|
||||
if (sizeof(time_t) == sizeof(UInt64))
|
||||
{
|
||||
/* 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));
|
||||
ts = st.st_mtim.tv_sec;
|
||||
ts *= 1000;
|
||||
ts += st.st_mtim.tv_nsec / 1000000;
|
||||
}
|
||||
|
||||
return ts;
|
||||
}
|
||||
|
||||
#ifdef PLATFORM_DARWIN
|
||||
#undef st_mtim
|
||||
#endif
|
||||
|
||||
int
|
||||
UtilMkdir(const char *dir, const mode_t mode)
|
||||
{
|
||||
|
@ -202,21 +186,13 @@ UtilMkdir(const char *dir, const mode_t mode)
|
|||
}
|
||||
|
||||
int
|
||||
UtilSleepMillis(UInt64 ms)
|
||||
UtilSleepMillis(uint64_t ms)
|
||||
{
|
||||
struct timespec ts;
|
||||
int res;
|
||||
|
||||
if (sizeof(time_t) == sizeof(UInt64))
|
||||
{
|
||||
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;
|
||||
ts.tv_sec = ms / 1000;
|
||||
ts.tv_nsec = (ms % 1000) * 1000000;
|
||||
|
||||
res = nanosleep(&ts, &ts);
|
||||
|
||||
|
@ -312,14 +288,14 @@ ThreadNoDestructor(void *p)
|
|||
free(p);
|
||||
}
|
||||
|
||||
UInt32
|
||||
uint32_t
|
||||
UtilThreadNo(void)
|
||||
{
|
||||
static pthread_key_t key;
|
||||
static int createdKey = 0;
|
||||
static unsigned long count = 0;
|
||||
|
||||
UInt32 *no;
|
||||
uint32_t *no;
|
||||
|
||||
if (!createdKey)
|
||||
{
|
||||
|
@ -330,7 +306,7 @@ UtilThreadNo(void)
|
|||
no = pthread_getspecific(key);
|
||||
if (!no)
|
||||
{
|
||||
no = malloc(sizeof(UInt32));
|
||||
no = malloc(sizeof(uint32_t));
|
||||
*no = count++;
|
||||
pthread_setspecific(key, no);
|
||||
}
|
||||
|
|
|
@ -1,122 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef CYTOPLASM_INT_H
|
||||
#define CYTOPLASM_INT_H
|
||||
|
||||
/***
|
||||
* @Nm Int
|
||||
* @Nd Fixed-width integer types.
|
||||
* @Dd April 27 2023
|
||||
*
|
||||
* This header provides cross-platform, fixed-width integer types.
|
||||
* Specifically, it uses preprocessor magic to define the following
|
||||
* types:
|
||||
* .Bl -bullet -offset indent
|
||||
* .It
|
||||
* Int8 and UInt8
|
||||
* .It
|
||||
* Int16 and UInt16
|
||||
* .It
|
||||
* Int32 and UInt32
|
||||
* .El
|
||||
* .Pp
|
||||
* Note that there is no 64-bit integer type, because the ANSI C
|
||||
* standard makes no guarantee that such a type will exist, even
|
||||
* though it does on most platforms.
|
||||
* .Pp
|
||||
* The reason Cytoplasm provides its own header for this is
|
||||
* because ANSI C does not define fixed-width types, and while it
|
||||
* should be safe to rely on C99 fixed-width types in most cases,
|
||||
* there may be cases where even that is not possible.
|
||||
*
|
||||
* @ignore-typedefs
|
||||
*/
|
||||
|
||||
#include <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 */
|
|
@ -1,252 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef CYTOPLASM_INT64_H
|
||||
#define CYTOPLASM_INT64_H
|
||||
|
||||
/***
|
||||
* @Nm Int64
|
||||
* @Nd Fixed-width 64 bit integers.
|
||||
* @Dd August 11, 2023
|
||||
*
|
||||
* .Pp
|
||||
* ANSI C89 (or C99 for that matter) provides no required mechanism
|
||||
* for 64 bit integers. Nevertheless, many compilers provide them as
|
||||
* extensions. However, since it is not a gaurantee, and to be fully
|
||||
* standards-compliant and thus portable, a platform-agnostic interface
|
||||
* is required. This header provides such an interface. If the platform
|
||||
* has a 64 bit integer type, that is used, and native operations are
|
||||
* performed by C preprocessor macro expansion. Otherwise, a
|
||||
* compatibility layer is provided, which implements 64-bit
|
||||
* arithmetic on an array of 2 32-bit numbers which are provided by
|
||||
* .Xr Int 3 .
|
||||
* .Pp
|
||||
* Note that 64-bit emulation is certainly not as performant as using
|
||||
* native 64-bit operations, so whenever possible, the native
|
||||
* operations should be preferred. However, since C provides no required
|
||||
* 64 bit integer on 32-bit and less platforms, this API can be used as
|
||||
* a "good enough" fallback mechanism.
|
||||
* .Pp
|
||||
* Also note that this implementation, both in the native and
|
||||
* non-native forms, makes some assumptions:
|
||||
* .Bl -bullet -width Ds
|
||||
* .It
|
||||
* When a cast from a larger integer to a smaller integer is performed,
|
||||
* the upper bits are truncated, not the lower bits.
|
||||
* .It
|
||||
* Negative numbers are represented in memory and in registers in two's
|
||||
* compliment form.
|
||||
* .El
|
||||
* .Pp
|
||||
* This API may provide unexpected output if these assumptions are
|
||||
* false for a given platform.
|
||||
*
|
||||
* @ignore-typedefs
|
||||
*/
|
||||
|
||||
#include "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 */
|
|
@ -1,252 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef CYTOPLASM_UINT64_H
|
||||
#define CYTOPLASM_UINT64_H
|
||||
|
||||
/***
|
||||
* @Nm UInt64
|
||||
* @Nd Fixed-width 64 bit integers.
|
||||
* @Dd August 11, 2023
|
||||
*
|
||||
* .Pp
|
||||
* ANSI C89 (or C99 for that matter) provides no required mechanism
|
||||
* for 64 bit integers. Nevertheless, many compilers provide them as
|
||||
* extensions. However, since it is not a gaurantee, and to be fully
|
||||
* standards-compliant and thus portable, a platform-agnostic interface
|
||||
* is required. This header provides such an interface. If the platform
|
||||
* has a 64 bit integer type, that is used, and native operations are
|
||||
* performed by C preprocessor macro expansion. Otherwise, a
|
||||
* compatibility layer is provided, which implements 64-bit
|
||||
* arithmetic on an array of 2 32-bit numbers which are provided by
|
||||
* .Xr Int 3 .
|
||||
* .Pp
|
||||
* Note that 64-bit emulation is certainly not as performant as using
|
||||
* native 64-bit operations, so whenever possible, the native
|
||||
* operations should be preferred. However, since C provides no required
|
||||
* 64 bit integer on 32-bit and less platforms, this API can be used as
|
||||
* a "good enough" fallback mechanism.
|
||||
* .Pp
|
||||
* Also note that this implementation, both in the native and
|
||||
* non-native forms, makes some assumptions:
|
||||
* .Bl -bullet -width Ds
|
||||
* .It
|
||||
* When a cast from a larger integer to a smaller integer is performed,
|
||||
* the upper bits are truncated, not the lower bits.
|
||||
* .It
|
||||
* Negative numbers are represented in memory and in registers in two's
|
||||
* compliment form.
|
||||
* .El
|
||||
* .Pp
|
||||
* This API may provide unexpected output if these assumptions are
|
||||
* false for a given platform.
|
||||
*
|
||||
* @ignore-typedefs
|
||||
*/
|
||||
|
||||
#include "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 */
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
||||
|
|
487
tools/j2s.c
487
tools/j2s.c
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
||||
|
@ -25,6 +25,7 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <Log.h>
|
||||
#include <Array.h>
|
||||
|
@ -37,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)
|
||||
{
|
||||
|
@ -45,33 +47,6 @@ Trim(char c, char *str)
|
|||
return str;
|
||||
}
|
||||
|
||||
static char *
|
||||
JsonTypeFunction(JsonType jsonType)
|
||||
{
|
||||
char *func;
|
||||
|
||||
switch (jsonType)
|
||||
{
|
||||
case JSON_STRING:
|
||||
func = "String";
|
||||
break;
|
||||
case JSON_INTEGER:
|
||||
func = "Integer";
|
||||
break;
|
||||
case JSON_FLOAT:
|
||||
func = "Float";
|
||||
break;
|
||||
case JSON_BOOLEAN:
|
||||
func = "Boolean";
|
||||
break;
|
||||
default:
|
||||
/* Should not happen */
|
||||
func = NULL;
|
||||
break;
|
||||
}
|
||||
return func;
|
||||
}
|
||||
|
||||
static JsonType
|
||||
TypeToJsonType(char *type)
|
||||
{
|
||||
|
@ -101,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;
|
||||
|
@ -117,7 +96,7 @@ JsonTypeToStr(JsonType type)
|
|||
{
|
||||
switch (type)
|
||||
{
|
||||
case JSON_OBJECT:
|
||||
case JSON_OBJECT:
|
||||
return "JSON_OBJECT";
|
||||
case JSON_ARRAY:
|
||||
return "JSON_ARRAY";
|
||||
|
@ -336,7 +315,7 @@ Main(Array * args)
|
|||
ArrayAdd(requiredTypes, StrDuplicate(type));
|
||||
}
|
||||
|
||||
if (StrEquals(typeType, "struct") || StrEquals(typeType, "union"))
|
||||
if (StrEquals(typeType, "struct"))
|
||||
{
|
||||
typeFieldsVal = HashMapGet(typeObj, "fields");
|
||||
if (JsonValueType(typeFieldsVal) != JSON_OBJECT)
|
||||
|
@ -350,7 +329,8 @@ Main(Array * args)
|
|||
while (HashMapIterate(typeFields, &fieldName, (void **) &fieldVal))
|
||||
{
|
||||
char *fieldType;
|
||||
int isArrType = 0;
|
||||
bool isArrType = false;
|
||||
bool isObjType = false;
|
||||
JsonValue *requiredVal;
|
||||
JsonValue *ignoreVal;
|
||||
|
||||
|
@ -369,11 +349,17 @@ Main(Array * args)
|
|||
goto finish;
|
||||
}
|
||||
|
||||
if (*fieldType == '[' && fieldType[strlen(fieldType) - 1] == ']')
|
||||
if (IsDelimited(fieldType, '[', ']'))
|
||||
{
|
||||
fieldType++;
|
||||
fieldType[strlen(fieldType) - 1] = '\0';
|
||||
isArrType = 1;
|
||||
isArrType = true;
|
||||
}
|
||||
else if (IsDelimited(fieldType, '{', '}'))
|
||||
{
|
||||
fieldType++;
|
||||
fieldType[strlen(fieldType) - 1] = '\0';
|
||||
isObjType = true;
|
||||
}
|
||||
|
||||
if (!StrEquals(fieldType, "object") &&
|
||||
|
@ -400,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)
|
||||
|
@ -486,10 +476,11 @@ Main(Array * args)
|
|||
StreamPrintf(headerFile, "#ifndef %s\n", guard);
|
||||
StreamPrintf(headerFile, "#define %s\n\n", guard);
|
||||
|
||||
StreamPrintf(headerFile, "#include <stdint.h>\n");
|
||||
StreamPrintf(headerFile, "#include <stdbool.h>\n");
|
||||
|
||||
StreamPrintf(headerFile, "#include <Cytoplasm/Array.h>\n");
|
||||
StreamPrintf(headerFile, "#include <Cytoplasm/HashMap.h>\n");
|
||||
StreamPrintf(headerFile, "#include <Cytoplasm/Int64.h>\n");
|
||||
StreamPrintf(headerFile, "#include <Cytoplasm/Json.h>\n");
|
||||
|
||||
StreamPutc(headerFile, '\n');
|
||||
|
||||
|
@ -509,7 +500,6 @@ Main(Array * args)
|
|||
HashMap *fields;
|
||||
|
||||
char *field;
|
||||
char *info;
|
||||
HashMap *fieldDesc;
|
||||
|
||||
if (!type)
|
||||
|
@ -526,29 +516,9 @@ Main(Array * args)
|
|||
|
||||
fields = JsonValueAsObject(JsonGet(types, 2, type, "fields"));
|
||||
|
||||
StreamPrintf(headerFile, "typedef %s %s\n{\n", typeType, type);
|
||||
|
||||
if (StrEquals(typeType, "union"))
|
||||
{
|
||||
Array *keys = HashMapKeys(fields);
|
||||
|
||||
size_t j;
|
||||
|
||||
StreamPrintf(headerFile, "typedef enum %sType\n{\n", type);
|
||||
|
||||
for (j = 0; j < ArraySize(keys); j++)
|
||||
{
|
||||
char *key = ArrayGet(keys, j);
|
||||
char *comma = j == ArraySize(keys) - 1 ? "\n" : ",\n";
|
||||
StreamPrintf(headerFile, " %s_AS_%s%s", type, key, comma);
|
||||
}
|
||||
|
||||
ArrayFree(keys);
|
||||
StreamPrintf(headerFile, "} %sType;\n\n", type);
|
||||
}
|
||||
info = StrEquals(typeType, "union") ? "Union" : "";
|
||||
StreamPrintf(headerFile, "typedef %s %s%s\n{\n", typeType, type, info);
|
||||
|
||||
if (StrEquals(typeType, "struct") || StrEquals(typeType, "union"))
|
||||
if (StrEquals(typeType, "struct"))
|
||||
{
|
||||
while (HashMapIterate(fields, &field, (void **) &fieldDesc))
|
||||
{
|
||||
|
@ -565,21 +535,23 @@ Main(Array * args)
|
|||
}
|
||||
else if (StrEquals(fieldType, "integer"))
|
||||
{
|
||||
cType = "Int64";
|
||||
cType = "int64_t";
|
||||
}
|
||||
else if (StrEquals(fieldType, "boolean"))
|
||||
{
|
||||
cType = "int";
|
||||
cType = "bool";
|
||||
}
|
||||
else if (StrEquals(fieldType, "float"))
|
||||
{
|
||||
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 *";
|
||||
}
|
||||
|
@ -588,18 +560,23 @@ Main(Array * args)
|
|||
cType = fieldType;
|
||||
}
|
||||
|
||||
StreamPrintf(headerFile, " %s %s;\n", cType, field);
|
||||
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%s;\n\n", type, info);
|
||||
|
||||
if (StrEquals(typeType, "union"))
|
||||
{
|
||||
StreamPrintf(headerFile, "typedef struct %s {\n", type);
|
||||
StreamPrintf(headerFile, " %sType type;\n", type);
|
||||
StreamPrintf(headerFile, " %s%s value;\n", type, info);
|
||||
StreamPrintf(headerFile, "} %s;\n\n", type);
|
||||
}
|
||||
StreamPrintf(headerFile, "} %s;\n\n", type);
|
||||
}
|
||||
else if (StrEquals(typeType, "enum"))
|
||||
{
|
||||
|
@ -679,8 +656,8 @@ Main(Array * args)
|
|||
|
||||
if (StrEquals(typeType, "struct"))
|
||||
{
|
||||
StreamPrintf(headerFile, "extern int %sFromJson(HashMap *, %s *, char **);\n", type, type);
|
||||
StreamPrintf(implFile, "int\n%sFromJson(HashMap *json, %s *out, char **errp)\n{\n", type, type);
|
||||
StreamPrintf(headerFile, "extern bool %sFromJson(HashMap *, %s *, char **);\n", type, type);
|
||||
StreamPrintf(implFile, "bool\n%sFromJson(HashMap *json, %s *out, char **errp)\n{\n", type, type);
|
||||
StreamPrintf(implFile, " JsonValue *val;\n");
|
||||
StreamPrintf(implFile, " int enumParseRes;\n");
|
||||
StreamPrintf(implFile, "\n");
|
||||
|
@ -689,14 +666,14 @@ Main(Array * args)
|
|||
StreamPrintf(implFile, " if (!json | !out)\n"
|
||||
" {\n"
|
||||
" *errp = \"Invalid pointers passed to %sFromJson()\";\n"
|
||||
" return 0;\n"
|
||||
" return false;\n"
|
||||
" }\n\n"
|
||||
,type);
|
||||
for (i = 0; i < ArraySize(keys); i++)
|
||||
{
|
||||
char *key = ArrayGet(keys, i);
|
||||
int required = JsonValueAsBoolean(JsonGet(fields, 2, key, "required"));
|
||||
int ignore = JsonValueAsBoolean(JsonGet(fields, 2, key, "ignore"));
|
||||
bool required = JsonValueAsBoolean(JsonGet(fields, 2, key, "required"));
|
||||
bool ignore = JsonValueAsBoolean(JsonGet(fields, 2, key, "ignore"));
|
||||
char *fieldType = JsonValueAsString(JsonGet(fields, 2, key, "type"));
|
||||
int isEnum = StrEquals(JsonValueAsString(JsonGet(types, 2, fieldType, "type")), "enum");
|
||||
JsonType jsonType = isEnum ? JSON_STRING : TypeToJsonType(fieldType);
|
||||
|
@ -713,7 +690,7 @@ Main(Array * args)
|
|||
StreamPrintf(implFile, " if (val)\n {\n");
|
||||
StreamPrintf(implFile, " if (JsonValueType(val) != %s)\n {\n", jsonTypeStr);
|
||||
StreamPrintf(implFile, " *errp = \"%s.%s must be of type %s.\";\n", type, Trim('_', key), fieldType);
|
||||
StreamPrintf(implFile, " return 0;\n");
|
||||
StreamPrintf(implFile, " return false;\n");
|
||||
StreamPrintf(implFile, " }\n\n");
|
||||
if (StrEquals(fieldType, "array"))
|
||||
{
|
||||
|
@ -727,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';
|
||||
|
@ -738,7 +842,7 @@ Main(Array * args)
|
|||
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 0;\n");
|
||||
StreamPrintf(implFile, " return false;\n");
|
||||
StreamPrintf(implFile, " }\n");
|
||||
StreamPrintf(implFile, " else\n");
|
||||
StreamPrintf(implFile, " {\n");
|
||||
|
@ -757,7 +861,7 @@ Main(Array * args)
|
|||
|
||||
if (StrEquals(fieldType, "integer"))
|
||||
{
|
||||
cType = "Int64";
|
||||
cType = "int64_t";
|
||||
}
|
||||
else if (StrEquals(fieldType, "float"))
|
||||
{
|
||||
|
@ -765,7 +869,7 @@ Main(Array * args)
|
|||
}
|
||||
else if (StrEquals(fieldType, "boolean"))
|
||||
{
|
||||
cType = "int";
|
||||
cType = "bool";
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -779,13 +883,13 @@ Main(Array * args)
|
|||
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 0;\n");
|
||||
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 array value.\";\n");
|
||||
StreamPrintf(implFile, " return 0;\n");
|
||||
StreamPrintf(implFile, " return false;\n");
|
||||
StreamPrintf(implFile, " }\n");
|
||||
StreamPrintf(implFile, " *ref = JsonValueAs%s(v);\n", fieldType);
|
||||
StreamPrintf(implFile, " ArrayAdd(out->%s, ref);\n", key);
|
||||
|
@ -797,7 +901,7 @@ Main(Array * args)
|
|||
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 0;\n");
|
||||
StreamPrintf(implFile, " return false;\n");
|
||||
StreamPrintf(implFile, " }\n");
|
||||
StreamPrintf(implFile, " ArrayAdd(out->%s, StrDuplicate(JsonValueAsString(v)));\n", key);
|
||||
}
|
||||
|
@ -806,7 +910,7 @@ Main(Array * args)
|
|||
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 0;\n");
|
||||
StreamPrintf(implFile, " return false;\n");
|
||||
StreamPrintf(implFile, " }\n");
|
||||
StreamPrintf(implFile, " ArrayAdd(out->%s, JsonDuplicate(JsonValueAsObject(v)));\n", key);
|
||||
}
|
||||
|
@ -820,13 +924,13 @@ Main(Array * args)
|
|||
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 0;\n");
|
||||
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 0;\n");
|
||||
StreamPrintf(implFile, " return false;\n");
|
||||
StreamPrintf(implFile, " }\n");
|
||||
if (isEnum)
|
||||
{
|
||||
|
@ -844,7 +948,7 @@ Main(Array * args)
|
|||
StreamPrintf(implFile, " %sFree(parsed);\n", fieldType);
|
||||
}
|
||||
StreamPrintf(implFile, " Free(parsed);\n");
|
||||
StreamPrintf(implFile, " return 0;\n");
|
||||
StreamPrintf(implFile, " return false;\n");
|
||||
StreamPrintf(implFile, " }\n");
|
||||
StreamPrintf(implFile, " ArrayAdd(out->%s, parsed);\n", key);
|
||||
}
|
||||
|
@ -857,12 +961,32 @@ Main(Array * args)
|
|||
else if (jsonType == JSON_OBJECT)
|
||||
{
|
||||
StreamPrintf(implFile, " if (!%sFromJson(JsonValueAsObject(val), &out->%s, errp))\n {\n", fieldType, key);
|
||||
StreamPrintf(implFile, " return 0;\n");
|
||||
StreamPrintf(implFile, " return false;\n");
|
||||
StreamPrintf(implFile, " }\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
char *func = JsonTypeFunction(jsonType);
|
||||
char *func;
|
||||
|
||||
switch (jsonType)
|
||||
{
|
||||
case JSON_STRING:
|
||||
func = "String";
|
||||
break;
|
||||
case JSON_INTEGER:
|
||||
func = "Integer";
|
||||
break;
|
||||
case JSON_FLOAT:
|
||||
func = "Float";
|
||||
break;
|
||||
case JSON_BOOLEAN:
|
||||
func = "Boolean";
|
||||
break;
|
||||
default:
|
||||
/* Should not happen */
|
||||
func = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (jsonType == JSON_STRING)
|
||||
{
|
||||
|
@ -872,7 +996,7 @@ Main(Array * args)
|
|||
StreamPrintf(implFile, " if (enumParseRes == -1)\n", key);
|
||||
StreamPrintf(implFile, " {\n");
|
||||
StreamPrintf(implFile, " *errp = \"Invalid value for %s.%s.\";\n", type, key);
|
||||
StreamPrintf(implFile, " return 0;\n");
|
||||
StreamPrintf(implFile, " return false;\n");
|
||||
StreamPrintf(implFile, " }\n");
|
||||
StreamPrintf(implFile, " out->%s = enumParseRes;\n", key);
|
||||
}
|
||||
|
@ -892,13 +1016,13 @@ Main(Array * args)
|
|||
{
|
||||
StreamPrintf(implFile, " else\n {\n");
|
||||
StreamPrintf(implFile, " *errp = \"%s.%s is required.\";\n", type, key);
|
||||
StreamPrintf(implFile, " return 0;\n");
|
||||
StreamPrintf(implFile, " return false;\n");
|
||||
StreamPrintf(implFile, " }\n");
|
||||
}
|
||||
|
||||
StreamPutc(implFile, '\n');
|
||||
}
|
||||
StreamPrintf(implFile, " return 1;\n");
|
||||
StreamPrintf(implFile, " return true;\n");
|
||||
StreamPrintf(implFile, "}\n\n");
|
||||
|
||||
StreamPrintf(headerFile, "extern HashMap * %sToJson(%s *);\n", type, type);
|
||||
|
@ -918,8 +1042,8 @@ Main(Array * args)
|
|||
{
|
||||
char *key = ArrayGet(keys, i);
|
||||
char *fieldType = JsonValueAsString(JsonGet(fields, 2, key, "type"));
|
||||
int isEnum = StrEquals(JsonValueAsString(JsonGet(types, 2, fieldType, "type")), "enum");
|
||||
int ignore = JsonValueAsBoolean(JsonGet(fields, 2, key, "ignore"));
|
||||
bool isEnum = StrEquals(JsonValueAsString(JsonGet(types, 2, fieldType, "type")), "enum");
|
||||
bool ignore = JsonValueAsBoolean(JsonGet(fields, 2, key, "ignore"));
|
||||
|
||||
if (ignore)
|
||||
{
|
||||
|
@ -949,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;
|
||||
|
||||
|
@ -987,7 +1177,7 @@ Main(Array * args)
|
|||
|
||||
if (StrEquals(fieldType, "integer"))
|
||||
{
|
||||
cType = "Int64";
|
||||
cType = "int64_t";
|
||||
}
|
||||
else if (StrEquals(fieldType, "float"))
|
||||
{
|
||||
|
@ -995,7 +1185,7 @@ Main(Array * args)
|
|||
}
|
||||
else if (StrEquals(fieldType, "boolean"))
|
||||
{
|
||||
cType = "int";
|
||||
cType = "bool";
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1070,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;
|
||||
|
||||
|
@ -1112,72 +1331,6 @@ Main(Array * args)
|
|||
StreamPrintf(implFile, "}\n\n");
|
||||
StreamPutc(headerFile, '\n');
|
||||
}
|
||||
else if (StrEquals(typeType, "union"))
|
||||
{
|
||||
StreamPrintf(headerFile, "extern int %sFromValue(JsonValue *, %s *, char **);\n", type, type);
|
||||
StreamPrintf(implFile, "int\n%sFromValue(JsonValue *val, %s *out, char **errp)\n{\n", type, type);
|
||||
StreamPrintf(implFile, " if (!val || !out)\n");
|
||||
StreamPrintf(implFile, " {\n");
|
||||
StreamPrintf(implFile, " *errp = \"Invalid values passed to %sFromValue\";\n", type);
|
||||
StreamPrintf(implFile, " return 0;\n");
|
||||
StreamPrintf(implFile, " }\n\n");
|
||||
|
||||
for (i = 0; i < ArraySize(keys); i++)
|
||||
{
|
||||
char *key = ArrayGet(keys, i);
|
||||
char *fieldType = JsonValueAsString(JsonGet(fields, 2, key, "type"));
|
||||
int isSimple = StrEquals(fieldType, "string") ||
|
||||
StrEquals(fieldType, "boolean") ||
|
||||
StrEquals(fieldType, "integer") ||
|
||||
StrEquals(fieldType, "float") ;
|
||||
int ignore = JsonValueAsBoolean(JsonGet(fields, 2, key, "ignore"));
|
||||
if (isSimple)
|
||||
{
|
||||
JsonType actualType = TypeToJsonType(fieldType);
|
||||
char *func = JsonTypeFunction(actualType);
|
||||
|
||||
StreamPrintf(implFile, " if (JsonValueType(val) == %s)\n", JsonTypeToStr(actualType));
|
||||
StreamPrintf(implFile, " {\n");
|
||||
StreamPrintf(implFile, " out->type = %s_AS_%s;\n", type, key);
|
||||
StreamPrintf(implFile, " out->value.%s = JsonValueAs%s(val);\n\n", key, func);
|
||||
StreamPrintf(implFile, " return 1;\n");
|
||||
StreamPrintf(implFile, " }\n");
|
||||
continue;
|
||||
}
|
||||
if (StrEquals(fieldType, "array"))
|
||||
{
|
||||
StreamPrintf(implFile, " if (JsonValueType(val) == JSON_ARRAY)\n");
|
||||
StreamPrintf(implFile, " {\n");
|
||||
StreamPrintf(implFile, " out->type = %s_AS_%s;\n", type, key);
|
||||
StreamPrintf(implFile, " out->value.%s = JsonValueAsArray(val);\n\n", key);
|
||||
StreamPrintf(implFile, " return 1;\n");
|
||||
StreamPrintf(implFile, " }\n");
|
||||
continue;
|
||||
}
|
||||
if (ignore)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
StreamPrintf(implFile, " if (JsonValueType(val) == JSON_OBJECT)\n");
|
||||
StreamPrintf(implFile, " {\n");
|
||||
StreamPrintf(implFile, " HashMap *json = JsonValueAsObject(val);\n");
|
||||
StreamPrintf(implFile, " if (!%sFromJson(json, &out->value.%s, errp))\n", fieldType, key, fieldType);
|
||||
StreamPrintf(implFile, " {\n");
|
||||
StreamPrintf(implFile, " /* errp is already set */\n");
|
||||
StreamPrintf(implFile, " return 0;\n");
|
||||
StreamPrintf(implFile, " }\n");
|
||||
StreamPrintf(implFile, " out->type = %s_AS_%s;\n", type, key);
|
||||
StreamPrintf(implFile, " return 1;\n");
|
||||
StreamPrintf(implFile, " }\n");
|
||||
|
||||
/* TODO */
|
||||
}
|
||||
|
||||
StreamPrintf(implFile, " \n");
|
||||
StreamPrintf(implFile, " *errp = \"Invalid type for %s\";\n", type);
|
||||
StreamPrintf(implFile, " return 0;\n");
|
||||
StreamPrintf(implFile, "}\n");
|
||||
}
|
||||
else if (StrEquals(typeType, "enum"))
|
||||
{
|
||||
StreamPrintf(headerFile, "extern int %sFromStr(char *);\n", type);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
* 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
|
||||
|
|
Loading…
Reference in a new issue