Compare commits

...

3 Commits

@ -0,0 +1,51 @@
# libneo
libneo is an alternative standard library that wraps around libc.
It comes with an entirely refactored API that is easier and safer
to use than traditional POSIX. Key features include:
- Sane(ish), standardized error handling
- Minimal undefined behavior
- Significantly less boilerplate
- Memory safe(ish)
- Runs on most POSIX compliant systems (tested on FreeBSD and Linux)
- No Windows support
## Usage
Please note that this project is still in its early development stage.
APIs are subject to arbitrary change without prior notice.
### Dependencies
- A libc implementing at least POSIX.1-2008
- clang (recommended) or GCC
- CMake >= 3.14
- git
### CMake
libneo is distributed as a static library and can be directly included
in a CMake project, e.g. using CMake's `FetchContent` module:
```cmake
include(FetchContent)
FetchContent_Declare(
neo
GIT_REPOSITORY https://git.bsd.gay/fef/libneo.git
)
FetchContent_MakeAvailable(neo)
target_link_libraries(${PROJECT_NAME} PRIVATE neo)
```
## License
Copyright &copy; 2021 Fefie <owo@fef.moe>
libneo is non-violent software: you may only use, redistribute,
and/or modify it under the terms of the CNPLv6+ as found in the
LICENSE file or at <https://git.pixie.town/thufie/CNPL>.
libneo comes with ABSOLUTELY NO WARRANTY, to the extent permitted
by applicable law. See the CNPLv6+ for details.

File diff suppressed because it is too large Load Diff

@ -1,4 +1,8 @@
/** See the end of this file for copyright and license terms. */
/* See the end of this file for copyright and license terms. */
/**
* @file Main libneo header file providing core functionalities.
*/
#pragma once

@ -1,4 +1,4 @@
/** See the end of this file for copyright and license terms. */
/* See the end of this file for copyright and license terms. */
#pragma once
@ -6,46 +6,56 @@
#include "neo/_types.h"
/**
* Throw an error. If `err` is `nil`, the program halts.
* Functions accepting an `err` object must call either `yeet` or `neat`
* @defgroup error Error Handling
*
* @{
*/
/**
* @brief Throw an error. If `err` is `nil`, the program halts.
*
* Functions accepting an `error` object must call either `yeet()` or `neat()`
* on the object exactly once, or the behavior is undefined.
* If a function yeets an error, its return value is undefined.
*
* @param err: The error pointer passed to the callee
* @param number: An error number appropriate for the condition,
* @param err The error pointer passed to the callee
* @param number An error number appropriate for the condition,
* usually one from `errno.h`
* @param fmt: If non nil, a printf-style format string followed by
* @param fmt If non `nil`, a printf-style format string followed by
* the values to insert which will become the error message.
*/
__attribute__(( __format__(printf, 3, 4) ))
void yeet(error *err, u32 number, const char *restrict fmt, ...);
void yeet(error *err, u32 number, const char *restrict fmt, ...)
__attribute__(( __format__(printf, 3, 4) ));
/**
* Indicate an operation has completed successfully.
* Functions accepting an `error` pointer must call either `yeet` or `neat`,
* Functions accepting an `error` pointer must call either `yeet()` or `neat()`,
* or the begavior is undefined.
*
* @param err: Error object
* @param err Error object
*/
void neat(error *err);
/**
* Release any resources allocated for an error.
* @brief Release any resources allocated for an error.
*
* This must always be called within the catch block of the outermost calling
* function, i.e. the one that declared the error variable (not the ones just
* accepting an error * argument and passing it through to further calls).
* accepting an `error *` argument and passing it through to further calls).
*
* Put another way, you probably need to call this if you had to put an `&`
* before the error variable for passing it into `catch`.
* before the error variable for passing it into `catch()`.
*
* @param err: Error object to destroy
* @param err Error object to destroy
*/
void errput(error *err);
/**
* @brief Catch an error.
*
* Execute the expression after this macro call if the error pointed to
* by `err` is an actual error, i.e. it has been `yeet`ed to.
* Resources for the error must be released using `nput`.
* by `err` is an actual error, i.e. it has been `yeet()`ed to.
* Resources for the error must be released using `nput()`.
*/
#define ncatch(err) if ((err) != nil && (err)->_number != 0)
#ifndef __cplusplus
@ -53,17 +63,27 @@ void errput(error *err);
#endif
/**
* Get the error number.
* Must only be used within a catch block and before `errput` is called.
* @brief Get the error number.
*
* Must only be used within a catch block and before `errput()` is called.
*
* @param err `error *` to get the number of
* @returns The error number
*/
#define errnum(err) ((err) == nil ? 0 : (err)->_number)
/**
* Get an optional error message, this may be `nil`
* Must only be used within a catch block and before `errput` is called.
* @brief Get an optional error message, this may be `nil`.
*
* Must only be used within a catch block and before `errput()` is called.
*
* @param err `error *` to get the message of
* @returns The error message, may be `nil`
*/
#define errmsg(err) ((err) == nil ? (nstr_t *)nil : (err)->_message)
/** @} */
/*
* This file is part of libneo.
* Copyright (c) 2021 Fefie <owo@fef.moe>.

@ -1,35 +1,44 @@
/** See the end of this file for copyright and license terms. */
/* See the end of this file for copyright and license terms. */
#pragma once
#include "neo/_toolchain.h"
#include "neo/_types.h"
/**
* @defgroup nalloc Memory Management
*
* @{
*/
/**
* @brief Release memory.
*
* @param ptr Pointer returned by `nalloc()` or `nzalloc()`
*/
void nfree(void *ptr);
/**
* Allocate `size` bytes of memory and return a pointer to the memory region.
* The memory is *not* initialized; use `nzalloc` if you want it to be.
* The memory is *not* initialized; use `nzalloc()` if you want it to be.
* If `size` is 0, the allocation fails.
* If the allocation fails, the error is set and `nil` is returned.
*
* @param size: Desired memory size in bytes
* @param err: Error object
* @param size Desired memory size in bytes
* @param err Error object
*/
__neo_malloc(nfree, 1)
void *nalloc(usize size, error *err);
void *nalloc(usize size, error *err) __neo_malloc(nfree, 1);
/**
* Allocate `size` bytes of memory and return a pointer to the memory region.
* The memory is initialized to zeroes; use `nalloc` if you don't want that.
* The memory is initialized to zeroes; use `nalloc()` if you don't want that.
* If `size` is 0, the allocation fails.
* If the allocation fails, the error is set and `nil` is returned.
*
* @param size: Desired memory size in bytes
* @param err: Error object
* @param size Desired memory size in bytes
* @param err Error object
*/
__neo_malloc(nfree, 1)
void *nzalloc(usize size, error *err);
void *nzalloc(usize size, error *err) __neo_malloc(nfree, 1);
/**
* Resize an allocated memory region to fit at least `newsize` bytes and return
@ -38,13 +47,14 @@ void *nzalloc(usize size, error *err);
* If `ptr` is `nil`, the function behaves like `nalloc`.
* If allocation fails, an error is yeeted.
*
* @param ptr: The pointer to the memory region to be expanded,
* as returned by `nalloc` or `nzalloc`.
* @param newsize: The new size.
* @param err: Error object
* @param ptr The pointer to the memory region to be expanded,
* as returned by `nalloc()` or `nzalloc()`
* @param newsize The new size
* @param err Error object
*/
__neo_malloc(nfree, 1)
void *nrealloc(void *ptr, usize newsize, error *err);
void *nrealloc(void *ptr, usize newsize, error *err) __neo_malloc(nfree, 1);
/** @} */
/*
* This file is part of libneo.

@ -1,4 +1,4 @@
/** See the end of this file for copyright and license terms. */
/* See the end of this file for copyright and license terms. */
#pragma once
@ -6,72 +6,78 @@
#include "neo/_types.h"
/**
* Create a new buffer of fixed size.
* @defgroup nbuf Buffers
*
* @{
*/
/**
* @brief Create a new buffer of fixed size.
*
* If allocation fails or `size` is 0, an error is yeeted.
*
* @param size: Size in bytes
* @param err: Error pointer
* @param size Size in bytes
* @param err Error pointer
* @returns The buffer, except if an error occurred
*/
nbuf_t *nbuf_create(usize size, error *err);
/**
* Create a new buffer of fixed size and copy `data` into it.
* @brief Create a new buffer of fixed size and copy `data` into it.
*
* The original pointer is neither modified nor deallocated.
* If allocation fails, `data` is `nil`, or `size` is 0, an error is yeeted.
*
* @param data: Raw data to fill the buffer with
* @param size: How many bytes are read from `data`, and the buffer size
* @param err: Error pointer
* @param data Raw data to fill the buffer with
* @param size How many bytes are read from `data`, and the buffer size
* @param err Error pointer
* @returns The buffer, except if an error occurred
*/
nbuf_t *nbuf_from(const void *restrict data, usize size, error *err);
/**
* Create a new buffer of fixed size and copy a string into it.
* @brief Create a new buffer of fixed size and copy a string into it.
*
* The original string is neither modified not deallocated.
* The original string is neither modified nor deallocated.
* If allocation fails or `s` is `nil`, an error is yeeted.
*
* @param s: String to fill the buffer with
* @param err: Error pointer
* @param s String to fill the buffer with
* @param err Error pointer
* @returns The buffer, except if an error occurred
*/
nbuf_t *nbuf_from_str(const char *restrict s, error *err);
/**
* Create a new buffer of fixed size and copy a neo string into it.
* @brief Create a new buffer of fixed size and copy a neo string into it.
*
* The original string is unmodified.
* If allocation fails or `s` is `nil`, an error is yeeted.
*
* @param s: Neo string to fill the buffer with
* @param err: Error pointer
* @param s Neo string to fill the buffer with
* @param err Error pointer
* @returns The buffer, except if an error occurred
*/
nbuf_t *nbuf_from_nstr(nstr_t *s, error *err);
/**
* Return a new copy of `buf`.
* @brief Return a new copy of `buf`.
*
* If `buf` is `nil` or allocation fails, an error is yeeted.
*
* @param buf: Buffer to create a copy of
* @param err: Error pointer
* @param buf Buffer to create a copy of
* @param err Error pointer
* @returns A copy of `buf`, unless an error occurred
*/
nbuf_t *nbuf_clone(nbuf_t *buf, error *err);
/**
* Get the byte at the specified index.
* @brief Get the byte at the specified index.
*
* If `buf` is `nil` or `index` is out of bounds, an error is yeeted.
*
* @param buf: `nbuf_t *` to get the byte from
* @param index: Byte index (counting from 0)
* @param err: Error pointer
* @param buf `nbuf_t *` to get the byte from
* @param index Byte index (counting from 0)
* @param err Error pointer
* @returns The byte at position `index` (as a `u8`), unless an error occurred
*/
#define nbuf_byte(buf, index, err) ({ \
@ -91,7 +97,7 @@ nbuf_t *nbuf_clone(nbuf_t *buf, error *err);
cursor++)
/**
* Compare two buffers.
* @brief Compare two buffers.
*
* If the first buffer is found to be greater than the second one, the return
* value is greater than 0.
@ -101,15 +107,17 @@ nbuf_t *nbuf_clone(nbuf_t *buf, error *err);
*
* If `buf1` or `buf2` is `nil`, an error is yeeted.
*
* @param buf1: First buffer to compare
* @param buf2: Second buffer to compare
* @param err: Error pointer
* @param buf1 First buffer to compare
* @param buf2 Second buffer to compare
* @param err Error pointer
* @returns The difference between the buffers, unless an error occurred
*/
int nbuf_cmp(const nbuf_t *buf1, const nbuf_t *buf2, error *err);
#define nbuf_eq(buf1, buf2, err) ( (bool)(nbuf_cmp(buf1, buf2, err) == 0) )
/** @} */
/*
* This file is part of libneo.
* Copyright (c) 2021 Fefie <owo@fef.moe>.

@ -1,4 +1,4 @@
/** See the end of this file for copyright and license terms. */
/* See the end of this file for copyright and license terms. */
#pragma once
@ -6,51 +6,66 @@
void _neo_nref_init(struct _neo_nref *ref, void (*destroy)(void *ptr), usize offset);
int _neo_nget(struct _neo_nref *ref);
int _neo_nput(struct _neo_nref *ref);
/**
* @defgroup nref Reference Counting
*
* @{
*/
/**
* Initialize the reference counter in a structure.
* @brief Initialize the reference counter in a structure.
*
* The count is initialized to 1 and can be incremented and decremented using
* `nget` and `nput` respectively. If the count reaches 0, the `destroy`
* `nget()` and `nput()` respectively. If the count reaches 0, the `destroy`
* callback is invoked where the structure and all resources tied to it must
* be released. The reference counter must be embedded in the struct using
* the `NREF_FIELD` macro.
*
* @param ptr: The `struct *` containing the `NREF_FIELD`
* @param destroy: A callback accepting a pointer to the original struct as its
* @param ptr The `struct *` containing the `NREF_FIELD`
* @param destroy A callback accepting a pointer to the original struct as its
* only parameter and return type `void`, which will deallocate the struct
*/
#define nref_init(ptr, destroy) ({ \
void (*__destroy_typechecked)(typeof(ptr)) = destroy; \
void (*__destroy_typechecked)(typeof(ptr)) = (destroy); \
_neo_nref_init(&(ptr)->__neo_nref, \
(void (*)(void *))__destroy_typechecked, \
offsetof(typeof(*(ptr)), __neo_nref)); \
})
int _neo_nget(struct _neo_nref *ref);
int _neo_nput(struct _neo_nref *ref);
/**
* Increment the reference counter of a structure embedding `NREF_FIELD`.
* @brief Increment the reference counter of a structure embedding `NREF_FIELD`.
*
* @param ptr: The `struct *` to increment the reference counter of
* @returns The new reference count
* @param ptr The `struct *` to increment the reference counter of
*/
#define nget(ptr) (_neo_nget( &(ptr)->__neo_nref ))
#define nget(ptr) ((void)_neo_nget( &(ptr)->__neo_nref ))
/**
* Decrement the reference counter of a structure embedding `NREF_FIELD`.
* @brief Decrement the reference counter of a structure embedding `NREF_FIELD`.
*
* If the counter reaches zero, the destroy callback passed to `nref_init`
* is invoked and the pointer is set to `nil` to prevent any further usage.
*
* @param ptr: The `struct *` to decrement the reference counter of
* @returns The new reference count
* @param ptr The `struct *` to decrement the reference counter of
*/
#define nput(ptr) ({ \
if (_neo_nput(&(ptr)->__neo_nref) == 0) \
ptr = nil; \
})
/**
* @brief Return the current reference count of a structure embedding `NREF_FIELD`.
* You usually shouldn't need this though.
*
* @param ptr The `struct *` embedding `NREF_FIELD`
* @returns The structure's current refcount value as a `const int`
*/
#define nref_count(ptr) ((const int)(ptr)->__neo_nref._count)
/** @} */
/*
* This file is part of libneo.
* Copyright (c) 2021 Fefie <owo@fef.moe>.

@ -1,4 +1,4 @@
/** See the end of this file for copyright and license terms. */
/* See the end of this file for copyright and license terms. */
#pragma once
@ -20,12 +20,31 @@ struct _neo_nstr_init_info {
};
/**
* Statically define a neo string.
* @defgroup nstr Strings
*
* @{
*/
/**
* @brief Return a pointer to the raw C string stored in this neo string.
*
* This macro should only be used when it is strictly necessary, for example
* when passing the string to `printf` and friends, because it will bypass the
* refcounting protection. If you absolutely need to store it in a structure
* with dynamic lifetime, it is advisable to combine it with `borrow`.
*
* @param nstr `nstr_t *` to get the raw string of
* @returns A `const char *` pointing to the raw C string
*/
#define nstr_raw(nstr) ((nstr)->_data)
/**
* @brief Statically define a neo string (file scope only).
*
* The string will be initialized before `main` is called.
*
* @param name: Name of the `nstr_t *` variable to be declared
* @param content: A `const char *` with the contents of the string
* @param name Name of the `nstr_t *` variable to be declared
* @param content A `const char *` with the contents of the string
*/
#define NSTR_DEFINE(name, content) \
nstr_t *name = nil; \
@ -36,20 +55,20 @@ struct _neo_nstr_init_info {
}
/**
* Copy a regular C string to a neo string.
* @brief Copy a regular C string to a neo string.
*
* `s` must be NUL terminated, and be deallocated manually if no longer needed.
* If `s` is `nil` or allocation fails, an error is yeeted. Strings in libneo
* are reference counted, see `nget` and `nput`.
*
* @param s: String to convert
* @param err: Error pointer
* @param s String to convert
* @param err Error pointer
* @returns The converted string, unless an error occurred
*/
nstr_t *nstr(const char *restrict s, error *err);
/**
* Copy a regular C string to a neo string, but at most `maxsize` bytes.
* @brief Copy a regular C string to a neo string, but at most `maxsize` bytes.
*
* The string is copied until a NUL terminator is encountered, or until
* `maxsize` bytes have been read. If `maxsize` is 0, an empty string is
@ -63,147 +82,153 @@ nstr_t *nstr(const char *restrict s, error *err);
nstr_t *nnstr(const char *restrict s, usize maxsize, error *err);
/**
* Get the Unicode code point in a string at the specified index.
* @brief Get the Unicode code point in a string at the specified index.
*
* If the string is `nil` or the index is out of range, an error is yeeted.
*
* @param s: String to get a character from
* @param index: Character index, starting from 0
* @param err: Error pointer
* @param s String to get a character from
* @param index Character index, starting from 0
* @param err Error pointer
* @returns The Unicode code point at the specified index,
* unless an error occurred
*/
nchar nchrat(const nstr_t *s, usize index, error *err);
/**
* Convert a signed integer to a string.
* @brief Convert a signed integer to a string.
*
* If allocation fails or the radix is out of range, an error is yeeted.
*
* @param i: The integer
* @param radix: Base of the numerical system to convert to, must be within 2~36
* @param err: Error pointer
* @param i The integer
* @param radix Base of the numerical system to convert to, must be within 2~36
* @param err Error pointer
* @returns The stringified integer, unless an error occurred
*/
nstr_t *i2nstr(i64 i, int radix, error *err);
/**
* Convert an unsigned integer to a string.
* @brief Convert an unsigned integer to a string.
*
* If allocation fails or the radix is out of range, an error is yeeted.
*
* @param u: The integer
* @param radix: Base of the numerical system to convert to, must be within 2~36
* @param err: Error pointer
* @param u The integer
* @param radix Base of the numerical system to convert to, must be within 2~36
* @param err Error pointer
* @returns The stringified integer, unless an error occurred
*/
nstr_t *u2nstr(u64 u, int radix, error *err);
/**
* Duplicate a string.
* @brief Duplicate a string.
*
* This should never be neccessary because strings in libneo are refcounted
* and immutable. If `s` is `nil` or allocation fails, an error is yeeted.
*
* @param s: String to duplicate
* @param err: Error pointer
* @param s String to duplicate
* @param err Error pointer
* @returns The duplicated string, unless an error occurred
*/
nstr_t *nstrdup(nstr_t *s, error *err);
/**
* Repeat a string `n` times and return the new string.
* @brief Repeat a string `n` times and return the new string.
*
* If `s` is `nil` or allocation fails, an error is yeeted.
* If `n` is 0, an empty string is returned.
* If `n` is 1, this function behaves exactly like `nstrdup`.
*
* @param s: String to repeat, remains unmodified
* @param n: Total amount of repetitions
* @param err: Error pointer
* @param s String to repeat, remains unmodified
* @param n Total amount of repetitions
* @param err Error pointer
* @returns A new string with the repeated content, unless an error occurred
*/
nstr_t *nstrmul(nstr_t *s, usize n, error *err);
/**
* Create a string considting of `n` repetitions of `c`.
* @brief Create a string considting of `n` repetitions of `c`.
*
* If `c` is not within unicode space or allocation fails, an error is yeeted.
* If `n` is 0, an empty string is returned.
*
* @param c: Character to fill the string with
* @param n: How many characters to put into the string
* @param err: Error pointer
* @param c Character to fill the string with
* @param n How many characters to put into the string
* @param err Error pointer
* @returns The string, unless an error occurred
*/
nstr_t *nchrmul(nchar c, usize n, error *err);
/**
* Concatenate two strings and return the result.
* @brief Concatenate two strings and return the result.
*
* The original two strings are unmodified.
* If any of the two strings are `nil` or allocation fails, an error is yeeted.
*
* @param s1: First string
* @param s2: Second string
* @param err: Error pointer
* @param s1 First string
* @param s2 Second string
* @param err Error pointer
* @returns A new string instance that consists of the two concatenated ones,
* unless an error occurred
*/
nstr_t *nstrcat(const nstr_t *s1, const nstr_t *s2, error *err);
/**
* Compare two strings character by character.
* @brief Compare two strings character by character.
*
* The return value is negative if the first different character in `s1` comes
* before the one in `s2`
*
* @param s1: First string
* @param s2: Second string
* @param err: Error pointer
* @param s1 First string
* @param s2 Second string
* @param err Error pointer
* @returns The difference between the two strings, unless an error occurred
*/
int nstrcmp(const nstr_t *s1, const nstr_t *s2, error *err);
/**
* Determine whether two strings are exactly equal.
* @brief Determine whether two strings are exactly equal.
*
* If either of the two string are `nil` or acquiring a lock on them fails,
* an error is yeeted.
*
* @param s1: First `nstr_t *`
* @param s2: Second `nstr_t *`
* @param err: Error pointer
* @param s1 First `nstr_t *`
* @param s2 Second `nstr_t *`
* @param err Error pointer
* @returns Whether the two strings are equal, unless an error occurred
*/
#define nstreq(s1, s2, err) ( (bool)(nstrcmp(s1, s2, err) == 0) )
/**
* @brief Extend a string to match a certain length.
*
* Prepend fill characters to a string to make it a specific length, and return
* a new string with the result. The original string is unmodified.
* If the string is already longer than the desired width or allocation fails,
* an error is yeeted.
*
* @param s: String to expand
* @param length: Desired length of the new string
* @param fill: Character to fill the space with
* @param err: Error pointer
* @param s String to expand
* @param length Desired length of the new string
* @param fill Character to fill the space with
* @param err Error pointer
* @returns The new padded string
*/
nstr_t *leftpad(nstr_t *s, usize length, nchar fill, error *err);
/**
* Iterate over each character in a string.
* @brief Iterate over each character in a string.
*
* @param cursor: `nchar *` to store the current character in
* @param nstr: `nstr_t *` to iterate over
* @param err: Error pointer; the loop will terminate early if an uncaught
* @param cursor `nchar *` to store the current character in
* @param nstr `nstr_t *` to iterate over
* @param err Error pointer; the loop will terminate early if an uncaught
* error occurred
*/
#define nstr_foreach(cursor, nstr, err) \
for (const char *__pos = \
for (const char *__next = \
(nstr)->_data + utf8_to_nchr(cursor, (nstr)->_data, err); \
/* errput sets the error number to 0xffffffff, thus number + 1 */ \
*__pos != '\0' && (err) != nil && (err)->_number + 1 < 2; \
__pos += utf8_to_nchr(cursor, __pos, err))
*__next != '\0' && (err) != nil && (err)->_number + 1 < 2; \
__next += utf8_to_nchr(cursor, __next, err))
/** @} */
/** Internal callback for nref */
void _neo_nstr_destroy(nstr_t *s);

@ -1,9 +1,15 @@
/** See the end of this file for copyright and license terms. */
/* See the end of this file for copyright and license terms. */
#pragma once
#include "neo/_types.h"
/**
* @defgroup stddef Core Macros
*
* @{
*/
#ifdef __cplusplus
# define nil nullptr
#else
@ -18,7 +24,7 @@
# define typeof(expr) __typeof(expr)
#endif
/** Get the absolute (non negative) value of an integer */
/** @brief Get the absolute (non negative) value of an integer */
#define nabs(n) ({ \
/* n is an expression, not a variable, evaluate it only once */ \
typeof(n) __neo_local_n = (n); \
@ -38,14 +44,18 @@
})
/**
* @brief Get the length of a data structure embedding `NLEN_FIELD`.
*
* Quickly get the length (as in amount of items, not bytes) of any libneo data
* structure that supports it. This includes strings, buffers, lists, and more.
*
* @param thing: Thing to get the length of
* @returns: The length as a `const usize`
* @param thing Thing to get the length of
* @returns The length as a `const usize`
*/
#define nlen(thing) ((thing)->__neo_nlen)
/* @} */
/*
* This file is part of libneo.
* Copyright (c) 2021 Fefie <owo@fef.moe>.

@ -1,4 +1,4 @@
/** See the end of this file for copyright and license terms. */
/* See the end of this file for copyright and license terms. */
#pragma once

@ -1,9 +1,15 @@
/** See the end of this file for copyright and license terms. */
/* See the end of this file for copyright and license terms. */
#pragma once
#include "neo/_stddef.h"
/**
* @defgroup types Primitive Types
*
* @{
*/
typedef __INT8_TYPE__ i8;
typedef __INT16_TYPE__ i16;
typedef __INT32_TYPE__ i32;
@ -18,13 +24,15 @@ typedef __UINT64_TYPE__ u64;
typedef __SIZE_TYPE__ usize;
typedef __PTRDIFF_TYPE__ isize;
/** A single Unicode character (32 bits) */
/** @brief A single Unicode character (32 bits) */
typedef u32 nchar;
typedef float f32;
typedef double f64;
typedef long double f128;
/** @} */
#ifdef __cplusplus
/* TODO: This is probably not a good idea */
# define __neo_atomic_type volatile int
@ -38,10 +46,12 @@ typedef long double f128;
#endif
/**
* Declare a length field in a structure.
* @brief Declare a length field in a structure.
* This makes it compatible with the `nlen` macro.
*
* @param name: field name, will be of type `usize`
* @param name field name, will be of type `usize`
*
* @ingroup stddef
*/
#define NLEN_FIELD(name) \
union { \
@ -56,12 +66,23 @@ struct _neo_nref {
__neo_atomic_type _count;
};
/**
* A basic reference counter for data structures.
* @brief A basic reference counter for data structures.
*
* Embed this into your data structure using the `NREF_FIELD` macro, initialize
* it using `nref_init`, and use `nget` and `nput` to increment/decrement the
* reference counter.
*
* @ingroup nref
*/
typedef struct _neo_nref nref_t;
/**
* @brief Embed this macro into your structure to enable refcounting.
*
* The reference counter must be initialized using `nref_init` and may be
* incremented and decremented using `nget` and `nput` respectively.
*
* @ingroup stddef
*/
#define NREF_FIELD nref_t __neo_nref
struct _neo_nbuf {
@ -75,7 +96,9 @@ struct _neo_nbuf {
const byte *_data;
};
/**
* A statically sized, refcounted buffer.
* @brief An immutable, refcounted buffer.
*
* @ingroup nbuf
*/
typedef struct _neo_nbuf nbuf_t;
@ -92,12 +115,22 @@ struct _neo_nstr {
nref_t *_borrow;
const char *_data;
};
/**
* @brief An immutable, refcounted UTF-8 string.
*
* @ingroup nstr
*/
typedef struct _neo_nstr nstr_t;
struct _neo_error {
nstr_t *_message;
u32 _number;
};
/**
* @brief The generic error type.
*
* @ingroup error
*/
typedef struct _neo_error error;
/*

@ -1,4 +1,8 @@
/** See the end of this file for copyright and license terms. */
/* See the end of this file for copyright and license terms. */
/**
* @file Hashtable API
*/
#pragma once
@ -24,31 +28,39 @@ struct _neo_hashtab {
u32 _buckets_len;
list_t _buckets[0]; /* -> _neo_hashtab_entry::link */
};
/**
* @defgroup hashtab Hashtable API
*
* @{
*/
/** @brief The hash table type. */
typedef struct _neo_hashtab hashtab_t;
/**
* Create a new hash table.
* @brief Create a new hash table.
*
* The hashing function used is implementation defined; use
* `hashtab_create_custom` to specify your own.
* If allocation fails or `buckets` is 0, an error is yeeted.
*
* @param buckets: Number of hash buckets
* @param err: Error pointer
* @param buckets Number of hash buckets
* @param err Error pointer
* @returns The initialized hash table, unless an error occurred
*/
hashtab_t *hashtab_create(u32 buckets, error *err);
/**
* Create a new hash table with custom hashing algorithm.
* @brief Create a new hash table with custom hashing algorithm.
*
* If allocation fails, `buckets` is 0, or `hashfn` is nil, an error is yeeted.
* The custom hash function must return a value less than the `limit` parameter
* passed to it. The `limit` parameter is the number of buckets minus one.
*
* @param buckets: Number of hash buckets
* @param hashfn: Custom hash function to use
* @param err: Error pointer
* @param buckets Number of hash buckets
* @param hashfn Custom hash function to use
* @param err Error pointer
* @returns The initialized hash table, unless an error occurred
*/
hashtab_t *hashtab_create_custom(u32 buckets,
@ -56,56 +68,56 @@ hashtab_t *hashtab_create_custom(u32 buckets,
error *err);
/**
* Get an entry in a hash table.
* @brief Get an entry in a hash table.
*
* If the key does not exist, *no* error is yeeted and the return value is `nil`.
* If the key is `nil` or the hash function returned a value greater than the
* maxval parameter passed to it, an error is yeeted.
*
* @param table: Hash table to get the entry from
* @param key: Key to get the value of
* @param err: Error pointer
* @param table Hash table to get the entry from
* @param key Key to get the value of
* @param err Error pointer
* @returns The entry or `nil` if it does not exist, unless an error occurred
*/
void *hashtab_get(hashtab_t *table, const nbuf_t *key, error *err);
/**
* Put an entry into a hash table.
* @brief Put an entry into a hash table.
*
* The reference counter in `key` is incremented.
* If `key` is `nil`, `key_size` is 0, the key already exists in the table,
* or the hash function returned a value greater than or equal to the `limit`
* parameter passed to it, an error is yeeted.
*
* @param table: Hash table to insert the value at
* @param key: Key to insert the value under
* @param val: Value to insert
* @param err: Error pointer
* @param table Hash table to insert the value at
* @param key Key to insert the value under
* @param val Value to insert
* @param err Error pointer
*/
void hashtab_put(hashtab_t *table, nbuf_t *key, void *val, error *err);
/**
* Delete an entry from a hash table.
* @brief Delete an entry from a hash table.
*
* If `table` or `key` is `nil`, or the key was not found within the table,
* an error is yeeted.
*
* @param table: Table to delete an entry from
* @param key: Key of the item to delete
* @param err: Error pointer
* @param table Table to delete an entry from
* @param key Key of the item to delete
* @param err Error pointer
* @returns The removed item, unless an error occurred
*/
void *hashtab_del(hashtab_t *table, nbuf_t *key, error *err);
/**
* Iterate over every entry in a hash table.
* @brief Iterate over every entry in a hash table.
*
* If `table` or `callback` is `nil`, an error is yeeted.
*
* @param table: Table to iterate over
* @param callback: Callback function that is invoked for every entry;
* @param table Table to iterate over
* @param callback Callback function that is invoked for every entry;
* the iteration stops if the return value is nonzero
* @param extra: Optional pointer that is passed as an extra argument to the
* @param extra Optional pointer that is passed as an extra argument to the
* callback function
* @returns The last return value of the callback, unless an error occurred
*/
@ -113,6 +125,8 @@ int hashtab_foreach(hashtab_t *table,
int (*callback)(hashtab_t *table, nbuf_t *key, void *val, void *extra),
void *extra, error *err);
/** @} */
#ifdef __cplusplus
}; /* extern "C" */
#endif

@ -1,4 +1,8 @@
/** See the end of this file for copyright and license terms. */
/* See the end of this file for copyright and license terms. */
/**
* @file List API
*/
#pragma once
@ -17,7 +21,9 @@ struct _neo_listnode {
struct _neo_list *_list;
};
/**
* List node anchor for embedding into your own structure.
* @brief List node anchor for embedding into your own structure.
*
* @ingroup list
*/
typedef struct _neo_listnode listnode_t;
@ -25,23 +31,21 @@ struct _neo_list {
struct _neo_listnode _root;
NLEN_FIELD(_len);
};
typedef struct _neo_list list_t;
/**
* Initialize a list.
* @brief List data type
*
* @param list: The list
* @ingroup list
*/
void list_init(list_t *list);
typedef struct _neo_list list_t;
/**
* When casting out from a `listnode_t` to its supposed containing structure
* (in a loop), it is possible that it was in fact the `list_t` that contained
* the `listnode_t`. This macro checks whether that is the case.
* When casting out from a @a `listnode_t` to its supposed containing structure
* (in a loop), it is possible that it was in fact the @a `list_t` that contained
* the @a `listnode_t`. This macro checks whether that is the case.
*
* @param casted: `struct *` that the `listnode_t *` was casted out to
* @param list: `list_t *` storing the root `listnode_t`
* @param member: Name of the `listnode_t` member embedded within the `struct *`
* @param casted `struct *` that the @a `listnode_t *` was casted out to
* @param list @a `list_t *` storing the root @a `listnode_t`
* @param member Name of the @a `listnode_t` member embedded within the `struct *`
*/
#define _neo_list_is_root(casted, list, member) \
( &(casted)->member == &(list)->_root )
@ -64,52 +68,65 @@ void list_init(list_t *list);
((typeof(current))( (u8 *)((current)->member._prev) - offsetof(typeof(*(current)), member) ))
/**
* Append a new node to the end of a list.
* @defgroup list List API
*
* @{
*/
/**
* Initialize a list.
*
* @param list The list
*/
void list_init(list_t *list);
/**
* @brief Append a new node to the end of a list.
*
* @param list: List to append to
* @param new_node: New node to append
* @param list List to append to
* @param new_node New node to append
*/
void list_add(list_t *list, listnode_t *new_node);
/**
* Remove a node from a list.
* @brief Remove a node from a list.
*
* @param node: Node to remove
* @param node Node to remove
*/
void list_del(listnode_t *node);
/**
* Insert a new node to the beginning of a list.
* @brief Insert a new node to the beginning of a list.
*
* @param list: List to insert the node into
* @param new_node: New node to insert
* @param list List to insert the node into
* @param new_node New node to insert
*/
void list_add_first(list_t *list, listnode_t *new_node);
/**
* Insert a new node after the specified list node.
*
* @param pos: List node to insert the new node after
* @param new_node: Node to insert after the specified position
* @param pos List node to insert the new node after
* @param new_node Node to insert after the specified position
*/
void list_insert(listnode_t *pos, listnode_t *new_node);
/**
* Insert a new node before the specified list node.
*
* @param pos: List node to insert the new node before
* @param new_node: Node to insert before the specified position
* @param pos List node to insert the new node before
* @param new_node Node to insert before the specified position
*/
void list_insert_before(listnode_t *pos, listnode_t *new_node);
/**
* Iterate over each item in a list.
* @brief Iterate over each item in a list.
*
* The current entry can be safely removed from the list.
*
* @param cursor: `type *` to use as a cursor; may be a declaration
* @param list: `list_t *` to iterate over
* @param member: Name of the `listnode_t` member embedded within `cursor`
* @param cursor `type *` to use as a cursor
* @param list `list_t *` to iterate over
* @param member Name of the `listnode_t` member embedded within \a `cursor`
*/
#define list_foreach(cursor, list, member) \
for (typeof(cursor) __tmp = _neo_list_next( \
@ -120,13 +137,13 @@ void list_insert_before(listnode_t *pos, listnode_t *new_node);
cursor = __tmp, __tmp = _neo_list_next(__tmp, member))
/**
* Iterate over each item in a list in reverse order.
* @brief Iterate over each item in a list in reverse order.
*
* The current entry can be safely removed from the list.
*
* @param cursor: `type *` to use as a cursor; may be a declaration
* @param list: `list_t *` to iterate over
* @param member: Name of the `listnode_t` member embedded within `cursor`
* @param cursor `type *` to use as a cursor
* @param list `list_t *` to iterate over
* @param member Name of the `listnode_t` member embedded within \a `cursor`
*/
#define list_foreach_reverse(cursor, list, member) \
for (typeof(cursor) __tmp = _neo_list_prev( \
@ -136,6 +153,8 @@ void list_insert_before(listnode_t *pos, listnode_t *new_node);
!_neo_list_is_root(cursor, list, member); \
cursor = __tmp, __tmp = _neo_list_prev(__tmp, member))
/** @} */
#ifdef __cplusplus
}; /* extern "C" */
#endif

@ -1,4 +1,4 @@
/** See the end of this file for copyright and license terms. */
/* See the end of this file for copyright and license terms. */
/**
* @file Conversion utilities for raw UTF.
@ -24,75 +24,75 @@ extern "C" {
#include "neo/_types.h"
/**
* Check whether a NUL terminated string is valid UTF-8.
* @brief Check whether a NUL terminated string is valid UTF-8.
*
* If the string contains any malformed code sequences, an error is yeeted.
*
* @param s: String to validate
* @param err: Error pointer
* @param s String to validate
* @param err Error pointer
* @returns The number of UTF-8 code points (i.e. number of Unicode characters)
* excluding the terminating NUL byte; undefined on error
*/
usize utf8_check(const char *restrict s, error *err);
/**
* Check whether a NUL terminated string is valid UTF-8, but read at most
* `maxsize + 3` bytes (this function uses `utf8_to_nchr` internally).
* @brief Check whether a NUL terminated string is valid UTF-8.
* At most `maxsize + 3` bytes are read (this function uses `utf8_to_nchr` internally).
*
* If a NUL terminator is encountered before `maxsize` bytes, reading stops
* before the specified size. If the string contains any malformed code
* sequences, an error is yeeted.
*
* @param s: String to validate
* @param maxsize: Maximum amount of byte to read from `s`
* @param err: Error pointer
* @param s String to validate
* @param maxsize Maximum amount of byte to read from `s`
* @param err Error pointer
* @returns The number of UTF-8 code points (i.e. number of Unicode characters)
* excluding the terminating NUL byte; undefined on error
*/
usize utf8_ncheck(const char *restrict s, usize maxsize, error *err);
/**
* Compute the length of a raw UTF-8 encoded, NUL terminated string.
* @brief Compute the length of a raw UTF-8 encoded, NUL terminated string.
*
* The string is *not* checked for malformed code sequences,
* use `utf8_check` for that.
*
* @param s: String to get the length of
* @returns: String length as in Unicode code points (not bytes),
* @param s String to get the length of
* @returns String length as in Unicode code points (not bytes),
* excluding the terminating NUL byte
*/
usize utf8_strlen(const char *restrict s);
/**
* Get the amount of bytes a Unicode character takes up in UTF-8.
* @brief Get the amount of bytes a Unicode character takes up in UTF-8.
*
* If the character is outside of the Unicode range (`0x00000000`~`0x0010ffff`),
* an error is yeeted.
*
* @param c: The character
* @param err: Error pointer
* @param c The character
* @param err Error pointer
* @returns The amount of bytes needed to store the character in UTF-8 encoding,
* which is always between 1 and 4 except on errors
*/
usize utf8_chrsize(nchar c, error *err);
/**
* UTF-8 encode a Unicode character and store it in `dest` with NUL terminator.
* @brief UTF-8 encode a Unicode character and store it in `dest` with NUL terminator.
*
* The buffer needs to hold at least 5 bytes. If the character is outside of
* the Unicode range (`0x00000000`~`0x0010ffff`), an error is yeeted and the
* buffer is not modified.
*
* @param dest: Where to store the encoded character (*not* NUL terminated)
* @param c: Character to encode
* @param err: Error pointer
* @param dest Where to store the encoded character (*not* NUL terminated)
* @param c Character to encode
* @param err Error pointer
* @returns The amount of bytes taken up by the character,
* which is always between 1 and 4 except on errors
*/
usize utf8_from_nchr(char *restrict dest, nchar c, error *err);
/**
* Decode a UTF-8 character and store it in `c`.
* @brief Decode a UTF-8 character and store it in `c`.
*
* If the character encoding is malformed, an error is yeeted and `c` is set to
* the ASCII NUL character. The encoded character does not need to be NUL
@ -101,9 +101,9 @@ usize utf8_from_nchr(char *restrict dest, nchar c, error *err);
* this may cause the method to read up to 3 bytes over the end of the buffer
* if the code sequence is malformed.
*
* @param c: Where to store the decoded character
* @param utf8chr: UTF-8 encoded character sequence
* @param err: Error pointer
* @param c Where to store the decoded character
* @param utf8chr UTF-8 encoded character sequence
* @param err Error pointer
* @returns The amount of bytes the character took up when encoded as UTF-8,
* which is always between 1 and 4 except on errors
*/

@ -17,6 +17,50 @@ configure_file(
${CMAKE_BINARY_DIR}/include/neo/buildconfig.h
)
option(BUILD_DOCS "Build documentation" ON)
option(BUILD_DOCS_HTML "Build documentation in HTML format" ON)
option(BUILD_DOCS_LATEX "Build documentation in LaTEX format" OFF)
option(BUILD_DOCS_RTF "Build documentation in Rich Text Format" OFF)
option(BUILD_DOCS_XML "Build documentation in XML format" OFF)
if(BUILD_DOCS)
find_package(Doxygen REQUIRED)
set(DOXYFILE_IN ${CMAKE_CURRENT_SOURCE_DIR}/../doc/Doxyfile.in)
set(DOXYFILE_OUT ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile)
configure_file(${DOXYFILE_IN} ${DOXYFILE_OUT} @ONLY)
if(BUILD_DOCS_HTML)
set(neo_BUILD_DOCS_HTML YES)
else()
set(neo_BUILD_DOCS_HTML NO)
endif()
if(BUILD_DOCS_LATEX)
set(neo_BUILD_DOCS_LATEX YES)
else()
set(neo_BUILD_DOCS_LATEX NO)
endif()
if(BUILD_DOCS_RTF)
set(neo_BUILD_DOCS_RTF YES)
else()
set(neo_BUILD_DOCS_RTF NO)
endif()
if(BUILD_DOCS_XML)
set(neo_BUILD_DOCS_XML YES)
else()
set(neo_BUILD_DOCS_XML NO)
endif()
add_custom_target(neo_docs ALL
COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYGEN_OUT}
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT "Building documentation"
VERBATIM
)
endif()
target_include_directories(neo PUBLIC ../include)
target_include_directories(neo PRIVATE
./include

@ -9,8 +9,9 @@
TEST_CASE( "utf8_check: ASCII string", "[string/utf.c]" )
{
error err;
utf8_check("i'm gay,,,", &err);
usize len = utf8_check("i'm gay,,,", &err);
REQUIRE( len == 10 );
REQUIRE( errnum(&err) == 0 );
REQUIRE( errmsg(&err) == nil );
}
@ -19,8 +20,9 @@ TEST_CASE( "utf8_check: String with 2-byte UTF-8 sequence", "[string/utf.c]" )
{
error err;
/* U+03B1 Greek Smol Letter Alpha */
utf8_check("i'm g\xce\xb1y,,,", &err);
usize len = utf8_check("i'm g\xce\xb1y,,,", &err);
REQUIRE( len == 10 );
REQUIRE( errnum(&err) == 0 );
REQUIRE( errmsg(&err) == nil );
}
@ -39,8 +41,9 @@ TEST_CASE( "utf8_check: String with 4-byte UTF-8 sequence", "[string/utf.c]" )
{
error err;
/* U+1F97A The Bottom Emoji(TM) */
utf8_check("i'm gay\xf0\x9f\xa5\xba,,,", &err);
usize len = utf8_check("i'm gay\xf0\x9f\xa5\xba,,,", &err);
REQUIRE( len == 11 );
REQUIRE( errnum(&err) == 0 );
REQUIRE( errmsg(&err) == nil );
}

Loading…
Cancel
Save