Compare commits

..

No commits in common. "5dc1ec49eb777e3887014ad0d682bad664068ce6" and "b6388eb7fe35325fdbe528fa3b369f1848ca0f05" have entirely different histories.

77 changed files with 2114 additions and 1200 deletions

View file

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

View file

@ -1,35 +0,0 @@
# Cytoplasm Change Log
This document contains the complete change log for every official release of
Cytoplasm. It is intended to be updated with every commit that makes a user-facing
change worth reporting in the change log. As such, it changes frequently between
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
encoding and decoding. Note that additional fields are totally untouched—they
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.
## v0.4.0
**Released on November 1, 2023**
This is the first independent release of Cytoplasm! Last month, Cytoplasm was
split off of [Telodendria](/Telodendria/Telodendria) to become its own independent
project with its own independent releases. This allows it to develop at a much more
rapid pace than Telodendria.
Changes in future releases will be reported here. Since this is the first release,
there are no changes to show.

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files * obtaining a copy of this software and associated documentation files

106
README.md
View file

@ -1,39 +1,31 @@
<p align="center"><img src="https://telodendria.io/user/themes/bancino/images/logo/Cytoplasm.png"></p> # Cytoplasm (libcytoplasm)
<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, particularly on systems that don't have advanced tools like `valgrind`. Cytoplasm is a general-purpose C library and runtime stub 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 perform various complex tasks. Cytoplasm provides high-level data structures, a basic logging facility, an HTTP client and server, and more.
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 aims not to only do one thing well, but to do many things good enough. The primary target of Cytoplasm is simple, yet higher level C applications that have to perform relatively complex tasks, but don't want to pull in 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 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 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 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. 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 safetly, this inevitably adds a little overhead.
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. 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 a Telodendria project, and is maintained along side of Telodendria itself, even living in the same CVS module, 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. 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.
Cytoplasm also starts with a C, which I think is a nice touch for C libraries. It's also fun to say and unique enough that searching for "libcytoplasm" should bring you to this project and not some other one. Cytoplasm also starts with a C, which I think is a nice touch for C libraries. It's also fun to say and unique enough that searching for "libcytoplasm" should bring you to this project and not some other one.
## Requirements
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
If TLS support is not enabled, all APIs that use it should fall back to non-TLS behavior in a sensible manner. For example, if TLS support is not enabled, then the HTTP client API will simply return an error if a TLS connection is requested.
## Building ## Building
If your operating system or software distribution provides a pre-built package of Cytoplasm, you should prefer to use that instead of building it from source. If your operating system or software distribution provides a pre-built package of Cytoplasm, you should prefer to use that instead of building it from source.
Cytoplasm uses the standard C library build procedure. Just run these commands: Cytoplasm aims to have zero 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 by setting the TLS_IMPL environment variable. The supported TLS implementations are as follows:
``` - OpenSSL
./configure - LibreSSL
make
``` If TLS support is not enabled, all APIs that use it should fall back to non-TLS behavior in a sensible manner. For example, if TLS support is not enabled, then the HTTP client API will simply return an error if a TLS connection is requested. Cytoplasm uses a custom build script instead of Make, for the sake of portability. To build everything, just run the script:
$ sh make.sh
This will produce the following out/ directory: This will produce the following out/ directory:
@ -41,76 +33,46 @@ This will produce the following out/ directory:
out/ out/
lib/ lib/
libcytoplasm.so - The Cytoplasm shared library. libcytoplasm.so - The Cytoplasm shared library.
libcytoplasm.a - The Cytoplasm static archive. libcytoplasm.a - The Cytoplasm static library.
bin/ - A few useful tools build with Cytoplasm. cytoplasm.o - The Cytoplasm runtime stub.
bin/
hdoc - The documentation generator tool.
man/ - All Cytoplasm API documentation. man/ - All Cytoplasm API documentation.
``` ```
You can also run `make install` as `root` to install Cytoplasm to the system. This will install the libraries, tools, and `man` pages.
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)-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.
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.
The following recipes are available in the generated `Makefile`:
- `all`: This is the default target. It builds everything.
- `Cytoplasm`: Build the `libCytoplasm.(so|a)` binaries. If you specified an alternative `--lib-name`, then this target will be named after that.
- `docs`: Generate the header documentation as `man` pages.
- `tools`: Build the supplemental tools which may be useful for development.
- `clean`: Remove the build and output directories. Cytoplasm builds are out-of-tree, which greatly simplifies this recipe compared to in-tree builds.
If you're developing Cytoplasm, these recipes may also be helpful:
- `format`: Format the source code using `indent`. This may require a BSD `indent` because last time I tried GNU `indent`, it didn't like the flags in `indent.pro`. Your mileage may vary.
- `license`: Update the license headers in all source code files with the contents of the `LICENSE.txt`.
To install Telodendria to your system, the following recipes are available:
- `install`: This installs Cytoplasm under the prefix set with `./configure --prefix=<dir>` or with `make PREFIX=<dir>`. By default, the `make` `PREFIX` is set to whatever was set with `configure --prefix`.
- `uninstall`: Uninstall Cytoplasm from the same prefix as specified above.
After a build, you can find the object files in `build/` and the output binaries in `out/lib/`.
## Usage ## Usage
Cytoplasm provides the typical .so and .a files, which can be used to link programs with it in the usual way. Somewhat *unusually* for C libraries, however, it provides its own `main()` function, so programs written with Cytoplasm provide `Main()` instead, which is called by Cytoplasm. Cytoplasm works this way because it needs to perform some setup logic before user code runs and some teardown logic after user code returns. Cytoplasm provides the typical .so and .a files, which can be used to link programs with it in the usual way. However, Cytoplasm also provides a minimal runtime environment that is intended to be used with the library. As such, it also provides a runtime stub, which is intended to be linked in with programs using Cytoplasm. This stub is responsible for setting up and tearing down some Cytoplasm APIs. While it isn't required by any means, it makes Cytoplasm a lot easier to use for programmers by abstracting out all of the common logic that most programs will want to use.
Here is the canonical Hello World written with Cytoplasm: Here is the canonical Hello World written with Cytoplasm:
```c ```c
#include <Cytoplasm/Log.h> #include <Log.h>
int Main(void) int Main(void)
{ {
Log(LOG_INFO, "Hello World!"); Log(LOG_INFO, "Hello World!");
return 0; return 0;
} }
``` ```
If this file is `Hello.c`, then you can compile it by doing this: If this file is Hello.c, then you can compile it by doing something like this:
$ cc -o hello Hello.c -lCytoplasm $ cc -o hello Hello.c cytoplasm.o -lcytoplasm
The full form of `Main()` expected by the stub is as follows: This command assumes that the runtime stub resides in the current working directory, and that libcytoplasm.so is in your library path. If you're using the version of Cytoplasm installed by your operating system or software distribution, consult the documentation for the location of the runtime stub. It may be located in /usr/local/libexec or some other similar location. If you've built Cytoplasm from source and wish to link to it from there, you may wish to do something like this:
$ export CYTOPLASM=/path/to/Cytoplasm/out/lib
$ cc -o hello Hello.c "${CYTOPLASM}/cytoplasm.o" \
"-L${CYTOPLASM}" -lcytoplasm
As you may have noticed, C programs using Cytoplasm's runtime stub don't write the main() function. Instead, they use Main(). The main() function is provided by the runtime stub. The full form of Main() expected by the stub is as follows:
```c ```c
int Main(Array *args, HashMap *env); int Main(Array *args, HashMap *env);
``` ```
The first argument is a Cytoplasm array of the command line arguments, and the second is a Cytoplasm hash map of environment variables. Most linkers will let programs omit the `env` argument, or both arguments if you don't need either. The return value of `Main()` is returned to the operating system, as would be expected. The first argument is a Cytoplasm array of the command line arguments, and the second is a Cytoplasm hash map of environment variables. Most linkers will let programs omit the env argument, or both arguments if you don't need either. The return value of Main() is returned to the operating system.
Note that both arguments to Main may be treated like any other Cytoplasm array or hash map. However, do not invoke `ArrayFree()` or `HashMapFree()` on the passed pointers, because memory is cleaned up after `Main()` returns. Note that both arguments to Main may be treated like any other array or hash map. However, do not invoke ArrayFree() or HashMapFree() on the passed pointers, because memory is cleaned up after Main() returns.
## License
All of the code and documentation for Cytoplasm is licensed under the same license as Telodendria itself. Please refer to [Telodendria &rightarrow; License](/Telodendria/Telodendria#license) for details.
The Cytoplasm logo was designed by [Tobskep](https://tobskep.com) and is licensed under the [Creative Commons Attribution-ShareAlike 4.0](https://creativecommons.org/licenses/by-sa/4.0/) license.

133
configure vendored
View file

@ -10,64 +10,32 @@ echo "-------------------"
BUILD="build" BUILD="build"
OUT="out" OUT="out"
SRC="src" SRC="src"
INCLUDE="include/Cytoplasm" INCLUDE="src/include"
TOOLS="tools" TOOLS="tools"
# Default compiler flags. These must be supported by all POSIX C compilers. CFLAGS="-Wall -Wextra -pedantic -std=c89 -O3 -pipe -D_DEFAULT_SOURCE -I${INCLUDE}"
# "Fancy" compilers that have additional options must be detected and set below. LIBS="-lm -pthread"
CFLAGS="-O1 -D_DEFAULT_SOURCE -I${INCLUDE}"
LIBS="-lm -lpthread"
# Default args for all platforms.
SCRIPT_ARGS="--prefix=/usr/local --lib-name=Cytoplasm"
# Set SSL flags depending on the platform. # Set default args for all platforms
SCRIPT_ARGS="--lib-rtstub=RtStub --prefix=/usr/local --enable-ld-extra --lib-name=Cytoplasm --lib-version=0.4.0 --static $@"
# Set platform specific args
case "$(uname)" in case "$(uname)" in
OpenBSD) OpenBSD)
SCRIPT_ARGS="${SCRIPT_ARGS} --with-libressl" SCRIPT_ARGS="--with-libressl $SCRIPT_ARGS"
;; ;;
*) *)
SCRIPT_ARGS="${SCRIPT_ARGS} --with-openssl" SCRIPT_ARGS="--with-openssl $SCRIPT_ARGS"
;; ;;
esac 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)
# 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 "Processing options..."
echo "Ran with arguments: $SCRIPT_ARGS" echo "Ran with arguments: $SCRIPT_ARGS"
# Process all arguments # Process all arguments
for arg in $SCRIPT_ARGS; do for arg in $SCRIPT_ARGS; do
case "$arg" in 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) --with-openssl)
TLS_IMPL="TLS_OPENSSL" TLS_IMPL="TLS_OPENSSL"
TLS_LIBS="-lcrypto -lssl" TLS_LIBS="-lcrypto -lssl"
@ -77,23 +45,45 @@ for arg in $SCRIPT_ARGS; do
TLS_LIBS="-ltls -lcrypto -lssl" TLS_LIBS="-ltls -lcrypto -lssl"
;; ;;
--disable-tls) --disable-tls)
TLS_IMPL=""
TLS_LIBS="" TLS_LIBS=""
;; ;;
--prefix=*) --prefix=*)
PREFIX=$(echo "$arg" | cut -d '=' -f 2-) 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=*)
LIB_NAME=$(echo "$arg" | cut -d '=' -f 2-) LIB_NAME=$(echo "$arg" | cut -d '=' -f 2-)
;; ;;
--lib-version=*)
LIB_VERSION=$(echo "$arg" | cut -d '=' -f 2-)
;;
--enable-debug) --enable-debug)
DEBUG="-O0 -g" 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) --disable-debug)
DEBUG="" DEBUG=""
;; ;;
--lib-rtstub=*)
STUB=$(echo "$arg" | cut -d '=' -f 2-)
;;
--static)
STATIC="-static -Wl,-static"
;;
--no-static)
STATIC=""
;;
*) *)
echo "Invalid argument: $arg" echo "Invalid argument: $1"
exit 1 exit 1
;; ;;
esac esac
@ -104,8 +94,8 @@ if [ -n "$TLS_IMPL" ]; then
LIBS="${LIBS} ${TLS_LIBS}" LIBS="${LIBS} ${TLS_LIBS}"
fi fi
CFLAGS="${CFLAGS} '-DLIB_NAME=\"${LIB_NAME}\"' ${DEBUG}" CFLAGS="${CFLAGS} '-DLIB_NAME=\"${LIB_NAME}\"' '-DLIB_VERSION=\"${LIB_VERSION}\"' ${DEBUG}"
LDFLAGS="${LIBS} ${LDFLAGS}" LDFLAGS="${LIBS} ${LD_EXTRA}"
# #
# Makefile generation # Makefile generation
@ -137,27 +127,11 @@ print_obj() {
printf '%s ' "$2" printf '%s ' "$2"
} }
get_deps() {
src="$1"
${CC} -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() { compile_obj() {
src="$1" src="$1"
obj="$2" obj="$2"
echo "${obj}: $(get_deps ${src})" cc -I${INCLUDE} -MM -MT "${obj}" "${src}"
echo "${TAB}@mkdir -p $(dirname ${obj})" echo "${TAB}@mkdir -p $(dirname ${obj})"
echo "${TAB}\$(CC) \$(CFLAGS) -fPIC -c -o \"${obj}\" \"${src}\"" echo "${TAB}\$(CC) \$(CFLAGS) -fPIC -c -o \"${obj}\" \"${src}\""
} }
@ -166,46 +140,32 @@ compile_bin() {
src="$1" src="$1"
out="$2" out="$2"
echo "${out}: ${OUT}/lib/lib${LIB_NAME}.a ${OUT}/lib/lib${LIB_NAME}.so ${src}" echo "${out}: ${OUT}/lib/lib${LIB_NAME}.a ${OUT}/lib/lib${LIB_NAME}.so ${OUT}/lib/${LIB_NAME}.o ${src}"
echo "${TAB}@mkdir -p ${OUT}/bin" echo "${TAB}@mkdir -p ${OUT}/bin"
echo "${TAB}\$(CC) \$(CFLAGS) -o \"${out}\" \"${src}\" -L${OUT}/lib \$(LDFLAGS) -l${LIB_NAME}" echo "${TAB}\$(CC) \$(CFLAGS) -o \"${out}\" \"${src}\" ${OUT}/lib/${LIB_NAME}.o -L${OUT}/lib \$(LDFLAGS) -l${LIB_NAME} ${STATIC}"
} }
compile_doc() { compile_doc() {
src="$1" src="$1"
out="$2" out="$2"
pref="LD_LIBRARY_PATH=${OUT}/lib "
echo "${out}: ${OUT}/bin/hdoc ${src}" echo "${out}: ${OUT}/bin/hdoc ${src}"
echo "${TAB}@mkdir -p ${OUT}/man/man3" echo "${TAB}@mkdir -p ${OUT}/man/man3"
echo "${TAB}${pref}${OUT}/bin/hdoc -D \"Os=${LIB_NAME}\" -i \"${src}\" -o \"${out}\"" echo "${TAB}${OUT}/bin/hdoc -D \"Os=${LIB_NAME}\" -i \"${src}\" -o \"${out}\""
} }
install_out() { install_out() {
src="$1" src="$1"
out="$2" out="$2"
dir=$(dirname "$out")
echo "${TAB}mkdir -p \"$dir\"" echo "${TAB}install -D \"$src\" \"$out\""
echo "${TAB}cp \"$src\" \"$out\""
} }
install_man() { install_man() {
src="${OUT}/man/man3/${LIB_NAME}-$(basename $1 .h).3" src="${OUT}/man/man3/${LIB_NAME}-$(basename $1 .h).3"
out="$2" out="$2"
dir=$(dirname "$out")
echo "${TAB}mkdir -p \"$dir\"" echo "${TAB}install -D \"$src\" \"$out\""
echo "${TAB}cp \"$src\" \"$out\""
}
install_tool() {
src=${OUT}/bin/$(basename "$1" .c)
out="$2"
dir=$(dirname "$out")
echo "${TAB}mkdir -p \"$dir\""
echo "${TAB}cp \"$src\" \"$out\""
} }
uninstall_out() { uninstall_out() {
@ -226,7 +186,6 @@ cat << EOF > Makefile
# Generated by '$0' on $(date). # Generated by '$0' on $(date).
# This file should generally not be manually edited. # This file should generally not be manually edited.
CC = ${CC}
PREFIX = ${PREFIX} PREFIX = ${PREFIX}
CFLAGS = ${CFLAGS} CFLAGS = ${CFLAGS}
LDFLAGS = ${LDFLAGS} LDFLAGS = ${LDFLAGS}
@ -253,24 +212,28 @@ ${TAB} diff -u -p - "LICENSE.txt" | \\
${TAB} patch "\$\$src" | grep -v "^Hmm"; \\ ${TAB} patch "\$\$src" | grep -v "^Hmm"; \\
${TAB}done ${TAB}done
${LIB_NAME}: ${OUT}/lib/lib${LIB_NAME}.a ${OUT}/lib/lib${LIB_NAME}.so ${LIB_NAME}: ${OUT}/lib/${LIB_NAME}.o ${OUT}/lib/lib${LIB_NAME}.a ${OUT}/lib/lib${LIB_NAME}.so
install: ${LIB_NAME} install: ${LIB_NAME}
${TAB}install -D ${OUT}/lib/${LIB_NAME}.o \$(PREFIX)/lib/${LIB_NAME}.o
${TAB}install -D ${OUT}/lib/lib${LIB_NAME}.a \$(PREFIX)/lib/lib${LIB_NAME}.a ${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}install -D ${OUT}/lib/lib${LIB_NAME}.so \$(PREFIX)/lib/lib${LIB_NAME}.so
$(collect ${INCLUDE}/ '' '' \$\(PREFIX\)/include/${LIB_NAME}/ install_out) $(collect ${INCLUDE}/ '' '' \$\(PREFIX\)/include/${LIB_NAME}/ install_out)
$(collect ${INCLUDE}/ .h .3 \$\(PREFIX\)/man/man3/${LIB_NAME}- install_man) $(collect ${INCLUDE}/ .h .3 \$\(PREFIX\)/man/man3/${LIB_NAME}- install_man)
$(collect ${TOOLS}/ '.c' '' \$\(PREFIX\)/bin/ install_tool)
uninstall: uninstall:
${TAB}rm -r \$(PREFIX)/lib/${LIB_NAME}.*
${TAB}rm -r \$(PREFIX)/lib/lib${LIB_NAME}.* ${TAB}rm -r \$(PREFIX)/lib/lib${LIB_NAME}.*
${TAB}rm -r \$(PREFIX)/include/${LIB_NAME} ${TAB}rm -r \$(PREFIX)/include/${LIB_NAME}
${TAB}rm -r \$(PREFIX)/man/man3/${LIB_NAME}-* ${TAB}rm -r \$(PREFIX)/man/man3/${LIB_NAME}-*
$(collect ${TOOLS}/ '.c' '' \$\(PREFIX\)/bin/ uninstall_out)
clean: clean:
${TAB}rm -r "${BUILD}" "${OUT}" ${TAB}rm -r "${BUILD}" "${OUT}"
${OUT}/lib/${LIB_NAME}.o: ${BUILD}/${STUB}.o
${TAB}@mkdir -p ${OUT}/lib
${TAB}cp ${BUILD}/${STUB}.o ${OUT}/lib/${LIB_NAME}.o
${OUT}/lib/lib${LIB_NAME}.a: ${OBJS} ${OUT}/lib/lib${LIB_NAME}.a: ${OBJS}
${TAB}@mkdir -p ${OUT}/lib ${TAB}@mkdir -p ${OUT}/lib
${TAB}\$(AR) rcs ${OUT}/lib/lib${LIB_NAME}.a ${OBJS} ${TAB}\$(AR) rcs ${OUT}/lib/lib${LIB_NAME}.a ${OBJS}

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files * obtaining a copy of this software and associated documentation files

View file

@ -1,61 +0,0 @@
/*
* 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_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 STRINGIFY(x) #x
/***
* @Nm Cytoplasm
* @Nd A simple API that provides metadata on the library itself.
* @Dd September 30 2022
* @Xr Sha2
*
* This API simply provides name and versioning information for the
* currently loaded library.
*/
/** */
extern int CytoplasmGetVersion(void);
/**
* Get the library version. This will be useful mostly for printing
* to log files, but it may also be used by a program to verify that
* the version is new enough.
*
* This function returns a string, which should usually be able to be
* parsed using sscanf() if absolutely necessary.
*/
extern const char * CytoplasmGetVersionStr(void);
#endif /* CYTOPLASM_CYTOPLASM_H */

View file

@ -1,70 +0,0 @@
.Dd $Mdocdate: March 12 2023 $
.Dt HTTP 1
.Os Telodendria Project
.Sh NAME
.Nm http
.Nd A simple command line utility for making HTTP requests.
.Sh SYNOPSIS
.Nm
.Op Fl i
.Op Fl X Ar method
.Op Fl H Ar header
.Op Fl d Ar data
.Op URL
.Sh DESCRIPTION
.Nm
Is a command line HTTP client. It is very heavily inspired by
.Xr curl 1 ,
and even uses the same flag names when possible. However,
.Nm
is designed to be much simpler than
.Xr curl 1 ,
and is built on Telodendria's own
.Xr HttpClient 3
API. It primarily exists to test
.Xr HttpClient 3
and
.Xr HttpServer 3 ,
and make development of Telodendria possible without having
to install any external tools.
.sp
The options are as follows:
.Bl -tag -width Ds
.It Fl i
Display the response headers before writing the body.
.It Fl X Ar method
Set the request method. This can be any of the options
allowed by the
.Xr Http 3
API; unlike
.Xr curl 1 ,
it cannot be any arbitrary string.
.It Fl H Ar header
Set a request header, in the form of ``Header: value''. This option
can be set multiple times to add multiple request headers.
.It Fl d Ar data
Send data to the server in the request body. If
.Ar data
starts with ``@'', then the file specified after is opened
and read in. If it is ``@-'', then standard input is used.
Otherwise, the string is passed to the server as-is.
.El
.Pp
.Nm
also requires a
.Ar URL
to make the request to. The URL is parsed by the
.Xr Uri 3
API, so consult that page for the syntax of URLs.
.Sh EXIT STATUS
.Nm
exits with
.Va EXIT_SUCCESS
if all command line options were valid, the request was
made successfully, and the server returns an HTTP code
that indicates success. It exits with
.Va EXIT_FAILURE
in all other scenarios.
.Sh SEE ALSO
.Xr HttpClient 3 ,
.Xr Uri 3

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files * obtaining a copy of this software and associated documentation files

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files * obtaining a copy of this software and associated documentation files
@ -38,12 +38,12 @@ struct Array
size_t size; /* Elements actually filled */ size_t size; /* Elements actually filled */
}; };
bool int
ArrayAdd(Array * array, void *value) ArrayAdd(Array * array, void *value)
{ {
if (!array) if (!array)
{ {
return false; return 0;
} }
return ArrayInsert(array, array->size, value); return ArrayInsert(array, array->size, value);
@ -122,14 +122,14 @@ ArrayGet(Array * array, size_t index)
} }
bool extern int
ArrayInsert(Array * array, size_t index, void *value) ArrayInsert(Array * array, size_t index, void *value)
{ {
size_t i; size_t i;
if (!array || !value || index > array->size) if (!array || !value || index > array->size)
{ {
return false; return 0;
} }
if (array->size >= array->allocated) if (array->size >= array->allocated)
@ -145,7 +145,7 @@ ArrayInsert(Array * array, size_t index, void *value)
if (!array->entries) if (!array->entries)
{ {
array->entries = tmp; array->entries = tmp;
return false; return 0;
} }
array->allocated = newSize; array->allocated = newSize;
@ -160,7 +160,7 @@ ArrayInsert(Array * array, size_t index, void *value)
array->entries[index] = value; array->entries[index] = value;
return true; return 1;
} }
extern void * extern void *
@ -200,14 +200,14 @@ ArraySize(Array * array)
return array->size; return array->size;
} }
bool int
ArrayTrim(Array * array) ArrayTrim(Array * array)
{ {
void **tmp; void **tmp;
if (!array) if (!array)
{ {
return false; return 0;
} }
tmp = array->entries; tmp = array->entries;
@ -218,10 +218,10 @@ ArrayTrim(Array * array)
if (!array->entries) if (!array->entries)
{ {
array->entries = tmp; array->entries = tmp;
return false; return 0;
} }
return true; return 1;
} }
static void static void
@ -267,9 +267,8 @@ ArrayQuickSort(Array * array, size_t low, size_t high, int (*compare) (void *, v
void void
ArraySort(Array * array, int (*compare) (void *, void *)) ArraySort(Array * array, int (*compare) (void *, void *))
{ {
if (!ArraySize(array)) if (!array)
{ {
// If a NULL ptr was given, or the array has no elements, do nothing.
return; return;
} }
ArrayQuickSort(array, 0, array->size - 1, compare); ArrayQuickSort(array, 0, array->size - 1, compare);

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files * obtaining a copy of this software and associated documentation files
@ -212,7 +212,7 @@ Base64Unpad(char *base64, size_t length)
base64[length] = '\0'; base64[length] = '\0';
} }
bool extern int
Base64Pad(char **base64Ptr, size_t length) Base64Pad(char **base64Ptr, size_t length)
{ {
char *tmp; char *tmp;
@ -221,7 +221,7 @@ Base64Pad(char **base64Ptr, size_t length)
if (length % 4 == 0) if (length % 4 == 0)
{ {
return true; /* Success: no padding needed */ return length; /* Success: no padding needed */
} }
newSize = length + (4 - (length % 4)); newSize = length + (4 - (length % 4));
@ -229,7 +229,7 @@ Base64Pad(char **base64Ptr, size_t length)
tmp = Realloc(*base64Ptr, newSize + 100);; tmp = Realloc(*base64Ptr, newSize + 100);;
if (!tmp) if (!tmp)
{ {
return false; /* Memory error */ return 0; /* Memory error */
} }
*base64Ptr = tmp; *base64Ptr = tmp;
@ -240,5 +240,5 @@ Base64Pad(char **base64Ptr, size_t length)
(*base64Ptr)[newSize] = '\0'; (*base64Ptr)[newSize] = '\0';
return true; return newSize;
} }

View file

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

View file

@ -1,45 +0,0 @@
/*
* 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 <Cytoplasm.h>
int
CytoplasmGetVersion(void)
{
return CYTOPLASM_VERSION;
}
const char *
CytoplasmGetVersionStr(void)
{
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
;
}

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files * obtaining a copy of this software and associated documentation files
@ -23,6 +23,7 @@
*/ */
#include <Db.h> #include <Db.h>
#include <UInt64.h>
#include <Memory.h> #include <Memory.h>
#include <Json.h> #include <Json.h>
#include <Util.h> #include <Util.h>
@ -76,7 +77,7 @@ struct DbRef
{ {
HashMap *json; HashMap *json;
uint64_t ts; UInt64 ts;
size_t size; size_t size;
Array *name; Array *name;
@ -218,38 +219,19 @@ DbHashKey(Array * args)
return str; return str;
} }
static char
DbSanitiseChar(char input)
{
switch (input)
{
case '/':
return '_';
case '.':
return '-';
}
return input;
}
static char * static char *
DbDirName(Db * db, Array * args, size_t strip) DbDirName(Db * db, Array * args, size_t strip)
{ {
size_t i, j; size_t i;
char *str = StrConcat(2, db->dir, "/"); char *str = StrConcat(2, db->dir, "/");
for (i = 0; i < ArraySize(args) - strip; i++) for (i = 0; i < ArraySize(args) - strip; i++)
{ {
char *tmp; char *tmp;
char *sanitise = StrDuplicate(ArrayGet(args, i));
for (j = 0; j < strlen(sanitise); j++)
{
sanitise[j] = DbSanitiseChar(sanitise[j]);
}
tmp = StrConcat(3, str, sanitise, "/"); tmp = StrConcat(3, str, ArrayGet(args, i), "/");
Free(str); Free(str);
Free(sanitise);
str = tmp; str = tmp;
} }
@ -272,7 +254,17 @@ DbFileName(Db * db, Array * args)
/* Sanitize name to prevent directory traversal attacks */ /* Sanitize name to prevent directory traversal attacks */
while (arg[j]) while (arg[j])
{ {
arg[j] = DbSanitiseChar(arg[j]); switch (arg[j])
{
case '/':
arg[j] = '_';
break;
case '.':
arg[j] = '-';
break;
default:
break;
}
j++; j++;
} }
@ -503,12 +495,12 @@ DbLockFromArr(Db * db, Array * args)
if (ref) /* In cache */ if (ref) /* In cache */
{ {
uint64_t diskTs = UtilLastModified(file); UInt64 diskTs = UtilLastModified(file);
ref->fd = fd; ref->fd = fd;
ref->stream = stream; ref->stream = stream;
if (diskTs > ref->ts) if (UInt64Gt(diskTs, ref->ts))
{ {
/* File was modified on disk since it was cached */ /* File was modified on disk since it was cached */
HashMap *json = JsonDecode(ref->stream); HashMap *json = JsonDecode(ref->stream);
@ -596,7 +588,7 @@ DbLockFromArr(Db * db, Array * args)
if (db->cache) if (db->cache)
{ {
ref->ts = UtilTsMillis(); ref->ts = UtilServerTs();
ref->size = DbComputeSize(ref->json); ref->size = DbComputeSize(ref->json);
HashMapSet(db->cache, hash, ref); HashMapSet(db->cache, hash, ref);
db->cacheSize += ref->size; db->cacheSize += ref->size;
@ -660,7 +652,7 @@ DbCreate(Db * db, size_t nArgs,...)
file = DbFileName(db, args); file = DbFileName(db, args);
if (UtilLastModified(file)) if (UInt64Neq(UtilLastModified(file), UInt64Create(0, 0)))
{ {
Free(file); Free(file);
ArrayFree(args); ArrayFree(args);
@ -702,19 +694,19 @@ DbCreate(Db * db, size_t nArgs,...)
return ret; return ret;
} }
bool int
DbDelete(Db * db, size_t nArgs,...) DbDelete(Db * db, size_t nArgs,...)
{ {
va_list ap; va_list ap;
Array *args; Array *args;
char *file; char *file;
char *hash; char *hash;
bool ret = true; int ret = 1;
DbRef *ref; DbRef *ref;
if (!db) if (!db)
{ {
return false; return 0;
} }
va_start(ap, nArgs); va_start(ap, nArgs);
@ -763,9 +755,9 @@ DbDelete(Db * db, size_t nArgs,...)
Free(hash); Free(hash);
if (UtilLastModified(file)) if (UInt64Neq(UtilLastModified(file), UInt64Create(0, 0)))
{ {
ret = (remove(file) == 0); ret = remove(file) == 0;
} }
pthread_mutex_unlock(&db->lock); pthread_mutex_unlock(&db->lock);
@ -798,14 +790,14 @@ DbLock(Db * db, size_t nArgs,...)
return ret; return ret;
} }
bool int
DbUnlock(Db * db, DbRef * ref) DbUnlock(Db * db, DbRef * ref)
{ {
bool destroy; int destroy;
if (!db || !ref) if (!db || !ref)
{ {
return false; return 0;
} }
lseek(ref->fd, 0L, SEEK_SET); lseek(ref->fd, 0L, SEEK_SET);
@ -814,7 +806,7 @@ DbUnlock(Db * db, DbRef * ref)
pthread_mutex_unlock(&db->lock); pthread_mutex_unlock(&db->lock);
Log(LOG_ERR, "Failed to truncate file on disk."); Log(LOG_ERR, "Failed to truncate file on disk.");
Log(LOG_ERR, "Error on fd %d: %s", ref->fd, strerror(errno)); Log(LOG_ERR, "Error on fd %d: %s", ref->fd, strerror(errno));
return false; return 0;
} }
JsonEncode(ref->json, ref->stream, JSON_DEFAULT); JsonEncode(ref->json, ref->stream, JSON_DEFAULT);
@ -835,18 +827,18 @@ DbUnlock(Db * db, DbRef * ref)
* require some items to be evicted. */ * require some items to be evicted. */
DbCacheEvict(db); DbCacheEvict(db);
destroy = false; destroy = 0;
} }
else else
{ {
destroy = true; destroy = 1;
} }
Free(key); Free(key);
} }
else else
{ {
destroy = true; destroy = 1;
} }
if (destroy) if (destroy)
@ -858,16 +850,16 @@ DbUnlock(Db * db, DbRef * ref)
} }
pthread_mutex_unlock(&db->lock); pthread_mutex_unlock(&db->lock);
return true; return 1;
} }
bool int
DbExists(Db * db, size_t nArgs,...) DbExists(Db * db, size_t nArgs,...)
{ {
va_list ap; va_list ap;
Array *args; Array *args;
char *file; char *file;
bool ret; int ret;
va_start(ap, nArgs); va_start(ap, nArgs);
args = ArrayFromVarArgs(nArgs, ap); args = ArrayFromVarArgs(nArgs, ap);
@ -875,13 +867,13 @@ DbExists(Db * db, size_t nArgs,...)
if (!args) if (!args)
{ {
return false; return 0;
} }
pthread_mutex_lock(&db->lock); pthread_mutex_lock(&db->lock);
file = DbFileName(db, args); file = DbFileName(db, args);
ret = (UtilLastModified(file) != 0); ret = UInt64Neq(UtilLastModified(file), UInt64Create(0, 0));
pthread_mutex_unlock(&db->lock); pthread_mutex_unlock(&db->lock);
@ -963,15 +955,15 @@ DbJson(DbRef * ref)
return ref ? ref->json : NULL; return ref ? ref->json : NULL;
} }
bool int
DbJsonSet(DbRef * ref, HashMap * json) DbJsonSet(DbRef * ref, HashMap * json)
{ {
if (!ref || !json) if (!ref || !json)
{ {
return false; return 0;
} }
JsonFree(ref->json); JsonFree(ref->json);
ref->json = JsonDuplicate(json); ref->json = JsonDuplicate(json);
return true; return 1;
} }

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files * obtaining a copy of this software and associated documentation files

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files * obtaining a copy of this software and associated documentation files
@ -247,12 +247,12 @@ HashMapGet(HashMap * map, const char *key)
return NULL; return NULL;
} }
bool int
HashMapIterateReentrant(HashMap * map, char **key, void **value, size_t * i) HashMapIterateReentrant(HashMap * map, char **key, void **value, size_t * i)
{ {
if (!map) if (!map)
{ {
return false; return 0;
} }
if (*i >= map->capacity) if (*i >= map->capacity)
@ -260,7 +260,7 @@ HashMapIterateReentrant(HashMap * map, char **key, void **value, size_t * i)
*i = 0; *i = 0;
*key = NULL; *key = NULL;
*value = NULL; *value = NULL;
return false; return 0;
} }
while (*i < map->capacity) while (*i < map->capacity)
@ -273,20 +273,20 @@ HashMapIterateReentrant(HashMap * map, char **key, void **value, size_t * i)
{ {
*key = bucket->key; *key = bucket->key;
*value = bucket->value; *value = bucket->value;
return true; return 1;
} }
} }
*i = 0; *i = 0;
return false; return 0;
} }
bool int
HashMapIterate(HashMap * map, char **key, void **value) HashMapIterate(HashMap * map, char **key, void **value)
{ {
if (!map) if (!map)
{ {
return false; return 0;
} }
else else
{ {

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files * obtaining a copy of this software and associated documentation files

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files * obtaining a copy of this software and associated documentation files

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files * obtaining a copy of this software and associated documentation files
@ -34,7 +34,6 @@
#include <sys/socket.h> #include <sys/socket.h>
#include <netdb.h> #include <netdb.h>
#include <Cytoplasm.h>
#include <Http.h> #include <Http.h>
#include <Memory.h> #include <Memory.h>
#include <Util.h> #include <Util.h>
@ -155,7 +154,7 @@ HttpRequest(HttpRequestMethod method, int flags, unsigned short port, char *host
HttpRequestMethodToString(method), path); HttpRequestMethodToString(method), path);
HttpRequestHeader(context, "Connection", "close"); HttpRequestHeader(context, "Connection", "close");
HttpRequestHeader(context, "User-Agent", LIB_NAME "/" STRINGIFY(CYTOPLASM_VERSION)); HttpRequestHeader(context, "User-Agent", LIB_NAME "/" LIB_VERSION);
HttpRequestHeader(context, "Host", host); HttpRequestHeader(context, "Host", host);
return context; return context;

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files * obtaining a copy of this software and associated documentation files
@ -23,7 +23,6 @@
*/ */
#include <HttpRouter.h> #include <HttpRouter.h>
#include <Http.h>
#include <Memory.h> #include <Memory.h>
#include <HashMap.h> #include <HashMap.h>
#include <Str.h> #include <Str.h>
@ -143,7 +142,7 @@ HttpRouterFree(HttpRouter * router)
Free(router); Free(router);
} }
bool int
HttpRouterAdd(HttpRouter * router, char *regPath, HttpRouteFunc * exec) HttpRouterAdd(HttpRouter * router, char *regPath, HttpRouteFunc * exec)
{ {
RouteNode *node; RouteNode *node;
@ -152,19 +151,19 @@ HttpRouterAdd(HttpRouter * router, char *regPath, HttpRouteFunc * exec)
if (!router || !regPath || !exec) if (!router || !regPath || !exec)
{ {
return false; return 0;
} }
if (StrEquals(regPath, "/")) if (StrEquals(regPath, "/"))
{ {
router->root->exec = exec; router->root->exec = exec;
return true; return 1;
} }
regPath = StrDuplicate(regPath); regPath = StrDuplicate(regPath);
if (!regPath) if (!regPath)
{ {
return false; return 0;
} }
tmp = regPath; tmp = regPath;
@ -187,10 +186,10 @@ HttpRouterAdd(HttpRouter * router, char *regPath, HttpRouteFunc * exec)
Free(regPath); Free(regPath);
return true; return 1;
} }
bool int
HttpRouterRoute(HttpRouter * router, char *path, void *args, void **ret) HttpRouterRoute(HttpRouter * router, char *path, void *args, void **ret)
{ {
RouteNode *node; RouteNode *node;
@ -199,17 +198,17 @@ HttpRouterRoute(HttpRouter * router, char *path, void *args, void **ret)
HttpRouteFunc *exec = NULL; HttpRouteFunc *exec = NULL;
Array *matches = NULL; Array *matches = NULL;
size_t i; size_t i;
bool retval; int retval;
if (!router || !path) if (!router || !path)
{ {
return false; return 0;
} }
matches = ArrayCreate(); matches = ArrayCreate();
if (!matches) if (!matches)
{ {
return false; return 0;
} }
node = router->root; node = router->root;
@ -229,8 +228,6 @@ HttpRouterRoute(HttpRouter * router, char *path, void *args, void **ret)
regmatch_t pmatch[REG_MAX_SUB]; regmatch_t pmatch[REG_MAX_SUB];
pathPart = HttpUrlDecode(pathPart);
i = 0; i = 0;
while (HashMapIterateReentrant(node->children, &key, (void **) &val, &i)) while (HashMapIterateReentrant(node->children, &key, (void **) &val, &i))
@ -246,7 +243,6 @@ HttpRouterRoute(HttpRouter * router, char *path, void *args, void **ret)
if (!val) if (!val)
{ {
exec = NULL; exec = NULL;
Free(pathPart);
break; break;
} }
@ -273,14 +269,13 @@ HttpRouterRoute(HttpRouter * router, char *path, void *args, void **ret)
ArrayAdd(matches, substr); ArrayAdd(matches, substr);
} }
} }
Free(pathPart);
} }
Free(path); Free(path);
} }
if (!exec) if (!exec)
{ {
retval = false; retval = 0;
goto finish; goto finish;
} }
@ -293,7 +288,7 @@ HttpRouterRoute(HttpRouter * router, char *path, void *args, void **ret)
exec(matches, args); exec(matches, args);
} }
retval = true; retval = 1;
finish: finish:
for (i = 0; i < ArraySize(matches); i++) for (i = 0; i < ArraySize(matches); i++)

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files * obtaining a copy of this software and associated documentation files
@ -465,7 +465,7 @@ HttpServerWorkerThread(void *args)
ssize_t i = 0; ssize_t i = 0;
HttpRequestMethod requestMethod; HttpRequestMethod requestMethod;
uint64_t firstRead; UInt64 firstRead;
fp = DequeueConnection(server); fp = DequeueConnection(server);
@ -473,7 +473,7 @@ HttpServerWorkerThread(void *args)
{ {
/* Block for 1 millisecond before continuing so we don't /* Block for 1 millisecond before continuing so we don't
* murder the CPU if the queue is empty. */ * murder the CPU if the queue is empty. */
UtilSleepMillis(1); UtilSleepMillis(UInt64Create(0, 1));
continue; continue;
} }
@ -483,25 +483,21 @@ HttpServerWorkerThread(void *args)
* happens, UtilGetLine() sets errno to EAGAIN. If we get * happens, UtilGetLine() sets errno to EAGAIN. If we get
* EAGAIN, then clear the error on the stream and try again * EAGAIN, then clear the error on the stream and try again
* after a few ms. This is typically more than enough time for * after a few ms. This is typically more than enough time for
* the client to send data. * the client to send data. */
* firstRead = UtilServerTs();
* TODO: Instead of looping, abort immediately, and place the request
* at the end of the queue.
*/
firstRead = UtilTsMillis();
while ((lineLen = UtilGetLine(&line, &lineSize, fp)) == -1 while ((lineLen = UtilGetLine(&line, &lineSize, fp)) == -1
&& errno == EAGAIN) && errno == EAGAIN)
{ {
StreamClearError(fp); StreamClearError(fp);
// If the server is stopped, or it's been a while, just /* If the server is stopped, or it's been a while, just
// give up so we aren't wasting a thread on this client. * give up so we aren't wasting a thread on this client. */
if (server->stop || (UtilTsMillis() - firstRead) > (1000 * 30)) if (server->stop || UInt64Gt(UInt64Sub(UtilServerTs(), firstRead), UInt64Create(0, 1000 * 30)))
{ {
goto finish; goto finish;
} }
UtilSleepMillis(5); UtilSleepMillis(UInt64Create(0, 5));
} }
if (lineLen == -1) if (lineLen == -1)
@ -715,25 +711,25 @@ HttpServerEventThread(void *args)
return NULL; return NULL;
} }
bool int
HttpServerStart(HttpServer * server) HttpServerStart(HttpServer * server)
{ {
if (!server) if (!server)
{ {
return false; return 0;
} }
if (server->isRunning) if (server->isRunning)
{ {
return true; return 1;
} }
if (pthread_create(&server->socketThread, NULL, HttpServerEventThread, server) != 0) if (pthread_create(&server->socketThread, NULL, HttpServerEventThread, server) != 0)
{ {
return false; return 0;
} }
return true; return 1;
} }
void void

399
src/Int64.c Normal file
View file

@ -0,0 +1,399 @@
/*
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <Int64.h>
#include <stddef.h>
#include <signal.h>
#include <Log.h>
#ifdef INT64_NATIVE
#define Int64Sign(x) ((int) (((UInt64) (x)) >> 63))
#else
#define Int64Sign(x) ((int) ((x).i[1] >> 31))
#endif
size_t
Int64Str(Int64 x, int base, char *out, size_t len)
{
static const char symbols[] = "0123456789ABCDEF";
size_t i = len - 1;
size_t j = 0;
int neg = Int64Sign(x);
Int64 base64 = Int64Create(0, base);
/* We only have symbols up to base 16 */
if (base < 2 || base > 16)
{
return 0;
}
/*
* This algorithm doesn't work on INT64_MIN.
*
* But it works on all other integers in the range, so we
* just scoot the range in by one for now. It's a hack and
* I'm not a huge fan of it, but this function is mostly
* used in Json, which shouldn't have a range this large
* anyway (Json is limited to -2^53 -> 2^53-1).
*
* Proper fixes are always welcome.
*/
if (Int64Eq(x, Int64Create(0x80000000, 0x00000000)))
{
x = Int64Add(x, Int64Create(0, 1));
}
#if 0
else if (Int64Eq(x, Int64Create(0x7FFFFFFF, 0xFFFFFFFF)))
{
x = Int64Sub(x, Int64Create(0, 1));
}
#endif
if (base != 2 && base != 8 && base != 16 && neg)
{
x = Int64Neg(x);
}
do
{
Int64 mod = Int64Rem(x, base64);
Int32 low = Int64Low(mod);
out[i] = symbols[low];
i--;
x = Int64Div(x, base64);
} while (Int64Gt(x, Int64Create(0, 0)));
if (base != 2 && base != 8 && base != 16)
{
/*
* Binary, octal, and hexadecimal are known to
* be bit representations. Everything else (notably
* decimal) should include the negative sign.
*/
if (neg)
{
out[i] = '-';
i--;
}
}
while (++i < len)
{
out[j++] = out[i];
}
out[j] = '\0';
return j;
}
#ifndef INT64_NATIVE
/* No native 64-bit support, add our own */
Int64
Int64Create(UInt32 high, UInt32 low)
{
Int64 x;
x.i[0] = low;
x.i[1] = high;
return x;
}
Int64
Int64Add(Int64 x, Int64 y)
{
Int64 z = Int64Create(0, 0);
int carry;
z.i[0] = x.i[0] + y.i[0];
carry = z.i[0] < x.i[0];
z.i[1] = x.i[1] + y.i[1] + carry;
return z;
}
Int64
Int64Sub(Int64 x, Int64 y)
{
return Int64Add(x, Int64Neg(y));
}
Int64
Int64Mul(Int64 x, Int64 y)
{
Int64 z = Int64Create(0, 0);
int xneg = Int64Sign(x);
int yneg = Int64Sign(y);
if (xneg)
{
x = Int64Neg(x);
}
if (yneg)
{
y = Int64Neg(y);
}
/* while (y > 0) */
while (Int64Gt(y, Int64Create(0, 0)))
{
/* if (y & 1 != 0) */
if (Int64Neq(Int64And(y, Int64Create(0, 1)), Int64Create(0, 0)))
{
z = Int64Add(z, x);
}
x = Int64Sll(x, 1);
y = Int64Sra(y, 1);
}
if (xneg != yneg)
{
z = Int64Neg(z);
}
return z;
}
typedef struct
{
Int64 q;
Int64 r;
} Int64Ldiv;
static Int64Ldiv
Int64LongDivision(Int64 n, Int64 d)
{
Int64Ldiv o;
int i;
int nneg = Int64Sign(n);
int dneg = Int64Sign(d);
o.q = Int64Create(0, 0);
o.r = Int64Create(0, 0);
if (Int64Eq(d, Int64Create(0, 0)))
{
raise(SIGFPE);
return o;
}
if (nneg)
{
n = Int64Neg(n);
}
if (dneg)
{
d = Int64Neg(d);
}
for (i = 63; i >= 0; i--)
{
Int64 bit = Int64And(Int64Sra(n, i), Int64Create(0, 1));
o.r = Int64Sll(o.r, 1);
o.r = Int64Or(o.r, bit);
if (Int64Geq(o.r, d))
{
o.r = Int64Sub(o.r, d);
o.q = Int64Or(o.q, Int64Sll(Int64Create(0, 1), i));
}
}
if (nneg != dneg)
{
o.r = Int64Neg(o.r);
o.q = Int64Neg(o.q);
}
return o;
}
Int64
Int64Div(Int64 x, Int64 y)
{
return Int64LongDivision(x, y).q;
}
Int64
Int64Rem(Int64 x, Int64 y)
{
return Int64LongDivision(x, y).r;
}
Int64
Int64Sll(Int64 x, int y)
{
Int64 z;
if (!y)
{
return x;
}
z = Int64Create(0, 0);
if (y < 32)
{
z.i[1] = (x.i[0] >> (32 - y)) | (x.i[1] << y);
z.i[0] = x.i[0] << y;
}
else
{
z.i[1] = x.i[0] << (y - 32);
}
return z;
}
Int64
Int64Sra(Int64 x, int y)
{
Int64 z;
int neg = Int64Sign(x);
if (!y)
{
return x;
}
z = Int64Create(0, 0);
if (y < 32)
{
z.i[0] = (x.i[1] << (32 - y)) | (x.i[0] >> y);
z.i[1] = x.i[1] >> y;
}
else
{
z.i[0] = x.i[1] >> (y - 32);
}
if (neg)
{
Int64 mask = Int64Create(0xFFFFFFFF, 0xFFFFFFFF);
z = Int64Or(Int64Sll(mask, (64 - y)), z);
}
return z;
}
Int64
Int64And(Int64 x, Int64 y)
{
return Int64Create(x.i[1] & y.i[1], x.i[0] & y.i[0]);
}
Int64
Int64Or(Int64 x, Int64 y)
{
return Int64Create(x.i[1] | y.i[1], x.i[0] | y.i[0]);
}
Int64
Int64Xor(Int64 x, Int64 y)
{
return Int64Create(x.i[1] ^ y.i[1], x.i[0] ^ y.i[0]);
}
Int64
Int64Not(Int64 x)
{
return Int64Create(~(x.i[1]), ~(x.i[0]));
}
int
Int64Eq(Int64 x, Int64 y)
{
return x.i[0] == y.i[0] && x.i[1] == y.i[1];
}
int
Int64Lt(Int64 x, Int64 y)
{
int xneg = Int64Sign(x);
int yneg = Int64Sign(y);
if (xneg != yneg)
{
return xneg > yneg;
}
else
{
if (xneg)
{
/* Both negative */
return x.i[1] > y.i[1] || (x.i[1] == y.i[1] && x.i[0] > y.i[0]);
}
else
{
/* Both positive */
return x.i[1] < y.i[1] || (x.i[1] == y.i[1] && x.i[0] < y.i[0]);
}
}
}
int
Int64Gt(Int64 x, Int64 y)
{
int xneg = Int64Sign(x);
int yneg = Int64Sign(y);
if (xneg != yneg)
{
return xneg < yneg;
}
else
{
if (xneg)
{
/* Both negative */
return x.i[1] < y.i[1] || (x.i[1] == y.i[1] && x.i[0] < y.i[0]);
}
else
{
/* Both positive */
return x.i[1] > y.i[1] || (x.i[1] == y.i[1] && x.i[0] > y.i[0]);
}
}
}
#endif

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files * obtaining a copy of this software and associated documentation files

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files * obtaining a copy of this software and associated documentation files

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files * obtaining a copy of this software and associated documentation files

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files * obtaining a copy of this software and associated documentation files
@ -26,15 +26,14 @@
#include <Memory.h> #include <Memory.h>
#include <Str.h> #include <Str.h>
#include <Util.h> #include <Util.h>
#include <Int.h>
#include <Int64.h>
#include <stdio.h> #include <stdio.h>
#include <stddef.h> #include <stddef.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <ctype.h> #include <ctype.h>
#include <stdbool.h>
#include <inttypes.h>
#include <errno.h> #include <errno.h>
struct JsonValue struct JsonValue
@ -45,9 +44,9 @@ struct JsonValue
HashMap *object; HashMap *object;
Array *array; Array *array;
char *string; char *string;
uint64_t integer; Int64 integer;
double floating; double floating;
bool boolean; int boolean:1;
} as; } as;
}; };
@ -202,7 +201,7 @@ JsonValueAsString(JsonValue * value)
} }
JsonValue * JsonValue *
JsonValueInteger(uint64_t integer) JsonValueInteger(Int64 integer)
{ {
JsonValue *value; JsonValue *value;
@ -218,12 +217,12 @@ JsonValueInteger(uint64_t integer)
return value; return value;
} }
uint64_t Int64
JsonValueAsInteger(JsonValue * value) JsonValueAsInteger(JsonValue * value)
{ {
if (!value || value->type != JSON_INTEGER) if (!value || value->type != JSON_INTEGER)
{ {
return 0; return Int64Create(0, 0);
} }
return value->as.integer; return value->as.integer;
@ -259,7 +258,7 @@ JsonValueAsFloat(JsonValue * value)
} }
JsonValue * JsonValue *
JsonValueBoolean(bool boolean) JsonValueBoolean(int boolean)
{ {
JsonValue *value; JsonValue *value;
@ -275,12 +274,12 @@ JsonValueBoolean(bool boolean)
return value; return value;
} }
bool int
JsonValueAsBoolean(JsonValue * value) JsonValueAsBoolean(JsonValue * value)
{ {
if (!value || value->type != JSON_BOOLEAN) if (!value || value->type != JSON_BOOLEAN)
{ {
return false; return 0;
} }
return value->as.boolean; return value->as.boolean;
@ -336,12 +335,12 @@ JsonValueFree(JsonValue * value)
Free(value); Free(value);
} }
size_t int
JsonEncodeString(const char *str, Stream * out) JsonEncodeString(const char *str, Stream * out)
{ {
size_t i; size_t i;
char c; char c;
size_t length = 0; int length = 0;
StreamPutc(out, '"'); StreamPutc(out, '"');
length++; length++;
@ -404,9 +403,9 @@ JsonDecodeString(Stream * in)
int c; int c;
char a[5]; char a[5];
uint32_t codepoint; UInt32 codepoint;
uint16_t high; UInt16 high;
uint16_t low; UInt16 low;
char *utf8Ptr; char *utf8Ptr;
@ -599,13 +598,15 @@ JsonDecodeString(Stream * in)
return NULL; return NULL;
} }
size_t int
JsonEncodeValue(JsonValue * value, Stream * out, int level) JsonEncodeValue(JsonValue * value, Stream * out, int level)
{ {
size_t i; size_t i;
size_t len; size_t len;
Array *arr; Array *arr;
size_t length = 0; int length = 0;
char ibuf[INT64_STRBUF];
switch (value->type) switch (value->type)
{ {
@ -643,7 +644,8 @@ JsonEncodeValue(JsonValue * value, Stream * out, int level)
length += JsonEncodeString(value->as.string, out); length += JsonEncodeString(value->as.string, out);
break; break;
case JSON_INTEGER: case JSON_INTEGER:
length += StreamPrintf(out, "%" PRId64, value->as.integer); Int64Str(value->as.integer, 10, ibuf, INT64_STRBUF);
length += StreamPrintf(out, "%s", ibuf);
break; break;
case JSON_FLOAT: case JSON_FLOAT:
length += StreamPrintf(out, "%f", value->as.floating); length += StreamPrintf(out, "%f", value->as.floating);
@ -671,14 +673,14 @@ JsonEncodeValue(JsonValue * value, Stream * out, int level)
return length; return length;
} }
size_t int
JsonEncode(HashMap * object, Stream * out, int level) JsonEncode(HashMap * object, Stream * out, int level)
{ {
size_t index; size_t index;
size_t count; size_t count;
char *key; char *key;
JsonValue *value; JsonValue *value;
size_t length; int length;
if (!object) if (!object)
{ {
@ -861,7 +863,6 @@ JsonConsumeWhitespace(JsonParserState * state)
break; break;
} }
// TODO: This logic should be moved into Stream as a sync function.
if (StreamError(state->stream)) if (StreamError(state->stream))
{ {
if (errno == EAGAIN) if (errno == EAGAIN)
@ -875,7 +876,7 @@ JsonConsumeWhitespace(JsonParserState * state)
} }
else else
{ {
UtilSleepMillis(delay); UtilSleepMillis(UInt64Create(0, delay));
continue; continue;
} }
} }
@ -1122,7 +1123,7 @@ JsonDecodeValue(JsonParserState * state)
JsonValue *value; JsonValue *value;
char *strValue; char *strValue;
int64_t iValue; Int64 iValue;
size_t i; size_t i;
int neg; int neg;
@ -1145,7 +1146,7 @@ JsonDecodeValue(JsonParserState * state)
Free(strValue); Free(strValue);
break; break;
case TOKEN_INTEGER: case TOKEN_INTEGER:
iValue = 0; iValue = Int64Create(0, 0);
i = 0; i = 0;
neg = 0; neg = 0;
@ -1161,14 +1162,14 @@ JsonDecodeValue(JsonParserState * state)
} }
d = state->token[i] - '0'; d = state->token[i] - '0';
iValue *= 10; iValue = Int64Mul(iValue, Int64Create(0, 10));
iValue += d; iValue = Int64Add(iValue, Int64Create(0, d));
i++; i++;
} }
if (neg) if (neg)
{ {
iValue *= -1; iValue = Int64Neg(iValue);
} }
value = JsonValueInteger(iValue); value = JsonValueInteger(iValue);
break; break;
@ -1432,32 +1433,3 @@ finish:
va_end(argp); va_end(argp);
return val; return val;
} }
void
JsonMerge(HashMap *obj1, HashMap *obj2)
{
char *key;
JsonValue *val2;
while (HashMapIterate(obj2, &key, (void **) &val2))
{
JsonValue *val1 = HashMapGet(obj1, key);
if (val1)
{
if (JsonValueType(val1) == JsonValueType(val2) &&
JsonValueType(val1) == JSON_OBJECT)
{
JsonMerge(JsonValueAsObject(val1), JsonValueAsObject(val2));
}
else
{
JsonValueFree(HashMapSet(obj1, key, JsonValueDuplicate(val2)));
}
}
else
{
HashMapSet(obj1, key, JsonValueDuplicate(val2));
}
}
}

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files * obtaining a copy of this software and associated documentation files

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files * obtaining a copy of this software and associated documentation files
@ -32,6 +32,8 @@
#include <unistd.h> #include <unistd.h>
#include <pthread.h> #include <pthread.h>
#include <Int.h>
#ifndef MEMORY_TABLE_CHUNK #ifndef MEMORY_TABLE_CHUNK
#define MEMORY_TABLE_CHUNK 256 #define MEMORY_TABLE_CHUNK 256
#endif #endif
@ -48,12 +50,12 @@ struct MemoryInfo
void *pointer; void *pointer;
}; };
#define MEM_BOUND_TYPE uint32_t #define MEM_BOUND_TYPE UInt32
#define MEM_BOUND 0xDEADBEEF #define MEM_BOUND 0xDEADBEEF
#define MEM_BOUND_LOWER(p) *((MEM_BOUND_TYPE *) p) #define MEM_BOUND_LOWER(p) *((MEM_BOUND_TYPE *) p)
#define MEM_BOUND_UPPER(p, x) *(((MEM_BOUND_TYPE *) (((uint8_t *) p) + x)) + 1) #define MEM_BOUND_UPPER(p, x) *(((MEM_BOUND_TYPE *) (((UInt8 *) p) + x)) + 1)
#define MEM_SIZE_ACTUAL(x) (((x) * sizeof(uint8_t)) + (2 * sizeof(MEM_BOUND_TYPE))) #define MEM_SIZE_ACTUAL(x) (((x) * sizeof(UInt8)) + (2 * sizeof(MEM_BOUND_TYPE)))
static pthread_mutex_t lock; static pthread_mutex_t lock;
static void (*hook) (MemoryAction, MemoryInfo *, void *) = MemoryDefaultHook; static void (*hook) (MemoryAction, MemoryInfo *, void *) = MemoryDefaultHook;

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files * obtaining a copy of this software and associated documentation files
@ -75,39 +75,39 @@ QueueFree(Queue * q)
Free(q); Free(q);
} }
bool int
QueueFull(Queue * q) QueueFull(Queue * q)
{ {
if (!q) if (!q)
{ {
return false; return 0;
} }
return ((q->front == q->rear + 1) || (q->front == 0 && q->rear == q->size - 1)); return ((q->front == q->rear + 1) || (q->front == 0 && q->rear == q->size - 1));
} }
bool int
QueueEmpty(Queue * q) QueueEmpty(Queue * q)
{ {
if (!q) if (!q)
{ {
return false; return 0;
} }
return (q->front == (q->size + 1)); return q->front == q->size + 1;
} }
bool int
QueuePush(Queue * q, void *element) QueuePush(Queue * q, void *element)
{ {
if (!q || !element) if (!q || !element)
{ {
return false; return 0;
} }
if (QueueFull(q)) if (QueueFull(q))
{ {
return false; return 0;
} }
if (q->front == q->size + 1) if (q->front == q->size + 1)
@ -126,7 +126,7 @@ QueuePush(Queue * q, void *element)
q->items[q->rear] = element; q->items[q->rear] = element;
return true; return 1;
} }
void * void *

View file

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

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files * obtaining a copy of this software and associated documentation files

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files * 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) if (hexBuf && asciiBuf)
{ {
fprintf(report, "%04zx: %s | %s |\n", off, hexBuf, asciiBuf); fprintf(report, "%04lx: %s | %s |\n", off, hexBuf, asciiBuf);
} }
else else
{ {
fprintf(report, "%04zx\n", off); fprintf(report, "%04lx\n", off);
} }
} }
@ -52,7 +52,7 @@ MemoryIterator(MemoryInfo * i, void *args)
{ {
FILE *report = args; FILE *report = args;
fprintf(report, "%s:%d: %zu bytes at %p\n", fprintf(report, "%s:%d: %lu bytes at %p\n",
MemoryInfoGetFile(i), MemoryInfoGetLine(i), MemoryInfoGetFile(i), MemoryInfoGetLine(i),
MemoryInfoGetSize(i), MemoryInfoGetPointer(i)); MemoryInfoGetSize(i), MemoryInfoGetPointer(i));
@ -107,7 +107,7 @@ GenerateMemoryReport(int argc, char **argv)
fprintf(report, " '%s'", argv[i]); fprintf(report, " '%s'", argv[i]);
} }
fprintf(report, "\nDate: %s\n", tsBuffer); fprintf(report, "\nDate: %s\n", tsBuffer);
fprintf(report, "Total Bytes: %zu\n", MemoryAllocated()); fprintf(report, "Total Bytes: %lu\n", MemoryAllocated());
fprintf(report, "\n"); fprintf(report, "\n");
MemoryIterate(MemoryIterator, report); MemoryIterate(MemoryIterator, report);

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files * obtaining a copy of this software and associated documentation files

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files * obtaining a copy of this software and associated documentation files
@ -23,6 +23,7 @@
*/ */
#include <Sha.h> #include <Sha.h>
#include <Memory.h> #include <Memory.h>
#include <Int.h>
#include <string.h> #include <string.h>
@ -30,10 +31,10 @@
#define LOAD32H(x, y) \ #define LOAD32H(x, y) \
{ \ { \
x = ((uint32_t)((y)[0] & 255) << 24) | \ x = ((UInt32)((y)[0] & 255) << 24) | \
((uint32_t)((y)[1] & 255) << 16) | \ ((UInt32)((y)[1] & 255) << 16) | \
((uint32_t)((y)[2] & 255) << 8) | \ ((UInt32)((y)[2] & 255) << 8) | \
((uint32_t)((y)[3] & 255)); \ ((UInt32)((y)[3] & 255)); \
} }
#define ROL(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) #define ROL(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
@ -48,22 +49,22 @@
typedef union typedef union
{ {
uint8_t c[64]; UInt8 c[64];
uint32_t l[16]; UInt32 l[16];
} Char64Long16; } Char64Long16;
typedef struct typedef struct
{ {
uint32_t state[5]; UInt32 state[5];
uint32_t count[2]; UInt32 count[2];
uint8_t buffer[64]; UInt8 buffer[64];
} Sha1Context; } Sha1Context;
static void static void
Sha1Transform(uint32_t state[5], const uint8_t *buffer) Sha1Transform(UInt32 state[5], const UInt8 buffer[64])
{ {
uint32_t a, b, c, d, e, i; UInt32 a, b, c, d, e, i;
uint8_t workspace[64]; UInt8 workspace[64];
Char64Long16 *block = (Char64Long16 *) workspace; Char64Long16 *block = (Char64Long16 *) workspace;
for (i = 0; i < 16; i++) for (i = 0; i < 16; i++)
@ -179,9 +180,9 @@ Sha1Init(Sha1Context * ctx)
} }
static void static void
Sha1Update(Sha1Context * ctx, const void *buf, uint32_t size) Sha1Update(Sha1Context * ctx, const void *buf, UInt32 size)
{ {
uint32_t i, j; UInt32 i, j;
j = (ctx->count[0] >> 3) & 63; j = (ctx->count[0] >> 3) & 63;
@ -201,7 +202,7 @@ Sha1Update(Sha1Context * ctx, const void *buf, uint32_t size)
for (; i + 63 < size; i += 64) for (; i + 63 < size; i += 64)
{ {
Sha1Transform(ctx->state, (uint8_t *) buf + i); Sha1Transform(ctx->state, (UInt8 *) buf + i);
} }
j = 0; j = 0;
@ -211,14 +212,14 @@ Sha1Update(Sha1Context * ctx, const void *buf, uint32_t size)
i = 0; i = 0;
} }
memcpy(&ctx->buffer[j], &((uint8_t *) buf)[i], size - i); memcpy(&ctx->buffer[j], &((UInt8 *) buf)[i], size - i);
} }
static void static void
Sha1Calculate(Sha1Context * ctx, unsigned char *out) Sha1Calculate(Sha1Context * ctx, unsigned char *out)
{ {
uint32_t i; UInt32 i;
uint8_t count[8]; UInt8 count[8];
for (i = 0; i < 8; i++) for (i = 0; i < 8; i++)
{ {
@ -226,16 +227,16 @@ Sha1Calculate(Sha1Context * ctx, unsigned char *out)
>> ((3 - (i & 3)) * 8)) & 255); >> ((3 - (i & 3)) * 8)) & 255);
} }
Sha1Update(ctx, (uint8_t *) "\x80", 1); Sha1Update(ctx, (UInt8 *) "\x80", 1);
while ((ctx->count[0] & 504) != 448) while ((ctx->count[0] & 504) != 448)
{ {
Sha1Update(ctx, (uint8_t *) "\0", 1); Sha1Update(ctx, (UInt8 *) "\0", 1);
} }
Sha1Update(ctx, count, 8); Sha1Update(ctx, count, 8);
for (i = 0; i < (160 / 8); i++) for (i = 0; i < (160 / 8); i++)
{ {
out[i] = (uint8_t) ((ctx->state[i >> 2] >> ((3 - (i & 3)) * 8)) & 255); out[i] = (UInt8) ((ctx->state[i >> 2] >> ((3 - (i & 3)) * 8)) & 255);
} }
} }

View file

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

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files * obtaining a copy of this software and associated documentation files
@ -26,6 +26,7 @@
#include <Memory.h> #include <Memory.h>
#include <Util.h> #include <Util.h>
#include <Rand.h> #include <Rand.h>
#include <Int.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -34,8 +35,8 @@
#include <pthread.h> #include <pthread.h>
#include <unistd.h> #include <unistd.h>
uint32_t UInt32
StrUtf16Decode(uint16_t high, uint16_t low) StrUtf16Decode(UInt16 high, UInt16 low)
{ {
if (high <= 0xD7FF) if (high <= 0xD7FF)
{ {
@ -55,7 +56,7 @@ StrUtf16Decode(uint16_t high, uint16_t low)
} }
char * char *
StrUtf8Encode(uint32_t codepoint) StrUtf8Encode(UInt32 codepoint)
{ {
char *str; char *str;
@ -219,10 +220,10 @@ StrConcat(size_t nStr,...)
return str; return str;
} }
bool int
StrBlank(const char *str) StrBlank(const char *str)
{ {
bool blank = true; int blank = 1;
size_t i = 0; size_t i = 0;
while (str[i]) while (str[i])
@ -244,7 +245,7 @@ StrRandom(size_t len)
static const char charset[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; static const char charset[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
char *str; char *str;
uint32_t *nums; int *nums;
size_t i; size_t i;
if (!len) if (!len)
@ -259,7 +260,7 @@ StrRandom(size_t len)
return NULL; return NULL;
} }
nums = Malloc(len * sizeof(uint32_t)); nums = Malloc(len * sizeof(int));
if (!nums) if (!nums)
{ {
Free(str); Free(str);
@ -322,21 +323,21 @@ StrLower(char *str)
return ret; return ret;
} }
bool int
StrEquals(const char *str1, const char *str2) StrEquals(const char *str1, const char *str2)
{ {
/* Both strings are NULL, they're equal */ /* Both strings are NULL, they're equal */
if (!str1 && !str2) if (!str1 && !str2)
{ {
return true; return 1;
} }
/* One or the other is NULL, they're not equal */ /* One or the other is NULL, they're not equal */
if (!str1 || !str2) if (!str1 || !str2)
{ {
return false; return 0;
} }
/* Neither are NULL, do a regular string comparison */ /* Neither are NULL, do a regular string comparison */
return (strcmp(str1, str2) == 0); return strcmp(str1, str2) == 0;
} }

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files * obtaining a copy of this software and associated documentation files
@ -26,6 +26,7 @@
#include <Io.h> #include <Io.h>
#include <Memory.h> #include <Memory.h>
#include <Util.h> #include <Util.h>
#include <Int.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@ -49,11 +50,11 @@ struct Stream
{ {
Io *io; Io *io;
uint8_t *rBuf; UInt8 *rBuf;
size_t rLen; size_t rLen;
size_t rOff; size_t rOff;
uint8_t *wBuf; UInt8 *wBuf;
size_t wLen; size_t wLen;
char *ugBuf; char *ugBuf;
@ -549,13 +550,13 @@ StreamSeek(Stream * stream, off_t offset, int whence)
return result; return result;
} }
bool int
StreamEof(Stream * stream) StreamEof(Stream * stream)
{ {
return stream && (stream->flags & STREAM_EOF); return stream && (stream->flags & STREAM_EOF);
} }
bool int
StreamError(Stream * stream) StreamError(Stream * stream)
{ {
return stream && (stream->flags & STREAM_ERR); return stream && (stream->flags & STREAM_ERR);
@ -625,7 +626,7 @@ StreamCopy(Stream * in, Stream * out)
} }
else else
{ {
UtilSleepMillis(STREAM_DELAY); UtilSleepMillis(UInt64Create(0, STREAM_DELAY));
continue; continue;
} }
} }

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files * obtaining a copy of this software and associated documentation files

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files * obtaining a copy of this software and associated documentation files

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files * obtaining a copy of this software and associated documentation files
@ -89,7 +89,6 @@ TlsInitClient(int fd, const char *serverName)
cookie->method = TLS_client_method(); cookie->method = TLS_client_method();
cookie->ctx = SSL_CTX_new(cookie->method); cookie->ctx = SSL_CTX_new(cookie->method);
coolie->fd = fd;
if (!cookie->ctx) if (!cookie->ctx)
{ {
goto error; goto error;
@ -296,7 +295,9 @@ TlsClose(void *cookie)
SSL_free(ssl->ssl); SSL_free(ssl->ssl);
SSL_CTX_free(ssl->ctx); SSL_CTX_free(ssl->ctx);
#if 0
close(ssl->fd); close(ssl->fd);
#endif
Free(ssl); Free(ssl);

265
src/UInt64.c Normal file
View file

@ -0,0 +1,265 @@
/*
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <UInt64.h>
#include <stddef.h>
#include <signal.h>
size_t
UInt64Str(UInt64 x, int base, char *out, size_t len)
{
static const char symbols[] = "0123456789ABCDEF";
size_t i = len - 1;
size_t j = 0;
UInt64 base64 = UInt64Create(0, base);
/* We only have symbols up to base 16 */
if (base < 2 || base > 16)
{
return 0;
}
do
{
UInt64 mod = UInt64Rem(x, base64);
UInt32 low = UInt64Low(mod);
out[i] = symbols[low];
i--;
x = UInt64Div(x, base64);
} while (UInt64Gt(x, UInt64Create(0, 0)));
while (++i < len)
{
out[j++] = out[i];
}
out[j] = '\0';
return j;
}
#ifndef UINT64_NATIVE
/* No native 64-bit support, add our own */
UInt64
UInt64Create(UInt32 high, UInt32 low)
{
UInt64 x;
x.i[0] = low;
x.i[1] = high;
return x;
}
UInt64
UInt64Add(UInt64 x, UInt64 y)
{
UInt64 z = UInt64Create(0, 0);
int carry;
z.i[0] = x.i[0] + y.i[0];
carry = z.i[0] < x.i[0];
z.i[1] = x.i[1] + y.i[1] + carry;
return z;
}
UInt64
UInt64Sub(UInt64 x, UInt64 y)
{
UInt64 twosCompl = UInt64Add(UInt64Not(y), UInt64Create(0, 1));
return UInt64Add(x, twosCompl);
}
UInt64
UInt64Mul(UInt64 x, UInt64 y)
{
UInt64 z = UInt64Create(0, 0);
/* while (y > 0) */
while (UInt64Gt(y, UInt64Create(0, 0)))
{
/* if (y & 1 != 0) */
if (UInt64Neq(UInt64And(y, UInt64Create(0, 1)), UInt64Create(0, 0)))
{
z = UInt64Add(z, x);
}
x = UInt64Sll(x, 1);
y = UInt64Srl(y, 1);
}
return z;
}
typedef struct
{
UInt64 q;
UInt64 r;
} UInt64Ldiv;
static UInt64Ldiv
UInt64LongDivision(UInt64 n, UInt64 d)
{
UInt64Ldiv o;
int i;
o.q = UInt64Create(0, 0);
o.r = UInt64Create(0, 0);
if (UInt64Eq(d, UInt64Create(0, 0)))
{
raise(SIGFPE);
return o;
}
for (i = 63; i >= 0; i--)
{
UInt64 bit = UInt64And(UInt64Srl(n, i), UInt64Create(0, 1));
o.r = UInt64Sll(o.r, 1);
o.r = UInt64Or(o.r, bit);
if (UInt64Geq(o.r, d))
{
o.r = UInt64Sub(o.r, d);
o.q = UInt64Or(o.q, UInt64Sll(UInt64Create(0, 1), i));
}
}
return o;
}
UInt64
UInt64Div(UInt64 x, UInt64 y)
{
return UInt64LongDivision(x, y).q;
}
UInt64
UInt64Rem(UInt64 x, UInt64 y)
{
return UInt64LongDivision(x, y).r;
}
UInt64
UInt64Sll(UInt64 x, int y)
{
UInt64 z;
if (!y)
{
return x;
}
z = UInt64Create(0, 0);
if (y < 32)
{
z.i[1] = (x.i[0] >> (32 - y)) | (x.i[1] << y);
z.i[0] = x.i[0] << y;
}
else
{
z.i[1] = x.i[0] << (y - 32);
}
return z;
}
UInt64
UInt64Srl(UInt64 x, int y)
{
UInt64 z;
if (!y)
{
return x;
}
z = UInt64Create(0, 0);
if (y < 32)
{
z.i[0] = (x.i[1] << (32 - y)) | (x.i[0] >> y);
z.i[1] = x.i[1] >> y;
}
else
{
z.i[0] = x.i[1] >> (y - 32);
}
return z;
}
UInt64
UInt64And(UInt64 x, UInt64 y)
{
return UInt64Create(x.i[1] & y.i[1], x.i[0] & y.i[0]);
}
UInt64
UInt64Or(UInt64 x, UInt64 y)
{
return UInt64Create(x.i[1] | y.i[1], x.i[0] | y.i[0]);
}
UInt64
UInt64Xor(UInt64 x, UInt64 y)
{
return UInt64Create(x.i[1] ^ y.i[1], x.i[0] ^ y.i[0]);
}
UInt64
UInt64Not(UInt64 x)
{
return UInt64Create(~(x.i[1]), ~(x.i[0]));
}
int
UInt64Eq(UInt64 x, UInt64 y)
{
return x.i[0] == y.i[0] && x.i[1] == y.i[1];
}
int
UInt64Lt(UInt64 x, UInt64 y)
{
return x.i[1] < y.i[1] || (x.i[1] == y.i[1] && x.i[0] < y.i[0]);
}
int
UInt64Gt(UInt64 x, UInt64 y)
{
return x.i[1] > y.i[1] || (x.i[1] == y.i[1] && x.i[0] > y.i[0]);
}
#endif

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files * obtaining a copy of this software and associated documentation files

View file

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

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files * obtaining a copy of this software and associated documentation files
@ -38,7 +38,7 @@
* provide the standard command line interface. * provide the standard command line interface.
*/ */
#include "Array.h" #include <Array.h>
/** /**
* All state is stored in this structure, instead of global * All state is stored in this structure, instead of global

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files * obtaining a copy of this software and associated documentation files
@ -52,7 +52,6 @@
#include <stddef.h> #include <stddef.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdbool.h>
/** /**
* The functions in this API operate on an array structure which is * The functions in this API operate on an array structure which is
@ -99,7 +98,7 @@ extern void *ArrayGet(Array *, size_t);
* This function returns a boolean value indicating whether or not it * This function returns a boolean value indicating whether or not it
* suceeded. * suceeded.
*/ */
extern bool ArrayInsert(Array *, size_t, void *); extern int ArrayInsert(Array *, size_t, void *);
/** /**
* Set the value at the specified index in the specified array to the * Set the value at the specified index in the specified array to the
@ -116,7 +115,7 @@ extern void *ArraySet(Array *, size_t, void *);
* return value as * return value as
* .Fn ArrayInsert . * .Fn ArrayInsert .
*/ */
extern bool ArrayAdd(Array *, void *); extern int ArrayAdd(Array *, void *);
/** /**
* Remove the element at the specified index from the specified array. * Remove the element at the specified index from the specified array.
@ -147,7 +146,7 @@ extern void ArraySort(Array *, int (*) (void *, void *));
* .P * .P
* This is a relatively expensive operation. The array must first be * This is a relatively expensive operation. The array must first be
* duplicated. Then it is sorted, then it is iterated from beginning * duplicated. Then it is sorted, then it is iterated from beginning
* to end to remove duplicate entries. Note that the comparison * to end to remove duplicate entires. Note that the comparison
* function is executed on each element at least twice. * function is executed on each element at least twice.
*/ */
extern Array *ArrayUnique(Array *, int (*) (void *, void *)); extern Array *ArrayUnique(Array *, int (*) (void *, void *));
@ -168,7 +167,7 @@ extern Array *ArrayReverse(Array *);
* array. This function is intended to be used by functions that return * array. This function is intended to be used by functions that return
* relatively read-only arrays that will be long-lived. * relatively read-only arrays that will be long-lived.
*/ */
extern bool ArrayTrim(Array *); extern int ArrayTrim(Array *);
/** /**
* Convert a variadic arguments list into an Array. In most cases, the * Convert a variadic arguments list into an Array. In most cases, the

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files * obtaining a copy of this software and associated documentation files
@ -39,7 +39,6 @@
*/ */
#include <stddef.h> #include <stddef.h>
#include <stdbool.h>
/** /**
* This function computes the amount of bytes needed to store a message * This function computes the amount of bytes needed to store a message
@ -94,7 +93,7 @@ extern void
* this means it will only fail if a bigger string is necessary, but it * this means it will only fail if a bigger string is necessary, but it
* could not be automatically allocated on the heap. * could not be automatically allocated on the heap.
*/ */
extern bool extern int
Base64Pad(char **, size_t); Base64Pad(char **, size_t);
#endif /* CYTOPLASM_BASE64_H */ #endif /* CYTOPLASM_BASE64_H */

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files * obtaining a copy of this software and associated documentation files
@ -24,8 +24,6 @@
#ifndef CYTOPLASM_CRON_H #ifndef CYTOPLASM_CRON_H
#define CYTOPLASM_CRON_H #define CYTOPLASM_CRON_H
#include <stdint.h>
/*** /***
* @Nm Cron * @Nm Cron
* @Nd Basic periodic job scheduler. * @Nd Basic periodic job scheduler.
@ -58,6 +56,8 @@
* by any means. * by any means.
*/ */
#include <Int.h>
/** /**
* All functions defined here operate on a structure opaque to the * All functions defined here operate on a structure opaque to the
* caller. * caller.
@ -82,7 +82,7 @@ typedef void (JobFunc) (void *);
* .Pp * .Pp
* This function takes the tick interval in milliseconds. * This function takes the tick interval in milliseconds.
*/ */
extern Cron * CronCreate(uint64_t); extern Cron * CronCreate(UInt32);
/** /**
* Schedule a one-off job to be executed only at the next tick, and * Schedule a one-off job to be executed only at the next tick, and
@ -110,7 +110,7 @@ extern void
* and a pointer to pass to that function when it is executed. * and a pointer to pass to that function when it is executed.
*/ */
extern void extern void
CronEvery(Cron *, uint64_t, JobFunc *, void *); CronEvery(Cron *, unsigned long, JobFunc *, void *);
/** /**
* Start ticking the clock and executing registered jobs. * Start ticking the clock and executing registered jobs.

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files * obtaining a copy of this software and associated documentation files
@ -37,10 +37,9 @@
*/ */
#include <stddef.h> #include <stddef.h>
#include <stdbool.h>
#include "HashMap.h" #include <HashMap.h>
#include "Array.h" #include <Array.h>
/** /**
* All functions in this API operate on a database structure that is * All functions in this API operate on a database structure that is
@ -114,7 +113,7 @@ extern DbRef * DbLock(Db *, size_t,...);
* This function assumes the object is not locked, otherwise undefined * This function assumes the object is not locked, otherwise undefined
* behavior will result. * behavior will result.
*/ */
extern bool DbDelete(Db *, size_t,...); extern int DbDelete(Db *, size_t,...);
/** /**
* Unlock an object and return it back to the database. This function * Unlock an object and return it back to the database. This function
@ -122,7 +121,7 @@ extern bool DbDelete(Db *, size_t,...);
* read cache; writes are always immediate to ensure data integrity in * read cache; writes are always immediate to ensure data integrity in
* the event of a system failure. * the event of a system failure.
*/ */
extern bool DbUnlock(Db *, DbRef *); extern int DbUnlock(Db *, DbRef *);
/** /**
* Check the existence of the given database object in a more efficient * Check the existence of the given database object in a more efficient
@ -131,7 +130,7 @@ extern bool DbUnlock(Db *, DbRef *);
* This function does not lock the object, nor does it load it into * This function does not lock the object, nor does it load it into
* memory if it exists. * memory if it exists.
*/ */
extern bool DbExists(Db *, size_t,...); extern int DbExists(Db *, size_t,...);
/** /**
* List all of the objects at a given path. Unlike the other varargs * List all of the objects at a given path. Unlike the other varargs
@ -165,6 +164,6 @@ extern HashMap * DbJson(DbRef *);
* replace it with new JSON. This is more efficient than duplicating * replace it with new JSON. This is more efficient than duplicating
* a separate object into the database reference. * a separate object into the database reference.
*/ */
extern bool DbJsonSet(DbRef *, HashMap *); extern int DbJsonSet(DbRef *, HashMap *);
#endif #endif

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files * obtaining a copy of this software and associated documentation files

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files * obtaining a copy of this software and associated documentation files
@ -46,7 +46,7 @@
#include <stddef.h> #include <stddef.h>
#include "Array.h" #include <Array.h>
/** /**
* These functions operate on an opaque structure, which the caller * These functions operate on an opaque structure, which the caller
@ -150,7 +150,7 @@ extern void * HashMapDelete(HashMap *, const char *);
* insertions or deletions occur during the iteration. This * insertions or deletions occur during the iteration. This
* functionality has not been tested, and will likely not work. * functionality has not been tested, and will likely not work.
*/ */
extern bool HashMapIterate(HashMap *, char **, void **); extern int HashMapIterate(HashMap *, char **, void **);
/** /**
* A reentrant version of * A reentrant version of
@ -163,7 +163,7 @@ extern bool HashMapIterate(HashMap *, char **, void **);
* .Pp * .Pp
* The cursor should be initialized to 0 at the start of iteration. * The cursor should be initialized to 0 at the start of iteration.
*/ */
extern bool extern int
HashMapIterateReentrant(HashMap *, char **, void **, size_t *); HashMapIterateReentrant(HashMap *, char **, void **, size_t *);
/** /**

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files * obtaining a copy of this software and associated documentation files
@ -41,8 +41,8 @@
* for example usage of this parser. * for example usage of this parser.
*/ */
#include "Stream.h" #include <Stream.h>
#include "Array.h" #include <Array.h>
#define HEADER_EXPR_MAX 4096 #define HEADER_EXPR_MAX 4096

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files * obtaining a copy of this software and associated documentation files
@ -42,8 +42,8 @@
#include <stdio.h> #include <stdio.h>
#include "HashMap.h" #include <HashMap.h>
#include "Stream.h" #include <Stream.h>
#define HTTP_FLAG_NONE 0 #define HTTP_FLAG_NONE 0
#define HTTP_FLAG_TLS (1 << 0) #define HTTP_FLAG_TLS (1 << 0)

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files * obtaining a copy of this software and associated documentation files
@ -39,8 +39,8 @@
#include <stdio.h> #include <stdio.h>
#include "HashMap.h" #include <HashMap.h>
#include "Http.h" #include <Http.h>
/** /**
* A server response is represented by a client context. It is * A server response is represented by a client context. It is

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files * obtaining a copy of this software and associated documentation files
@ -38,7 +38,7 @@
* handler functions. * handler functions.
*/ */
#include "Array.h" #include <Array.h>
/** /**
* The router structure is opaque and thus managed entirely by the * The router structure is opaque and thus managed entirely by the
@ -74,7 +74,7 @@ extern void HttpRouterFree(HttpRouter *);
* .Pa /some/path/(.*)/parts * .Pa /some/path/(.*)/parts
* to work as one would expect. * to work as one would expect.
*/ */
extern bool HttpRouterAdd(HttpRouter *, char *, HttpRouteFunc *); extern int HttpRouterAdd(HttpRouter *, char *, HttpRouteFunc *);
/** /**
* Route the specified request path using the specified routing * Route the specified request path using the specified routing
@ -86,6 +86,6 @@ extern bool HttpRouterAdd(HttpRouter *, char *, HttpRouteFunc *);
* how to handle, and the pointer to a void pointer is where the * how to handle, and the pointer to a void pointer is where the
* route function's response will be placed. * route function's response will be placed.
*/ */
extern bool HttpRouterRoute(HttpRouter *, char *, void *, void **); extern int HttpRouterRoute(HttpRouter *, char *, void *, void **);
#endif /* CYTOPLASM_HTTPROUTER_H */ #endif /* CYTOPLASM_HTTPROUTER_H */

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files * obtaining a copy of this software and associated documentation files
@ -46,12 +46,12 @@
* closely resembles Java. * closely resembles Java.
*/ */
#include <stdio.h> #include <Http.h>
#include <stdbool.h>
#include "Http.h" #include <stdio.h>
#include "HashMap.h"
#include "Stream.h" #include <HashMap.h>
#include <Stream.h>
/** /**
* The functions on this API operate on an opaque structure. * The functions on this API operate on an opaque structure.
@ -134,7 +134,7 @@ extern void HttpServerFree(HttpServer *);
* caller can continue working while the HTTP server is running in a * caller can continue working while the HTTP server is running in a
* separate thread and managing a pool of threads to handle responses. * separate thread and managing a pool of threads to handle responses.
*/ */
extern bool HttpServerStart(HttpServer *); extern int HttpServerStart(HttpServer *);
/** /**
* Typically, at some point after calling * Typically, at some point after calling

122
src/include/Int.h Normal file
View file

@ -0,0 +1,122 @@
/*
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef CYTOPLASM_INT_H
#define CYTOPLASM_INT_H
/***
* @Nm Int
* @Nd Fixed-width integer types.
* @Dd April 27 2023
*
* This header provides cross-platform, fixed-width integer types.
* Specifically, it uses preprocessor magic to define the following
* types:
* .Bl -bullet -offset indent
* .It
* Int8 and UInt8
* .It
* Int16 and UInt16
* .It
* Int32 and UInt32
* .El
* .Pp
* Note that there is no 64-bit integer type, because the ANSI C
* standard makes no guarantee that such a type will exist, even
* though it does on most platforms.
* .Pp
* The reason Cytoplasm provides its own header for this is
* because ANSI C does not define fixed-width types, and while it
* should be safe to rely on C99 fixed-width types in most cases,
* there may be cases where even that is not possible.
*
* @ignore-typedefs
*/
#include <limits.h>
#define BIT32_MAX 4294967295UL
#define BIT16_MAX 65535UL
#define BIT8_MAX 255UL
#ifndef UCHAR_MAX
#error Size of char data type is unknown. Define UCHAR_MAX.
#endif
#ifndef USHRT_MAX
#error Size of short data type is unknown. Define USHRT_MAX.
#endif
#ifndef UINT_MAX
#error Size of int data type is unknown. Define UINT_MAX.
#endif
#ifndef ULONG_MAX
#error Size of long data type is unknown. Define ULONG_MAX.
#endif
#if UCHAR_MAX == BIT8_MAX
typedef signed char Int8;
typedef unsigned char UInt8;
#else
#error Unable to determine suitable data type for 8-bit integers.
#endif
#if UINT_MAX == BIT16_MAX
typedef signed int Int16;
typedef unsigned int UInt16;
#elif USHRT_MAX == BIT16_MAX
typedef signed short Int16;
typedef unsigned short UInt16;
#elif UCHAR_MAX == BIT16_MAX
typedef signed char Int16;
typedef unsigned char UInt16;
#else
#error Unable to determine suitable data type for 16-bit integers.
#endif
#if ULONG_MAX == BIT32_MAX
typedef signed long Int32;
typedef unsigned long UInt32;
#elif UINT_MAX == BIT32_MAX
typedef signed int Int32;
typedef unsigned int UInt32;
#elif USHRT_MAX == BIT32_MAX
typedef signed short Int32;
typedef unsigned short UInt32;
#elif UCHAR_MAX == BIT32_MAX
typedef signed char Int32;
typedef unsigned char UInt32;
#else
#error Unable to determine suitable data type for 32-bit integers.
#endif
#endif /* CYTOPLASM_INT_H */

252
src/include/Int64.h Normal file
View file

@ -0,0 +1,252 @@
/*
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef CYTOPLASM_INT64_H
#define CYTOPLASM_INT64_H
/***
* @Nm Int64
* @Nd Fixed-width 64 bit integers.
* @Dd August 11, 2023
*
* .Pp
* ANSI C89 (or C99 for that matter) provides no required mechanism
* for 64 bit integers. Nevertheless, many compilers provide them as
* extensions. However, since it is not a gaurantee, and to be fully
* standards-compliant and thus portable, a platform-agnostic interface
* is required. This header provides such an interface. If the platform
* has a 64 bit integer type, that is used, and native operations are
* performed by C preprocessor macro expansion. Otherwise, a
* compatibility layer is provided, which implements 64-bit
* arithmetic on an array of 2 32-bit numbers which are provided by
* .Xr Int 3 .
* .Pp
* Note that 64-bit emulation is certainly not as performant as using
* native 64-bit operations, so whenever possible, the native
* operations should be preferred. However, since C provides no required
* 64 bit integer on 32-bit and less platforms, this API can be used as
* a "good enough" fallback mechanism.
* .Pp
* Also note that this implementation, both in the native and
* non-native forms, makes some assumptions:
* .Bl -bullet -width Ds
* .It
* When a cast from a larger integer to a smaller integer is performed,
* the upper bits are truncated, not the lower bits.
* .It
* Negative numbers are represented in memory and in registers in two's
* compliment form.
* .El
* .Pp
* This API may provide unexpected output if these assumptions are
* false for a given platform.
*
* @ignore-typedefs
*/
#include <Int.h>
#include <UInt64.h>
#include <stddef.h>
#ifndef INT64_FORCE_EMULATED
#define BIT64_MAX 18446744073709551615UL
#if UINT_MAX == BIT64_MAX
typedef signed int Int64;
#define INT64_NATIVE
#elif ULONG_MAX == BIT64_MAX
typedef signed long Int64;
#define INT64_NATIVE
#endif
#endif /* ifndef INT64_FORCE_EMULATED */
#ifdef INT64_NATIVE
#define Int64Create(high, low) ((Int64) (((UInt64) (high) << 32) | (low)))
#define Int64Neg(x) (-(x))
#define Int64Low(a) ((UInt32) (a))
#define Int64High(a) ((UInt32) ((a) >> 32))
#define Int64Add(a, b) ((a) + (b))
#define Int64Sub(a, b) ((a) - (b))
#define Int64Mul(a, b) ((a) * (b))
#define Int64Div(a, b) ((a) / (b))
#define Int64Rem(a, b) ((a) % (b))
#define Int64Sll(a, b) ((a) << (b))
#define Int64Sra(a, b) ((a) >> (b))
#define Int64And(a, b) ((a) & (b))
#define Int64Or(a, b) ((a) | (b))
#define Int64Xor(a, b) ((a) ^ (b))
#define Int64Not(a) (~(a))
#define Int64Eq(a, b) ((a) == (b))
#define Int64Lt(a, b) ((a) < (b))
#define Int64Gt(a, b) ((a) > (b))
#define Int64Neq(a, b) ((a) != (b))
#define Int64Leq(a, b) ((a) <= (b))
#define Int64Geq(a, b) ((a) >= (b))
#else
#define Int64Neg(x) (Int64Add(Int64Not(x), Int64Create(0, 1)))
/**
* The internal bit representation of a signed integer is identical
* to an unsigned integer, the difference is in the algorithms and
* the way the bits are interpreted.
*/
typedef UInt64 Int64;
/**
* Create a new signed 64 bit integer using the given high and low
* bits.
*/
extern Int64 Int64Create(UInt32, UInt32);
/**
* Add two signed 64 bit integers together.
*/
extern Int64 Int64Add(Int64, Int64);
/**
* Subtract the second 64 bit integer from the first.
*/
extern Int64 Int64Sub(Int64, Int64);
/**
* Multiply two 64 bit integers together. The non-native version of
* this function uses the Russian Peasant method of multiplication,
* which should afford more performance than a naive multiplication by
* addition, but it is still rather slow and depends on the size of
* the integers being multiplied.
*/
extern Int64 Int64Mul(Int64, Int64);
/**
* Divide the first 64 bit integer by the second and return the
* quotient. The non-native version of this function uses naive binary
* long division, which is slow, but gauranteed to finish in constant
* time.
*/
extern Int64 Int64Div(Int64, Int64);
/**
* Divide the first 64 bit integer by the second and return the
* remainder. The non-native version of this function uses naive binary
* long division, which is slow, but gauranteed to finish in constant
* time.
*/
extern Int64 Int64Rem(Int64, Int64);
/**
* Perform a left logical bit shift of a 64 bit integer. The second
* parameter is how many places to shift, and is declared as a regular
* integer because anything more than 64 does not make sense.
*/
extern Int64 Int64Sll(Int64, int);
/**
* Perform a right arithmetic bit shift of a 64 bit integer. The second
* parameter is how many places to shift, and is declared as a regular
* integer because anything more than 64 does not make sense.
* .Pp
* Note that on platforms that use the native 64-bit implementation,
* this is technically implementation-defined, and may in fact be a
* logical shift instead of an arithmetic shift. Note that typically
* this operation is not performed on signed integers.
*/
extern Int64 Int64Sra(Int64, int);
/**
* Perform a bitwise AND (&) of the provided 64 bit integers.
*/
extern Int64 Int64And(Int64, Int64);
/**
* Perform a bitwise OR (|) of the provided 64 bit integers.
*/
extern Int64 Int64Or(Int64, Int64);
/**
* Perform a bitwise XOR (^) of the provided 64 bit integers.
*/
extern Int64 Int64Xor(Int64, Int64);
/**
* Perform a bitwise NOT (~) of the provided 64 bit integer.
*/
extern Int64 Int64Not(Int64);
/**
* Perform a comparison of the provided 64 bit integers and return a C
* boolean that is true if and only if they are equal.
*/
extern int Int64Eq(Int64, Int64);
/**
* Perform a comparison of the provided 64 bit integers and return a C
* boolean that is true if and only if the second operand is strictly
* less than the first.
*/
extern int Int64Lt(Int64, Int64);
/**
* Perform a comparison of the provided 64 bit integers and return a C
* boolean that is true if and only if the second operand is strictly
* greater than the first.
*/
extern int Int64Gt(Int64, Int64);
#define Int64Low(a) ((a).i[0])
#define Int64High(a) ((a).i[1])
#define Int64Neq(a, b) (!Int64Eq(a, b))
#define Int64Leq(a, b) (Int64Eq(a, b) || Int64Lt(a, b))
#define Int64Geq(a, b) (Int64Eq(a, b) || Int64Gt(a, b))
#endif
#define INT64_STRBUF 65 /* Base 2 representation with '\0' */
/**
* Convert a 64 bit integer to a string in an arbitrary base
* representation specified by the second parameter, using the provided
* buffer and length specified by the third and fourth parameters. To
* guarantee that the string will fit in the buffer, allocate it of
* size INT64_STRBUF or larger. Note that a buffer size smaller than
* INT64_STRBUF will invoke undefined behavior.
*/
extern size_t Int64Str(Int64, int, char *, size_t);
#endif /* CYTOPLASM_INT64_H */

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files * obtaining a copy of this software and associated documentation files

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files * obtaining a copy of this software and associated documentation files
@ -68,13 +68,13 @@
* behavior. * behavior.
*/ */
#include "HashMap.h" #include <HashMap.h>
#include "Array.h" #include <Array.h>
#include "Stream.h" #include <Stream.h>
#include <Int64.h>
#include <stdio.h> #include <stdio.h>
#include <stddef.h> #include <stddef.h>
#include <stdbool.h>
#define JSON_DEFAULT -1 #define JSON_DEFAULT -1
#define JSON_PRETTY 0 #define JSON_PRETTY 0
@ -152,7 +152,7 @@ extern char * JsonValueAsString(JsonValue *);
* Encode a number as a JSON value that can be added to an object or * Encode a number as a JSON value that can be added to an object or
* an array. * an array.
*/ */
extern JsonValue * JsonValueInteger(uint64_t); extern JsonValue * JsonValueInteger(Int64);
/** /**
* Unwrap a JSON value that represents a number. This function will * Unwrap a JSON value that represents a number. This function will
@ -160,7 +160,7 @@ extern JsonValue * JsonValueInteger(uint64_t);
* misleading. Check the type of the value before making assumptions * misleading. Check the type of the value before making assumptions
* about its value. * about its value.
*/ */
extern uint64_t JsonValueAsInteger(JsonValue *); extern Int64 JsonValueAsInteger(JsonValue *);
/** /**
* Encode a floating point number as a JSON value that can be added * Encode a floating point number as a JSON value that can be added
@ -181,7 +181,7 @@ extern double JsonValueAsFloat(JsonValue *);
* expressions as a JSON value that can be added to an object or an * expressions as a JSON value that can be added to an object or an
* array. * array.
*/ */
extern JsonValue * JsonValueBoolean(bool); extern JsonValue * JsonValueBoolean(int);
/** /**
* Unwrap a JSON value that represents a boolean. This function will * Unwrap a JSON value that represents a boolean. This function will
@ -189,7 +189,7 @@ extern JsonValue * JsonValueBoolean(bool);
* misleading. Check the type of the value before making assumptions * misleading. Check the type of the value before making assumptions
* about its type. * about its type.
*/ */
extern bool JsonValueAsBoolean(JsonValue *); extern int JsonValueAsBoolean(JsonValue *);
/** /**
* This is a special case that represents a JSON null. Because the * This is a special case that represents a JSON null. Because the
@ -253,7 +253,7 @@ extern void JsonFree(HashMap *);
* or if the stream is NULL, the number of bytes that would have * or if the stream is NULL, the number of bytes that would have
* been written. * been written.
*/ */
extern size_t JsonEncodeString(const char *, Stream *); extern int JsonEncodeString(const char *, Stream *);
/** /**
* Serialize a JSON value as it would appear in JSON output. This is * Serialize a JSON value as it would appear in JSON output. This is
@ -277,7 +277,7 @@ extern size_t JsonEncodeString(const char *, Stream *);
* or if the stream is NULL, the number of bytes that would have * or if the stream is NULL, the number of bytes that would have
* been written. * been written.
*/ */
extern size_t JsonEncodeValue(JsonValue *, Stream *, int); extern int JsonEncodeValue(JsonValue *, Stream *, int);
/** /**
* Encode a JSON object as it would appear in JSON output, writing it * Encode a JSON object as it would appear in JSON output, writing it
@ -289,7 +289,7 @@ extern size_t JsonEncodeValue(JsonValue *, Stream *, int);
* or if the stream is NULL, the number of bytes that would have * or if the stream is NULL, the number of bytes that would have
* been written. * been written.
*/ */
extern size_t JsonEncode(HashMap *, Stream *, int); extern int JsonEncode(HashMap *, Stream *, int);
/** /**
* Decode a JSON object from the given input stream and parse it into * Decode a JSON object from the given input stream and parse it into
@ -320,13 +320,4 @@ extern JsonValue * JsonGet(HashMap *, size_t,...);
*/ */
extern JsonValue * JsonSet(HashMap *, JsonValue *, size_t,...); extern JsonValue * JsonSet(HashMap *, JsonValue *, size_t,...);
/**
* Recursively merge two JSON objects. The second object is merged
* on top of the first; any keys present in the first object that are
* also present in the second object are replaced with those in the
* second object, and any keys present in the second object that are
* not present in the first object are copied to the first object.
*/
extern void JsonMerge(HashMap *, HashMap *);
#endif /* CYTOPLASM_JSON_H */ #endif /* CYTOPLASM_JSON_H */

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files * obtaining a copy of this software and associated documentation files
@ -42,7 +42,7 @@
#include <stddef.h> #include <stddef.h>
#include <syslog.h> #include <syslog.h>
#include "Stream.h" #include <Stream.h>
#define LOG_FLAG_COLOR (1 << 0) #define LOG_FLAG_COLOR (1 << 0)
#define LOG_FLAG_SYSLOG (1 << 1) #define LOG_FLAG_SYSLOG (1 << 1)

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files * obtaining a copy of this software and associated documentation files
@ -76,7 +76,6 @@
* macros. * macros.
*/ */
#include <stddef.h> #include <stddef.h>
#include <stdint.h>
/** /**
* These values are passed into the memory hook function to indicate * These values are passed into the memory hook function to indicate

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files * obtaining a copy of this software and associated documentation files
@ -46,7 +46,6 @@
*/ */
#include <stddef.h> #include <stddef.h>
#include <stdbool.h>
/** /**
* These functions operate on a queue structure that is opaque to the * These functions operate on a queue structure that is opaque to the
@ -74,7 +73,7 @@ extern void QueueFree(Queue *);
* value indicating whether or not the push succeeded. Pushing items * value indicating whether or not the push succeeded. Pushing items
* into the queue will fail if the queue is full. * into the queue will fail if the queue is full.
*/ */
extern bool QueuePush(Queue *, void *); extern int QueuePush(Queue *, void *);
/** /**
* Pop an element out of the queue. This function returns NULL if the * Pop an element out of the queue. This function returns NULL if the
@ -96,11 +95,11 @@ extern void * QueuePeek(Queue *);
/** /**
* Determine whether or not the queue is full. * Determine whether or not the queue is full.
*/ */
extern bool QueueFull(Queue *); extern int QueueFull(Queue *);
/** /**
* Determine whether or not the queue is empty. * Determine whether or not the queue is empty.
*/ */
extern bool QueueEmpty(Queue *); extern int QueueEmpty(Queue *);
#endif #endif

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files * obtaining a copy of this software and associated documentation files
@ -58,13 +58,11 @@
*/ */
#include <stddef.h> #include <stddef.h>
#include <stdint.h>
/** /**
* Generate a single random 32-bit integer between 0 and the * Generate a single random integer between 0 and the passed value.
* passed value.
*/ */
extern uint32_t RandInt(uint32_t); extern int RandInt(unsigned int);
/** /**
* Generate the number of integers specified by the second argument * Generate the number of integers specified by the second argument
@ -78,6 +76,6 @@ extern uint32_t RandInt(uint32_t);
* has to lock and unlock a mutex. It is therefore better to obtain * has to lock and unlock a mutex. It is therefore better to obtain
* multiple random numbers in one pass if multiple are needed. * multiple random numbers in one pass if multiple are needed.
*/ */
extern void RandIntN(uint32_t *, size_t, uint32_t); extern void RandIntN(int *, size_t, unsigned int);
#endif /* CYTOPLASM_RAND_H */ #endif /* CYTOPLASM_RAND_H */

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files * obtaining a copy of this software and associated documentation files
@ -37,6 +37,8 @@
* the runtime stub needs to know their definitions. * the runtime stub needs to know their definitions.
*/ */
#include <Array.h>
/** /**
* Write a memory report to a file in the current directory, using * Write a memory report to a file in the current directory, using
* the provided program arguments, including the program name that * the provided program arguments, including the program name that

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files * obtaining a copy of this software and associated documentation files

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files * obtaining a copy of this software and associated documentation files
@ -39,21 +39,21 @@
* is a standard library header. * is a standard library header.
*/ */
#include <Int.h>
#include <stddef.h> #include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
/** /**
* Convert UTF-16 into a Unicode codepoint. * Convert UTF-16 into a Unicode codepoint.
*/ */
extern uint32_t StrUtf16Decode(uint16_t, uint16_t); extern UInt32 StrUtf16Decode(UInt16, UInt16);
/** /**
* Take a Unicode codepoint and encode it into a string buffer containing * Take a Unicode codepoint and encode it into a string buffer containing
* between 1 and 4 bytes. The string buffer is allocated on the heap, * between 1 and 4 bytes. The string buffer is allocated on the heap,
* so it should be freed when it is no longer needed. * so it should be freed when it is no longer needed.
*/ */
extern char * StrUtf8Encode(uint32_t); extern char * StrUtf8Encode(UInt32);
/** /**
* Duplicate a null-terminated string, returning a new string on the * Duplicate a null-terminated string, returning a new string on the
@ -87,7 +87,7 @@ extern char * StrConcat(size_t,...);
* string consists only of blank characters, as determined by * string consists only of blank characters, as determined by
* .Xr isblank 3 . * .Xr isblank 3 .
*/ */
extern bool StrBlank(const char *str); extern int StrBlank(const char *str);
/** /**
* Generate a string of the specified length, containing random * Generate a string of the specified length, containing random
@ -124,6 +124,6 @@ extern char * StrLower(char *);
* function returns a boolean value indicating whether or not * function returns a boolean value indicating whether or not
* strcmp() returned 0. * strcmp() returned 0.
*/ */
extern bool StrEquals(const char *, const char *); extern int StrEquals(const char *, const char *);
#endif /* CYTOPLASM_STR_H */ #endif /* CYTOPLASM_STR_H */

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files * obtaining a copy of this software and associated documentation files
@ -36,11 +36,9 @@
* C library and offering some more convenience features. * C library and offering some more convenience features.
*/ */
#include "Io.h" #include <Io.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdint.h>
#include <stdbool.h>
/** /**
* An opaque structure analogous to C's FILE pointers. * An opaque structure analogous to C's FILE pointers.
@ -174,7 +172,7 @@ extern off_t StreamSeek(Stream *, off_t, int);
* .Xr feof 3 * .Xr feof 3
* function. * function.
*/ */
extern bool StreamEof(Stream *); extern int StreamEof(Stream *);
/** /**
* Test the stream for an error condition, returning a boolean value * Test the stream for an error condition, returning a boolean value
@ -183,7 +181,7 @@ extern bool StreamEof(Stream *);
* .Xr ferror 3 * .Xr ferror 3
* function. * function.
*/ */
extern bool StreamError(Stream *); extern int StreamError(Stream *);
/** /**
* Clear the error condition associated with the given stream, allowing * Clear the error condition associated with the given stream, allowing

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files * obtaining a copy of this software and associated documentation files
@ -46,7 +46,7 @@
* .El * .El
*/ */
#include "Stream.h" #include <Stream.h>
#define TLS_LIBRESSL 2 #define TLS_LIBRESSL 2
#define TLS_OPENSSL 3 #define TLS_OPENSSL 3

252
src/include/UInt64.h Normal file
View file

@ -0,0 +1,252 @@
/*
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef CYTOPLASM_UINT64_H
#define CYTOPLASM_UINT64_H
/***
* @Nm UInt64
* @Nd Fixed-width 64 bit integers.
* @Dd August 11, 2023
*
* .Pp
* ANSI C89 (or C99 for that matter) provides no required mechanism
* for 64 bit integers. Nevertheless, many compilers provide them as
* extensions. However, since it is not a gaurantee, and to be fully
* standards-compliant and thus portable, a platform-agnostic interface
* is required. This header provides such an interface. If the platform
* has a 64 bit integer type, that is used, and native operations are
* performed by C preprocessor macro expansion. Otherwise, a
* compatibility layer is provided, which implements 64-bit
* arithmetic on an array of 2 32-bit numbers which are provided by
* .Xr Int 3 .
* .Pp
* Note that 64-bit emulation is certainly not as performant as using
* native 64-bit operations, so whenever possible, the native
* operations should be preferred. However, since C provides no required
* 64 bit integer on 32-bit and less platforms, this API can be used as
* a "good enough" fallback mechanism.
* .Pp
* Also note that this implementation, both in the native and
* non-native forms, makes some assumptions:
* .Bl -bullet -width Ds
* .It
* When a cast from a larger integer to a smaller integer is performed,
* the upper bits are truncated, not the lower bits.
* .It
* Negative numbers are represented in memory and in registers in two's
* compliment form.
* .El
* .Pp
* This API may provide unexpected output if these assumptions are
* false for a given platform.
*
* @ignore-typedefs
*/
#include <Int.h>
#include <stddef.h>
#ifndef INT64_FORCE_EMULATED
#define BIT64_MAX 18446744073709551615UL
#if UINT_MAX == BIT64_MAX
/* typedef signed int Int64; */
typedef unsigned int UInt64;
#define UINT64_NATIVE
#elif ULONG_MAX == BIT64_MAX
/* typedef signed int Int64; */
typedef unsigned long UInt64;
#define UINT64_NATIVE
#endif
#endif /* ifndef INT64_FORCE_EMULATED */
#ifdef UINT64_NATIVE
#define UInt64Create(high, low) (((UInt64) (high) << 32) | (low))
#define UInt64Low(a) ((UInt32) ((a) & 0x00000000FFFFFFFF))
#define UInt64High(a) ((UInt32) ((a) >> 32))
#define UInt64Add(a, b) ((a) + (b))
#define UInt64Sub(a, b) ((a) - (b))
#define UInt64Mul(a, b) ((a) * (b))
#define UInt64Div(a, b) ((a) / (b))
#define UInt64Rem(a, b) ((a) % (b))
#define UInt64Sll(a, b) ((a) << (b))
#define UInt64Srl(a, b) ((a) >> (b))
#define UInt64And(a, b) ((a) & (b))
#define UInt64Or(a, b) ((a) | (b))
#define UInt64Xor(a, b) ((a) ^ (b))
#define UInt64Not(a) (~(a))
#define UInt64Eq(a, b) ((a) == (b))
#define UInt64Lt(a, b) ((a) < (b))
#define UInt64Gt(a, b) ((a) > (b))
#define UInt64Neq(a, b) ((a) != (b))
#define UInt64Leq(a, b) ((a) <= (b))
#define UInt64Geq(a, b) ((a) >= (b))
#else
/**
* For platforms that do not have a native integer large enough to
* store a 64 bit integer, this struct is used. i[0] contains the low
* bits of integer, and i[1] contains the high bits of the integer.
* .Pp
* This struct should not be accessed directly, because UInt64 may not
* actually be this struct, it might be an actual integer type. For
* maximum portability, only use the functions defined here to
* manipulate 64 bit integers.
*/
typedef struct
{
UInt32 i[2];
} UInt64;
/**
* Create a new unsigned 64 bit integer using the given high and low
* bits.
*/
extern UInt64 UInt64Create(UInt32, UInt32);
/**
* Add two unsigned 64 bit integers together.
*/
extern UInt64 UInt64Add(UInt64, UInt64);
/**
* Subtract the second 64 bit integer from the first.
*/
extern UInt64 UInt64Sub(UInt64, UInt64);
/**
* Multiply two 64 bit integers together. The non-native version of
* this function uses the Russian Peasant method of multiplication,
* which should afford more performance than a naive multiplication by
* addition, but it is still rather slow and depends on the size of
* the integers being multiplied.
*/
extern UInt64 UInt64Mul(UInt64, UInt64);
/**
* Divide the first 64 bit integer by the second and return the
* quotient. The non-native version of this function uses naive binary
* long division, which is slow, but gauranteed to finish in constant
* time.
*/
extern UInt64 UInt64Div(UInt64, UInt64);
/**
* Divide the first 64 bit integer by the second and return the
* remainder. The non-native version of this function uses naive binary
* long division, which is slow, but gauranteed to finish in constant
* time.
*/
extern UInt64 UInt64Rem(UInt64, UInt64);
/**
* Perform a left logical bit shift of a 64 bit integer. The second
* parameter is how many places to shift, and is declared as a regular
* integer because anything more than 64 does not make sense.
*/
extern UInt64 UInt64Sll(UInt64, int);
/**
* Perform a right logical bit shift of a 64 bit integer. The second
* parameter is how many places to shift, and is declared as a regular
* integer because anything more than 64 does not make sense.
*/
extern UInt64 UInt64Srl(UInt64, int);
/**
* Perform a bitwise AND (&) of the provided 64 bit integers.
*/
extern UInt64 UInt64And(UInt64, UInt64);
/**
* Perform a bitwise OR (|) of the provided 64 bit integers.
*/
extern UInt64 UInt64Or(UInt64, UInt64);
/**
* Perform a bitwise XOR (^) of the provided 64 bit integers.
*/
extern UInt64 UInt64Xor(UInt64, UInt64);
/**
* Perform a bitwise NOT (~) of the provided 64 bit integer.
*/
extern UInt64 UInt64Not(UInt64);
/**
* Perform a comparison of the provided 64 bit integers and return a C
* boolean that is true if and only if they are equal.
*/
extern int UInt64Eq(UInt64, UInt64);
/**
* Perform a comparison of the provided 64 bit integers and return a C
* boolean that is true if and only if the second operand is strictly
* less than the first.
*/
extern int UInt64Lt(UInt64, UInt64);
/**
* Perform a comparison of the provided 64 bit integers and return a C
* boolean that is true if and only if the second operand is strictly
* greater than the first.
*/
extern int UInt64Gt(UInt64, UInt64);
#define UInt64Low(a) ((a).i[0])
#define UInt64High(a) ((a).i[1])
#define UInt64Neq(a, b) (!UInt64Eq(a, b))
#define UInt64Leq(a, b) (UInt64Eq(a, b) || UInt64Lt(a, b))
#define UInt64Geq(a, b) (UInt64Eq(a, b) || UInt64Gt(a, b))
#endif
#define UINT64_STRBUF 65 /* Base 2 representation with '\0' */
/**
* Convert a 64 bit integer to a string in an arbitrary base
* representation specified by the second parameter, using the provided
* buffer and length specified by the third and fourth parameters. To
* guarantee that the string will fit in the buffer, allocate it of
* size UINT64_STRBUF or larger. Note that a buffer size smaller than
* UINT64_STRBUF will invoke undefined behavior.
*/
extern size_t UInt64Str(UInt64, int, char *, size_t);
#endif /* CYTOPLASM_UINT64_H */

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files * obtaining a copy of this software and associated documentation files

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files * obtaining a copy of this software and associated documentation files
@ -42,7 +42,8 @@
#include <stddef.h> #include <stddef.h>
#include <sys/types.h> #include <sys/types.h>
#include "Stream.h" #include <Stream.h>
#include <UInt64.h>
/** /**
* Get the current timestamp in milliseconds since the Unix epoch. This * Get the current timestamp in milliseconds since the Unix epoch. This
@ -60,7 +61,7 @@
* overflow before it even gets to this function, which will cause this * overflow before it even gets to this function, which will cause this
* function to produce unexpected results. * function to produce unexpected results.
*/ */
extern uint64_t UtilTsMillis(void); extern UInt64 UtilServerTs(void);
/** /**
* Use * Use
@ -69,7 +70,7 @@ extern uint64_t UtilTsMillis(void);
* was an error getting the last modified time of a file. This is * was an error getting the last modified time of a file. This is
* primarily useful for caching file data. * primarily useful for caching file data.
*/ */
extern uint64_t UtilLastModified(char *); extern UInt64 UtilLastModified(char *);
/** /**
* This function behaves just like the system call * This function behaves just like the system call
@ -85,7 +86,7 @@ extern int UtilMkdir(const char *, const mode_t);
* .Xr nanosleep 2 * .Xr nanosleep 2
* to make its usage much, much simpler. * to make its usage much, much simpler.
*/ */
extern int UtilSleepMillis(uint64_t); extern int UtilSleepMillis(UInt64);
/** /**
* This function works identically to the POSIX * This function works identically to the POSIX
@ -111,6 +112,6 @@ extern ssize_t UtilGetLine(char **, size_t *, Stream *);
* .Fn pthread_self * .Fn pthread_self
* to a number. * to a number.
*/ */
extern uint32_t UtilThreadNo(void); extern UInt32 UtilThreadNo(void);
#endif /* CYTOPLASM_UTIL_H */ #endif /* CYTOPLASM_UTIL_H */

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files * obtaining a copy of this software and associated documentation files

View file

@ -1,260 +0,0 @@
/*
* 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 <stdio.h>
#include <string.h>
#include <ctype.h>
#include <getopt.h>
#include <unistd.h>
#include <errno.h>
#include <Args.h>
#include <Memory.h>
#include <Str.h>
#include <HashMap.h>
#include <HttpClient.h>
#include <Uri.h>
#define FLAG_HEADERS (1 << 0)
static void
usage(char *prog)
{
StreamPrintf(StreamStderr(), "Usage: %s [-i -X method -H header -d data] url\n", prog);
}
int
Main(Array * args)
{
HttpClientContext *cx = NULL;
HttpStatus res;
HttpRequestMethod method = HTTP_GET;
Uri *uri = NULL;
char *data = NULL;
HashMap *requestHeaders = HashMapCreate();
char *key;
char *val;
ArgParseState arg;
int flags = 0;
int requestFlags = HTTP_FLAG_NONE;
int ch;
int ret = 1;
ArgParseStateInit(&arg);
while ((ch = ArgParse(&arg, args, "iH:X:d:")) != -1)
{
switch (ch)
{
case 'i':
flags |= FLAG_HEADERS;
break;
case 'X':
method = HttpRequestMethodFromString(arg.optArg);
if (!method)
{
StreamPrintf(StreamStderr(), "Unknown request method: %s\n", arg.optArg);
return 1;
}
break;
case 'H':
key = arg.optArg;
val = arg.optArg;
while (*val && *val != ':')
{
val++;
}
*val = '\0';
val++;
while (*val && isspace((unsigned char) *val))
{
val++;
}
HashMapSet(requestHeaders, key, StrDuplicate(val));
break;
case 'd':
data = arg.optArg;
break;
default:
usage(ArrayGet(args, 0));
goto finish;
}
}
if (ArraySize(args) - arg.optInd < 1)
{
usage(ArrayGet(args, 0));
goto finish;
}
uri = UriParse(ArrayGet(args, arg.optInd));
if (!uri)
{
StreamPrintf(StreamStderr(), "Failed to parse URI: %s\n", ArrayGet(args, arg.optInd));
goto finish;
}
if (!uri->port)
{
if (StrEquals(uri->proto, "https"))
{
uri->port = 443;
}
else if (StrEquals(uri->proto, "http"))
{
uri->port = 80;
}
}
if (!uri->port)
{
StreamPrintf(StreamStderr(), "Unknown protocol: %s\n", uri->proto);
goto finish;
}
if (StrEquals(uri->proto, "https"))
{
requestFlags |= HTTP_FLAG_TLS;
}
cx = HttpRequest(method, requestFlags, uri->port, uri->host, uri->path);
if (!cx)
{
StreamPuts(StreamStderr(), "Failed to connect.\n");
goto finish;
}
while (HashMapIterate(requestHeaders, &key, (void **) &val))
{
HttpRequestHeader(cx, key, val);
Free(val);
}
if (data)
{
if (*data == '@')
{
Stream *in;
int len;
data++;
if (StrEquals(data, "-"))
{
in = StreamStdin();
}
else
{
in = StreamOpen(data, "r");
}
if (!in)
{
StreamPrintf(StreamStderr(), "%s: %s\n", data, strerror(errno));
goto finish;
}
len = StreamSeek(in, 0, SEEK_END);
if (len > -1)
{
char *lenStr;
int nBytes;
StreamSeek(in, 0, SEEK_SET);
nBytes = snprintf(NULL, 0, "%d", len);
lenStr = Malloc(nBytes + 1);
snprintf(lenStr, nBytes + 1, "%d", len);
HttpRequestHeader(cx, "Content-Length", lenStr);
Free(lenStr);
}
HttpRequestSendHeaders(cx);
StreamCopy(in, HttpClientStream(cx));
if (in != StreamStdin())
{
StreamClose(in);
}
}
else
{
char *lenStr;
int len = strlen(data);
int nBytes = snprintf(NULL, 0, "%d", len);
lenStr = Malloc(nBytes + 1);
snprintf(lenStr, nBytes + 1, "%d", len);
HttpRequestHeader(cx, "Content-Length", lenStr);
Free(lenStr);
HttpRequestSendHeaders(cx);
StreamPuts(HttpClientStream(cx), data);
}
}
else
{
HttpRequestSendHeaders(cx);
}
res = HttpRequestSend(cx);
if (!res)
{
StreamPuts(StreamStderr(), "Failed to send request.\n");
goto finish;
}
if (flags & FLAG_HEADERS)
{
HashMap *responseHeaders = HttpResponseHeaders(cx);
StreamPrintf(StreamStdout(), "HTTP/1.0 %d %s\n", res, HttpStatusToString(res));
while (HashMapIterate(responseHeaders, &key, (void **) &val))
{
StreamPrintf(StreamStdout(), "%s: %s\n", key, val);
}
StreamPutc(StreamStdout(), '\n');
}
StreamCopy(HttpClientStream(cx), StreamStdout());
ret = !(res == HTTP_OK);
finish:
HashMapFree(requestHeaders);
HttpClientContextFree(cx);
UriFree(uri);
return ret;
}

145
tools/int64.c Normal file
View file

@ -0,0 +1,145 @@
#include <Int64.h>
#include <Log.h>
/* AssertEquals(actual, expected) */
int
AssertEquals(char *msg, Int64 x, Int64 y)
{
if (!Int64Eq(x, y))
{
Log(LOG_ERR, "%s: Expected 0x%X 0x%X, got 0x%X 0x%X", msg,
Int64High(y), Int64Low(y),
Int64High(x), Int64Low(x));
return 0;
}
return 1;
}
int
Main(void)
{
Int64 x, y;
Log(LOG_INFO, "sizeof(Int64) = %lu", sizeof(Int64));
#ifdef INT64_NATIVE
Log(LOG_INFO, "Using native 64-bit integers.");
#else
Log(LOG_INFO, "Using emulated 64-bit integers.");
#endif
/* BSR Tests */
x = Int64Create(0x000000FF, 0x00000000);
y = Int64Sra(x, 4);
AssertEquals("x >> 4", y, Int64Create(0x0000000F, 0xF0000000));
y = Int64Sra(x, 8);
AssertEquals("x >> 8", y, Int64Create(0x00000000, 0xFF000000));
y = Int64Sra(x, 36);
AssertEquals("x >> 36", y, Int64Create(0x00000000, 0x0000000F));
x = Int64Create(0xFF000000, 0x00000000);
y = Int64Sra(x, 4);
AssertEquals("x >> 4", y, Int64Create(0xFFF00000, 0x00000000));
y = Int64Sra(x, 8);
AssertEquals("x >> 8", y, Int64Create(0xFFFF0000, 0x00000000));
y = Int64Sra(x, 63);
AssertEquals("x >> 63", y, Int64Create(0xFFFFFFFF, 0xFFFFFFFF));
/* BSL Tests */
x = Int64Create(0x00000000, 0xFF000000);
y = Int64Sll(x, 4);
AssertEquals("x << 4", y, Int64Create(0x0000000F, 0xF0000000));
y = Int64Sll(x, 8);
AssertEquals("x << 8", y, Int64Create(0x000000FF, 0x00000000));
y = Int64Sll(x, 36);
AssertEquals("x << 36", y, Int64Create(0xF0000000, 0x00000000));
/* ADD Tests */
x = Int64Create(0x00000000, 0xF0000001);
y = Int64Create(0x00000000, 0x00000002);
AssertEquals("0xF0000001 + 0x00000002", Int64Add(x, y), Int64Create(0x00000000, 0xF0000003));
x = Int64Create(0x00000000, 0xF0000000);
y = Int64Create(0x00000000, 0x10000000);
AssertEquals("0xF0000000 + 0x10000000", Int64Add(x, y), Int64Create(0x00000001, 0x00000000));
x = Int64Create(0, 5);
y = Int64Neg(Int64Create(0, 10));
AssertEquals("5 + (-10)", Int64Add(x, y), Int64Neg(Int64Create(0, 5)));
/* SUB Tests */
x = Int64Create(0x00000000, 0x00000005);
y = Int64Create(0x00000000, 0x00000002);
AssertEquals("0x00000005 - 0x00000002", Int64Sub(x, y), Int64Create(0x00000000, 0x00000003));
x = Int64Create(0x00000001, 0x00000000);
y = Int64Create(0x00000000, 0x00000001);
AssertEquals("0x00000001 0x00000000 - 0x00000001", Int64Sub(x, y), Int64Create(0x00000000, 0xFFFFFFFF));
x = Int64Create(0, 5);
y = Int64Create(0, 10);
AssertEquals("5 - 10", Int64Sub(x, y), Int64Neg(Int64Create(0, 5)));
x = Int64Create(0, 5);
y = Int64Neg(Int64Create(0, 10));
AssertEquals("5 - (-10)", Int64Sub(x, y), Int64Create(0, 15));
/* MUL Tests */
x = Int64Create(0, 18);
y = Int64Create(0, 1);
AssertEquals("18 * 1", Int64Mul(x, y), Int64Create(0, 18));
x = Int64Create(0, 20);
y = Int64Create(0, 12);
AssertEquals("20 * 12", Int64Mul(x, y), Int64Create(0, 240));
x = Int64Create(0x00000000, 0x00000005);
y = Int64Create(0x00000000, 0x00000005);
AssertEquals("0x00000005 * 0x00000005", Int64Mul(x, y), Int64Create(0x00000000, 0x00000019));
x = Int64Create(0x00000001, 0x00000000);
y = Int64Create(0x00000000, 0x00000005);
AssertEquals("0x00000001 0x00000000 * 0x00000005", Int64Mul(x, y), Int64Create(0x00000005, 0x00000000));
/* DIV Tests */
x = Int64Create(0, 12);
y = Int64Create(0, 4);
AssertEquals("12 / 4", Int64Div(x, y), Int64Create(0, 3));
/* MOD Tests */
x = Int64Create(0x000000FF, 0x00000000);
y = Int64Create(0x00000000, 0x00000010);
AssertEquals("0x000000FF 0x00000000 mod 0x00000010", Int64Rem(x, y), Int64Create(0, 0));
x = Int64Create(0x00000000, 0xFF000000);
y = Int64Create(0x00000000, 0x00000010);
AssertEquals("0x00000000 0xFF000000 mod 0x00000010", Int64Rem(x, y), Int64Create(0, 0));
x = Int64Create(0xFF000000, 0x00000000);
y = Int64Create(0x00000000, 0x00000010);
AssertEquals("0xFF000000 0x00000000 mod 0x00000010", Int64Rem(x, y), Int64Create(0, 0));
x = Int64Create(0x00000000, 0x000000F0);
y = Int64Create(0x00000000, 0x00000010);
AssertEquals("0x00000000 0x000000F0 mod 0x00000010", Int64Rem(x, y), Int64Create(0, 0));
/* TODO: Add more tests for negative multiplication, division, and
* mod */
return 0;
}

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files * obtaining a copy of this software and associated documentation files
@ -25,7 +25,6 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <ctype.h> #include <ctype.h>
#include <stdbool.h>
#include <Log.h> #include <Log.h>
#include <Array.h> #include <Array.h>
@ -310,8 +309,6 @@ Main(Array * args)
ArrayAdd(requiredTypes, StrDuplicate(type)); ArrayAdd(requiredTypes, StrDuplicate(type));
} }
if (StrEquals(typeType, "struct"))
{
typeFieldsVal = HashMapGet(typeObj, "fields"); typeFieldsVal = HashMapGet(typeObj, "fields");
if (JsonValueType(typeFieldsVal) != JSON_OBJECT) if (JsonValueType(typeFieldsVal) != JSON_OBJECT)
{ {
@ -321,12 +318,13 @@ Main(Array * args)
typeFields = JsonValueAsObject(typeFieldsVal); typeFields = JsonValueAsObject(typeFieldsVal);
if (StrEquals(typeType, "struct"))
{
while (HashMapIterate(typeFields, &fieldName, (void **) &fieldVal)) while (HashMapIterate(typeFields, &fieldName, (void **) &fieldVal))
{ {
char *fieldType; char *fieldType;
bool isArrType = false; int isArrType = 0;
JsonValue *requiredVal; JsonValue *requiredVal;
JsonValue *ignoreVal;
if (JsonValueType(fieldVal) != JSON_OBJECT) if (JsonValueType(fieldVal) != JSON_OBJECT)
{ {
@ -347,7 +345,7 @@ Main(Array * args)
{ {
fieldType++; fieldType++;
fieldType[strlen(fieldType) - 1] = '\0'; fieldType[strlen(fieldType) - 1] = '\0';
isArrType = true; isArrType = 1;
} }
if (!StrEquals(fieldType, "object") && if (!StrEquals(fieldType, "object") &&
@ -381,26 +379,10 @@ Main(Array * args)
Log(LOG_ERR, "Validation error: 'types.%s.fields.%s.required' must be a boolean.", type, fieldName); Log(LOG_ERR, "Validation error: 'types.%s.fields.%s.required' must be a boolean.", type, fieldName);
goto finish; goto finish;
} }
ignoreVal = HashMapGet(fieldObj, "ignore");
if (ignoreVal && JsonValueType(ignoreVal) != JSON_BOOLEAN)
{
Log(LOG_ERR, "Validation error: 'types.%s.fields.%s.ignore' must be a boolean.", type, fieldName);
goto finish;
}
} }
} }
else if (StrEquals(typeType, "enum")) else if (StrEquals(typeType, "enum"))
{ {
typeFieldsVal = HashMapGet(typeObj, "fields");
if (JsonValueType(typeFieldsVal) != JSON_OBJECT)
{
Log(LOG_ERR, "Validation error: 'types.%s.fields' must be an object.", type);
goto finish;
}
typeFields = JsonValueAsObject(typeFieldsVal);
while (HashMapIterate(typeFields, &fieldName, (void **) &fieldVal)) while (HashMapIterate(typeFields, &fieldName, (void **) &fieldVal))
{ {
char *name; char *name;
@ -421,17 +403,16 @@ Main(Array * args)
} }
} }
} }
else if (StrEquals(typeType, "extern"))
{
/*
* No code will be generated for this type. We simply assume that it exists.
*/
}
else else
{ {
Log(LOG_ERR, "Validation error: 'types.%s.type' must be 'struct' or 'enum'.", type); Log(LOG_ERR, "Validation error: 'types.%s.type' must be 'struct' or 'enum'.", type);
goto finish; goto finish;
} }
/*
* TODO: Add "extern" type that doesn't actually generate any code,
* but trusts the user that it has been generated somewhere else. This
* is effectively "importing" types.
*/
} }
sortedNodes = GraphTopologicalSort(dependencyGraph, &sortedNodesLen); sortedNodes = GraphTopologicalSort(dependencyGraph, &sortedNodesLen);
@ -460,11 +441,9 @@ Main(Array * args)
StreamPrintf(headerFile, "#ifndef %s\n", guard); StreamPrintf(headerFile, "#ifndef %s\n", guard);
StreamPrintf(headerFile, "#define %s\n\n", guard); StreamPrintf(headerFile, "#define %s\n\n", guard);
StreamPrintf(headerFile, "#include <stdint.h>\n"); StreamPrintf(headerFile, "#include <Array.h>\n");
StreamPrintf(headerFile, "#include <stdbool.h>\n"); StreamPrintf(headerFile, "#include <HashMap.h>\n");
StreamPrintf(headerFile, "#include <Int64.h>\n");
StreamPrintf(headerFile, "#include <Cytoplasm/Array.h>\n");
StreamPrintf(headerFile, "#include <Cytoplasm/HashMap.h>\n");
StreamPutc(headerFile, '\n'); StreamPutc(headerFile, '\n');
@ -492,12 +471,6 @@ Main(Array * args)
} }
typeType = JsonValueAsString(JsonGet(types, 2, type, "type")); typeType = JsonValueAsString(JsonGet(types, 2, type, "type"));
if (StrEquals(typeType, "extern"))
{
continue;
}
fields = JsonValueAsObject(JsonGet(types, 2, type, "fields")); fields = JsonValueAsObject(JsonGet(types, 2, type, "fields"));
StreamPrintf(headerFile, "typedef %s %s\n{\n", typeType, type); StreamPrintf(headerFile, "typedef %s %s\n{\n", typeType, type);
@ -519,11 +492,11 @@ Main(Array * args)
} }
else if (StrEquals(fieldType, "integer")) else if (StrEquals(fieldType, "integer"))
{ {
cType = "int64_t"; cType = "Int64";
} }
else if (StrEquals(fieldType, "boolean")) else if (StrEquals(fieldType, "boolean"))
{ {
cType = "bool"; cType = "int";
} }
else if (StrEquals(fieldType, "float")) else if (StrEquals(fieldType, "float"))
{ {
@ -610,9 +583,9 @@ Main(Array * args)
StreamPrintf(implFile, "/* Generated by j2s */\n\n"); StreamPrintf(implFile, "/* Generated by j2s */\n\n");
StreamPrintf(implFile, "#include <%s>\n\n", headerName); StreamPrintf(implFile, "#include <%s>\n\n", headerName);
StreamPrintf(implFile, "#include <Cytoplasm/Memory.h>\n"); StreamPrintf(implFile, "#include <Memory.h>\n");
StreamPrintf(implFile, "#include <Cytoplasm/Json.h>\n"); StreamPrintf(implFile, "#include <Json.h>\n");
StreamPrintf(implFile, "#include <Cytoplasm/Str.h>\n"); StreamPrintf(implFile, "#include <Str.h>\n");
StreamPutc(implFile, '\n'); StreamPutc(implFile, '\n');
@ -625,8 +598,8 @@ Main(Array * args)
if (StrEquals(typeType, "struct")) if (StrEquals(typeType, "struct"))
{ {
StreamPrintf(headerFile, "extern bool %sFromJson(HashMap *, %s *, char **);\n", type, type); StreamPrintf(headerFile, "extern int %sFromJson(HashMap *, %s *, char **);\n", type, type);
StreamPrintf(implFile, "bool\n%sFromJson(HashMap *json, %s *out, char **errp)\n{\n", type, type); StreamPrintf(implFile, "int\n%sFromJson(HashMap *json, %s *out, char **errp)\n{\n", type, type);
StreamPrintf(implFile, " JsonValue *val;\n"); StreamPrintf(implFile, " JsonValue *val;\n");
StreamPrintf(implFile, " int enumParseRes;\n"); StreamPrintf(implFile, " int enumParseRes;\n");
StreamPrintf(implFile, "\n"); StreamPrintf(implFile, "\n");
@ -635,43 +608,32 @@ Main(Array * args)
StreamPrintf(implFile, " if (!json | !out)\n" StreamPrintf(implFile, " if (!json | !out)\n"
" {\n" " {\n"
" *errp = \"Invalid pointers passed to %sFromJson()\";\n" " *errp = \"Invalid pointers passed to %sFromJson()\";\n"
" return false;\n" " return 0;\n"
" }\n\n" " }\n\n"
,type); ,type);
for (i = 0; i < ArraySize(keys); i++) for (i = 0; i < ArraySize(keys); i++)
{ {
char *key = ArrayGet(keys, i); char *key = ArrayGet(keys, i);
bool required = JsonValueAsBoolean(JsonGet(fields, 2, key, "required")); int required = JsonValueAsBoolean(JsonGet(fields, 2, key, "required"));
bool ignore = JsonValueAsBoolean(JsonGet(fields, 2, key, "ignore"));
char *fieldType = JsonValueAsString(JsonGet(fields, 2, key, "type")); char *fieldType = JsonValueAsString(JsonGet(fields, 2, key, "type"));
int isEnum = StrEquals(JsonValueAsString(JsonGet(types, 2, fieldType, "type")), "enum"); int isEnum = StrEquals(JsonValueAsString(JsonGet(types, 2, fieldType, "type")), "enum");
JsonType jsonType = isEnum ? JSON_STRING : TypeToJsonType(fieldType); JsonType jsonType = isEnum ? JSON_STRING : TypeToJsonType(fieldType);
char *jsonTypeStr = JsonTypeToStr(jsonType); char *jsonTypeStr = JsonTypeToStr(jsonType);
if (ignore)
{
StreamPrintf(implFile, " /* Ignored field: %s */\n\n", key);
continue;
}
StreamPrintf(implFile, " val = HashMapGet(json, \"%s\");\n", Trim('_', key)); StreamPrintf(implFile, " val = HashMapGet(json, \"%s\");\n", Trim('_', key));
StreamPrintf(implFile, " if (val)\n {\n"); StreamPrintf(implFile, " if (val)\n {\n");
StreamPrintf(implFile, " if (JsonValueType(val) != %s)\n {\n", jsonTypeStr); 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, " *errp = \"%s.%s must be of type %s.\";\n", type, Trim('_', key), fieldType);
StreamPrintf(implFile, " return false;\n"); StreamPrintf(implFile, " return 0;\n");
StreamPrintf(implFile, " }\n\n"); StreamPrintf(implFile, " }\n\n");
if (StrEquals(fieldType, "array")) if (StrEquals(fieldType, "array"))
{ {
StreamPrintf(implFile, " val = JsonValueDuplicate(val);\n"); StreamPrintf(implFile, " out->%s = JsonValueAsArray(JsonValueDuplicate(val));\n", key);
StreamPrintf(implFile, " out->%s = JsonValueAsArray(val);\n", key);
StreamPrintf(implFile, " Free(val); /* Not JsonValueFree() because we want the inner value. */\n");
} }
else if (StrEquals(fieldType, "object")) else if (StrEquals(fieldType, "object"))
{ {
StreamPrintf(implFile, " val = JsonValueDuplicate(val);\n"); StreamPrintf(implFile, " out->%s = JsonValueAsObject(JsonValueDuplicate(val));\n", key);
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 (*fieldType == '[' && fieldType[strlen(fieldType) - 1] == ']')
{ {
@ -684,7 +646,7 @@ Main(Array * args)
StreamPrintf(implFile, " if (!out->%s)\n", key); StreamPrintf(implFile, " if (!out->%s)\n", key);
StreamPrintf(implFile, " {\n"); StreamPrintf(implFile, " {\n");
StreamPrintf(implFile, " *errp = \"Failed to allocate memory for %s.%s.\";\n", type, key); StreamPrintf(implFile, " *errp = \"Failed to allocate memory for %s.%s.\";\n", type, key);
StreamPrintf(implFile, " return false;\n"); StreamPrintf(implFile, " return 0;\n");
StreamPrintf(implFile, " }\n"); StreamPrintf(implFile, " }\n");
StreamPrintf(implFile, " else\n"); StreamPrintf(implFile, " else\n");
StreamPrintf(implFile, " {\n"); StreamPrintf(implFile, " {\n");
@ -703,7 +665,7 @@ Main(Array * args)
if (StrEquals(fieldType, "integer")) if (StrEquals(fieldType, "integer"))
{ {
cType = "int64_t"; cType = "Int64";
} }
else if (StrEquals(fieldType, "float")) else if (StrEquals(fieldType, "float"))
{ {
@ -711,7 +673,7 @@ Main(Array * args)
} }
else if (StrEquals(fieldType, "boolean")) else if (StrEquals(fieldType, "boolean"))
{ {
cType = "bool"; cType = "int";
} }
else else
{ {
@ -725,13 +687,13 @@ Main(Array * args)
StreamPrintf(implFile, " if (JsonValueType(v) != %s)\n", JsonTypeToStr(jsonType)); StreamPrintf(implFile, " if (JsonValueType(v) != %s)\n", JsonTypeToStr(jsonType));
StreamPrintf(implFile, " {\n"); StreamPrintf(implFile, " {\n");
StreamPrintf(implFile, " *errp = \"%s.%s[] contains an invalid value.\";\n", type, key); StreamPrintf(implFile, " *errp = \"%s.%s[] contains an invalid value.\";\n", type, key);
StreamPrintf(implFile, " return false;\n"); StreamPrintf(implFile, " return 0;\n");
StreamPrintf(implFile, " }\n"); StreamPrintf(implFile, " }\n");
StreamPrintf(implFile, " ref = Malloc(sizeof(%s));\n", cType); StreamPrintf(implFile, " ref = Malloc(sizeof(%s));\n", cType);
StreamPrintf(implFile, " if (!ref)\n"); StreamPrintf(implFile, " if (!ref)\n");
StreamPrintf(implFile, " {\n"); StreamPrintf(implFile, " {\n");
StreamPrintf(implFile, " *errp = \"Unable to allocate memory for array value.\";\n"); StreamPrintf(implFile, " *errp = \"Unable to allocate memory for array value.\";\n");
StreamPrintf(implFile, " return false;\n"); StreamPrintf(implFile, " return 0;\n");
StreamPrintf(implFile, " }\n"); StreamPrintf(implFile, " }\n");
StreamPrintf(implFile, " *ref = JsonValueAs%s(v);\n", fieldType); StreamPrintf(implFile, " *ref = JsonValueAs%s(v);\n", fieldType);
StreamPrintf(implFile, " ArrayAdd(out->%s, ref);\n", key); StreamPrintf(implFile, " ArrayAdd(out->%s, ref);\n", key);
@ -743,7 +705,7 @@ Main(Array * args)
StreamPrintf(implFile, " if (JsonValueType(v) != %s)\n", JsonTypeToStr(jsonType)); StreamPrintf(implFile, " if (JsonValueType(v) != %s)\n", JsonTypeToStr(jsonType));
StreamPrintf(implFile, " {\n"); StreamPrintf(implFile, " {\n");
StreamPrintf(implFile, " *errp = \"%s.%s[] contains an invalid value.\";\n", type, key); StreamPrintf(implFile, " *errp = \"%s.%s[] contains an invalid value.\";\n", type, key);
StreamPrintf(implFile, " return false;\n"); StreamPrintf(implFile, " return 0;\n");
StreamPrintf(implFile, " }\n"); StreamPrintf(implFile, " }\n");
StreamPrintf(implFile, " ArrayAdd(out->%s, StrDuplicate(JsonValueAsString(v)));\n", key); StreamPrintf(implFile, " ArrayAdd(out->%s, StrDuplicate(JsonValueAsString(v)));\n", key);
} }
@ -752,7 +714,7 @@ Main(Array * args)
StreamPrintf(implFile, " if (JsonValueType(v) != %s)\n", JsonTypeToStr(jsonType)); StreamPrintf(implFile, " if (JsonValueType(v) != %s)\n", JsonTypeToStr(jsonType));
StreamPrintf(implFile, " {\n"); StreamPrintf(implFile, " {\n");
StreamPrintf(implFile, " *errp = \"%s.%s[] contains an invalid value.\";\n", type, key); StreamPrintf(implFile, " *errp = \"%s.%s[] contains an invalid value.\";\n", type, key);
StreamPrintf(implFile, " return false;\n"); StreamPrintf(implFile, " return 0;\n");
StreamPrintf(implFile, " }\n"); StreamPrintf(implFile, " }\n");
StreamPrintf(implFile, " ArrayAdd(out->%s, JsonDuplicate(JsonValueAsObject(v)));\n", key); StreamPrintf(implFile, " ArrayAdd(out->%s, JsonDuplicate(JsonValueAsObject(v)));\n", key);
} }
@ -766,13 +728,13 @@ Main(Array * args)
StreamPrintf(implFile, " if (JsonValueType(v) != %s)\n", JsonTypeToStr(jsonType)); StreamPrintf(implFile, " if (JsonValueType(v) != %s)\n", JsonTypeToStr(jsonType));
StreamPrintf(implFile, " {\n"); StreamPrintf(implFile, " {\n");
StreamPrintf(implFile, " *errp = \"%s.%s[] contains an invalid value.\";\n", type, key); StreamPrintf(implFile, " *errp = \"%s.%s[] contains an invalid value.\";\n", type, key);
StreamPrintf(implFile, " return false;\n"); StreamPrintf(implFile, " return 0;\n");
StreamPrintf(implFile, " }\n"); StreamPrintf(implFile, " }\n");
StreamPrintf(implFile, " parsed = Malloc(sizeof(%s));\n", fieldType); StreamPrintf(implFile, " parsed = Malloc(sizeof(%s));\n", fieldType);
StreamPrintf(implFile, " if (!parsed)\n"); StreamPrintf(implFile, " if (!parsed)\n");
StreamPrintf(implFile, " {\n"); StreamPrintf(implFile, " {\n");
StreamPrintf(implFile, " *errp = \"Unable to allocate memory for array value.\";\n"); StreamPrintf(implFile, " *errp = \"Unable to allocate memory for array value.\";\n");
StreamPrintf(implFile, " return false;\n"); StreamPrintf(implFile, " return 0;\n");
StreamPrintf(implFile, " }\n"); StreamPrintf(implFile, " }\n");
if (isEnum) if (isEnum)
{ {
@ -790,7 +752,7 @@ Main(Array * args)
StreamPrintf(implFile, " %sFree(parsed);\n", fieldType); StreamPrintf(implFile, " %sFree(parsed);\n", fieldType);
} }
StreamPrintf(implFile, " Free(parsed);\n"); StreamPrintf(implFile, " Free(parsed);\n");
StreamPrintf(implFile, " return false;\n"); StreamPrintf(implFile, " return 0;\n");
StreamPrintf(implFile, " }\n"); StreamPrintf(implFile, " }\n");
StreamPrintf(implFile, " ArrayAdd(out->%s, parsed);\n", key); StreamPrintf(implFile, " ArrayAdd(out->%s, parsed);\n", key);
} }
@ -803,7 +765,7 @@ Main(Array * args)
else if (jsonType == JSON_OBJECT) else if (jsonType == JSON_OBJECT)
{ {
StreamPrintf(implFile, " if (!%sFromJson(JsonValueAsObject(val), &out->%s, errp))\n {\n", fieldType, key); StreamPrintf(implFile, " if (!%sFromJson(JsonValueAsObject(val), &out->%s, errp))\n {\n", fieldType, key);
StreamPrintf(implFile, " return false;\n"); StreamPrintf(implFile, " return 0;\n");
StreamPrintf(implFile, " }\n"); StreamPrintf(implFile, " }\n");
} }
else else
@ -838,7 +800,7 @@ Main(Array * args)
StreamPrintf(implFile, " if (enumParseRes == -1)\n", key); StreamPrintf(implFile, " if (enumParseRes == -1)\n", key);
StreamPrintf(implFile, " {\n"); StreamPrintf(implFile, " {\n");
StreamPrintf(implFile, " *errp = \"Invalid value for %s.%s.\";\n", type, key); StreamPrintf(implFile, " *errp = \"Invalid value for %s.%s.\";\n", type, key);
StreamPrintf(implFile, " return false;\n"); StreamPrintf(implFile, " return 0;\n");
StreamPrintf(implFile, " }\n"); StreamPrintf(implFile, " }\n");
StreamPrintf(implFile, " out->%s = enumParseRes;\n", key); StreamPrintf(implFile, " out->%s = enumParseRes;\n", key);
} }
@ -858,13 +820,13 @@ Main(Array * args)
{ {
StreamPrintf(implFile, " else\n {\n"); StreamPrintf(implFile, " else\n {\n");
StreamPrintf(implFile, " *errp = \"%s.%s is required.\";\n", type, key); StreamPrintf(implFile, " *errp = \"%s.%s is required.\";\n", type, key);
StreamPrintf(implFile, " return false;\n"); StreamPrintf(implFile, " return 0;\n");
StreamPrintf(implFile, " }\n"); StreamPrintf(implFile, " }\n");
} }
StreamPutc(implFile, '\n'); StreamPutc(implFile, '\n');
} }
StreamPrintf(implFile, " return true;\n"); StreamPrintf(implFile, " return 1;\n");
StreamPrintf(implFile, "}\n\n"); StreamPrintf(implFile, "}\n\n");
StreamPrintf(headerFile, "extern HashMap * %sToJson(%s *);\n", type, type); StreamPrintf(headerFile, "extern HashMap * %sToJson(%s *);\n", type, type);
@ -884,14 +846,7 @@ Main(Array * args)
{ {
char *key = ArrayGet(keys, i); char *key = ArrayGet(keys, i);
char *fieldType = JsonValueAsString(JsonGet(fields, 2, key, "type")); char *fieldType = JsonValueAsString(JsonGet(fields, 2, key, "type"));
bool isEnum = StrEquals(JsonValueAsString(JsonGet(types, 2, fieldType, "type")), "enum"); int isEnum = StrEquals(JsonValueAsString(JsonGet(types, 2, fieldType, "type")), "enum");
bool ignore = JsonValueAsBoolean(JsonGet(fields, 2, key, "ignore"));
if (ignore)
{
StreamPrintf(implFile, " /* Ignored field: %s */\n\n", key);
continue;
}
if (StrEquals(fieldType, "array")) if (StrEquals(fieldType, "array"))
{ {
@ -953,7 +908,7 @@ Main(Array * args)
if (StrEquals(fieldType, "integer")) if (StrEquals(fieldType, "integer"))
{ {
cType = "int64_t"; cType = "Int64";
} }
else if (StrEquals(fieldType, "float")) else if (StrEquals(fieldType, "float"))
{ {
@ -961,7 +916,7 @@ Main(Array * args)
} }
else if (StrEquals(fieldType, "boolean")) else if (StrEquals(fieldType, "boolean"))
{ {
cType = "bool"; cType = "int";
} }
else else
{ {
@ -1067,9 +1022,8 @@ Main(Array * args)
else else
{ {
/* Ignore primitives but call the appropriate free /* Ignore primitives but call the appropriate free
* method on declared types that aren't "extern". */ * method on declared types */
char *fieldTypeType = JsonValueAsString(JsonGet(types, 2, fieldType, "type")); if (!isEnum && HashMapGet(types, fieldType))
if (!isEnum && HashMapGet(types, fieldType) && !StrEquals(fieldTypeType, "extern"))
{ {
StreamPrintf(implFile, " %sFree(&val->%s);\n", fieldType, key); StreamPrintf(implFile, " %sFree(&val->%s);\n", fieldType, key);
} }

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files * obtaining a copy of this software and associated documentation files

119
tools/uint64.c Normal file
View file

@ -0,0 +1,119 @@
#include <UInt64.h>
#include <Log.h>
/* AssertEquals(actual, expected) */
int
AssertEquals(char *msg, UInt64 x, UInt64 y)
{
if (!UInt64Eq(x, y))
{
Log(LOG_ERR, "%s: Expected 0x%X 0x%X, got 0x%X 0x%X", msg,
UInt64High(y), UInt64Low(y),
UInt64High(x), UInt64Low(x));
return 0;
}
return 1;
}
int
Main(void)
{
UInt64 x, y;
Log(LOG_INFO, "sizeof(UInt64) = %lu", sizeof(UInt64));
#ifdef UINT64_NATIVE
Log(LOG_INFO, "Using native 64-bit integers.");
#else
Log(LOG_INFO, "Using emulated 64-bit integers.");
#endif
/* BSR Tests */
x = UInt64Create(0x000000FF, 0x00000000);
y = UInt64Srl(x, 4);
AssertEquals("x >> 4", y, UInt64Create(0x0000000F, 0xF0000000));
y = UInt64Srl(x, 8);
AssertEquals("x >> 8", y, UInt64Create(0x00000000, 0xFF000000));
y = UInt64Srl(x, 36);
AssertEquals("x >> 36", y, UInt64Create(0x00000000, 0x0000000F));
/* BSL Tests */
x = UInt64Create(0x00000000, 0xFF000000);
y = UInt64Sll(x, 4);
AssertEquals("x << 4", y, UInt64Create(0x0000000F, 0xF0000000));
y = UInt64Sll(x, 8);
AssertEquals("x << 8", y, UInt64Create(0x000000FF, 0x00000000));
y = UInt64Sll(x, 36);
AssertEquals("x << 36", y, UInt64Create(0xF0000000, 0x00000000));
/* ADD Tests */
x = UInt64Create(0x00000000, 0xF0000001);
y = UInt64Create(0x00000000, 0x00000002);
AssertEquals("0xF0000001 + 0x00000002", UInt64Add(x, y), UInt64Create(0x00000000, 0xF0000003));
x = UInt64Create(0x00000000, 0xF0000000);
y = UInt64Create(0x00000000, 0x10000000);
AssertEquals("0xF0000000 + 0x10000000", UInt64Add(x, y), UInt64Create(0x00000001, 0x00000000));
/* SUB Tests */
x = UInt64Create(0x00000000, 0x00000005);
y = UInt64Create(0x00000000, 0x00000002);
AssertEquals("0x00000005 - 0x00000002", UInt64Sub(x, y), UInt64Create(0x00000000, 0x00000003));
x = UInt64Create(0x00000001, 0x00000000);
y = UInt64Create(0x00000000, 0x00000001);
AssertEquals("0x00000001 0x00000000 - 0x00000001", UInt64Sub(x, y), UInt64Create(0x00000000, 0xFFFFFFFF));
/* MUL Tests */
x = UInt64Create(0, 18);
y = UInt64Create(0, 1);
AssertEquals("18 * 1", UInt64Mul(x, y), UInt64Create(0, 18));
x = UInt64Create(0, 20);
y = UInt64Create(0, 12);
AssertEquals("20 * 12", UInt64Mul(x, y), UInt64Create(0, 240));
x = UInt64Create(0x00000000, 0x00000005);
y = UInt64Create(0x00000000, 0x00000005);
AssertEquals("0x00000005 * 0x00000005", UInt64Mul(x, y), UInt64Create(0x00000000, 0x00000019));
x = UInt64Create(0x00000001, 0x00000000);
y = UInt64Create(0x00000000, 0x00000005);
AssertEquals("0x00000001 0x00000000 * 0x00000005", UInt64Mul(x, y), UInt64Create(0x00000005, 0x00000000));
/* DIV Tests */
x = UInt64Create(0, 12);
y = UInt64Create(0, 4);
AssertEquals("12 / 4", UInt64Div(x, y), UInt64Create(0, 3));
/* MOD Tests */
x = UInt64Create(0x000000FF, 0x00000000);
y = UInt64Create(0x00000000, 0x00000010);
AssertEquals("0x000000FF 0x00000000 mod 0x00000010", UInt64Rem(x, y), UInt64Create(0, 0));
x = UInt64Create(0x00000000, 0xFF000000);
y = UInt64Create(0x00000000, 0x00000010);
AssertEquals("0x00000000 0xFF000000 mod 0x00000010", UInt64Rem(x, y), UInt64Create(0, 0));
x = UInt64Create(0xFF000000, 0x00000000);
y = UInt64Create(0x00000000, 0x00000010);
AssertEquals("0xFF000000 0x00000000 mod 0x00000010", UInt64Rem(x, y), UInt64Create(0, 0));
x = UInt64Create(0x00000000, 0x000000F0);
y = UInt64Create(0x00000000, 0x00000010);
AssertEquals("0x00000000 0x000000F0 mod 0x00000010", UInt64Rem(x, y), UInt64Create(0, 0));
return 0;
}