[*] Initial commit

This commit is contained in:
Lorenzo Cogotti
2021-06-07 16:55:13 +02:00
commit b0ef4dd774
117 changed files with 29737 additions and 0 deletions

87
lonetix/include/df/argv.h Normal file
View File

@ -0,0 +1,87 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
/**
* \file argv.h
*
* Command line argument parsing library.
*
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
* \author Lorenzo Cogotti
*/
#ifndef DF_ARGV_H_
#define DF_ARGV_H_
#include "utf/utfdef.h"
#ifdef _WIN32
// DOS-style options are preferred
#define OPTCHAR '/'
#define OPTSEP ':'
#else
// Unix-style options
#define OPTCHAR '-'
#define OPTSEP '='
#endif
typedef enum {
ARG_NONE, ///< `Optflag` takes no argument
ARG_REQ, ///< `Optflag` with mandatory argument
ARG_OPT ///< `Optflag` may take an optional argument
} Optarg;
typedef struct {
Rune opt; ///< Set to `\0` to signal `Optflag` list end
const char *longopt; ///< Optional long name for this option
const char *argName; ///< Human readable descriptive argument name, ignored if `hasArg == ARG_NONE`
const char *descr; ///< Human readable description for option, displayed in help message
Optarg hasArg; ///< Whether an argument to this `Optflag` is mandatory, optional or prohibited
// Following fields are altered by `Com_ArgParse()` when flag is
// encountered.
// If option doesn't appear inside `argv`, then following fields are left
// untouched (thus using them to provide default values is legal)
Boolean flagged; ///< Set to `TRUE` if option was found in command line
char *optarg; ///< Set to a pointer inside `argv` when argument for this option is found
} Optflag;
extern const char *com_progName; ///< If set to non-NULL string, it is used to provide a program name during `Com_ArgParse()`, instead of `argv[0]`
extern const char *com_synopsis; ///< If set to non-NULL string, provides a short synopsis for `Com_ArgParse()` usage message
extern const char *com_shortDescr; ///< If set to non-NULL string, provides a brief command summary for `Com_ArgParse()` usage message
extern const char *com_longDescr; ///< If set to non-NULL string, provides a long description for `Com_ArgParse()` usage message
/// Do not emit error messages or help message automatically to `stderr`.
#define ARG_QUIET BIT(0)
/// Do not perform GNU-like command flags reordering.
#define ARG_NOREORD BIT(1)
/// The provided `Optflag` list is ill-formed (e.g. option with `opt == '-' && longopt == NULL` was found)
#define OPT_BADLIST -1
/// Missing required option argument
#define OPT_ARGMISS -2
/// Excess argument inside long option that requires none (e.g. `--foo=bar`, but `foo->hasArg == ARG_NONE`)
#define OPT_EXCESSARG -3
/// Encountered unknown option
#define OPT_UNKNOWN -4
/// Parsing terminated because help option was requested (-h, -?, --help or platform specific equivalents)
#define OPT_HELP -5
/// Command line is ambiguous
#define OPT_BADARGV -6
/**
* Parse command line arguments, updating relevant option flags.
*
* \param [in] argc Argument count
* \param [in] argv Argument vector`
* \param [in,out] options Option flag list
* \param [in] flags Parsing flags (`ARG_*` bit mask)
*
* \return Number of argument parsed or an appropriate error value:
* * On success the number of parsed arguments is returned (>= 0)
* * If the help option was requested then `OPT_HELP` is returned
* * If argument list is ill-formed, an error code is returned (`OPT_*`)
*/
int Com_ArgParse(int argc, char **argv, Optflag *options, unsigned flags);
#endif

87
lonetix/include/df/bgp/asn.h Executable file
View File

@ -0,0 +1,87 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
/**
* \file bgp/asn.h
*
* Types, constant and utilities to work with ASN of various width.
*
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
* \author Lorenzo Cogotti
*/
#ifndef DF_BGP_ASN_H_
#define DF_BGP_ASN_H_
#include "xpt.h"
/// 2 octet `AS_TRANS` constant, in network order (big-endian).
#define AS_TRANS BE16(23456)
/// 4 octet `AS_TRANS` constant, in network order (big-endian).
#define AS4_TRANS BE32(23456)
/// 2 bytes wide AS number (ASN16), in network order (big-endian).
typedef Uint16 Asn16;
/// 4 bytes wide AS number (ASN32), in network order (big-endian).
typedef Uint32 Asn32;
/**
* \brief Fat AS type capable of holding either a 4 octet (ASN32) or a 2 octet
* (ASN16) AS number, with additional flags indicating ASN properties.
*
* \note This type doesn't reflect any actual BGP ASN encoding, it is merely
* a convenience abstraction, useful when ASN properties should be bundled
* with the actual ASN.
*/
typedef Sint64 Asn; // NOTE: signed so negative values may be used for
// special purposes if necessary
/// `Asn` flag indicating that the currently stored ASN is 4 octet wide (ASN32).
#define ASN32BITFLAG BIT(62)
/**
* \brief Return an `Asn` holding an `Asn16` value.
*
* \note `asn` must be in network order (big endian).
*/
#define ASN16BIT(asn) ((Asn) ((Asn16) (asn)))
/**
* \brief Return an `Asn` holding a `Asn32`.
*
* \note `asn` should be in network order (big endian).
*/
#define ASN32BIT(asn) ((Asn) (ASN32BITFLAG | ((Asn) ((Asn32) asn))))
/// Test whether an `Asn` is holding a 4 octet wide ASN.
FORCE_INLINE Boolean ISASN32BIT(Asn asn)
{
return (asn & ASN32BITFLAG) != 0;
}
/**
* \brief Extract AS number stored inside an `Asn`.
*
* Returned ASN is extended to `Asn32` even if it was originally an `Asn16`,
* this allows code involving `Asn` to treat any ASN uniformly.
*
* \note Resulting ASN is in network byte order (big endian).
*/
FORCE_INLINE Asn32 ASN(Asn asn)
{
Asn32 res = asn & 0xffffffffuLL;
#if EDN_NATIVE != EDN_BE
// Shift _asn of 16 bits if this was originally ASN16,
// on LE machines this mimics a BE16 word to BE32 dword extension
res <<= (((asn & ASN32BITFLAG) == 0) << 4);
#endif
return res;
}
/// Test whether `asn` represents either `AS_TRANS` or `AS4_TRANS`.
FORCE_INLINE Boolean ISASTRANS(Asn asn)
{
return ASN(asn) == AS4_TRANS;
}
#endif

1198
lonetix/include/df/bgp/bgp.h Executable file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,62 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
/**
* \file bgp/bytebuf.h
*
* Allocator optimized for trivial BGP workflows.
*
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
* \author Lorenzo Cogotti
*/
#ifndef DF_BGP_MEMBUF_H_
#define DF_BGP_MEMBUF_H_
#include "mem.h"
/// Memory alignment for `Bgpbytebuf` allocations.
#define BGP_MEMBUF_ALIGN 4
// NOTE: Need at least 4 bytes alignment for TABLE_DUMPV2 peer lookup tables!
/**
* \brief Basic fixed-size packed single-threaded byte buffer.
*
* Nearly zero-overhead byte pool optimized for typical BGP message
* allocations. Returns chunks from an internal fixed-size byte buffer
*/
typedef struct {
size_t size; ///< Buffer block size in bytes
size_t pos; ///< Current position inside block
ALIGNED(BGP_MEMBUF_ALIGN, Uint8 base[FLEX_ARRAY]); ///< Block buffer
} Bgpbytebuf;
/**
* \brief Create `Bgpbytebuf` with statically sized fixed buffer
* (as opposed to a flexible array buffer).
*
* This is useful when the buffer should be placed in statically
* allocated variable, e.g.
* ```c
* static BGP_FIXBYTEBUF(4096) bgp_msgBuf = { 4096 };
* ```
*
* May also be used for a `typedef`:
* ```c
* typedef BGP_FIXBYTEBUF(1024) Bgpsmallbuf;
* ```
*
* Variables generated by this macro may be used
* as `allocp` of any API expecting a `MemOps` interface.
*/
#define BGP_FIXBYTEBUF(bufsiz) \
struct { \
size_t size; \
size_t pos; \
ALIGNED(BGP_MEMBUF_ALIGN, Uint8 base[bufsiz]); \
}
/// `MemOps` operating over `Bgpbytebuf`, use pointer to `Bgpbytebuf` as `allocp`.
extern const MemOps *const Mem_BgpBufOps;
#endif

90
lonetix/include/df/bgp/dump.h Executable file
View File

@ -0,0 +1,90 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
/**
* \file bgp/dump.h
*
* BGP message dump utilities.
*
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
* \author Lorenzo Cogotti
*/
#ifndef DF_BGP_DUMP_H_
#define DF_BGP_DUMP_H_
#include "mrt.h"
/**
* \brief BGP message dump formatter.
*
* \note Calling the dump functions directly should be considered low-level,
* under normal circumstances use: `Bgp_DumpMsg()`, `Bgp_DumpMrtUpdate()`,
* `Bgp_DumpRib()`, and `Bgp_DumpRibv2()`.
*/
typedef struct {
Sint64 (*DumpMsg)(const Bgphdr *, unsigned,
void *, const StmOps *,
Bgpattrtab);
Sint64 (*DumpRibv2)(const Mrthdr *,
const Mrtpeerentv2 *, const Mrtribentv2 *,
void *, const StmOps *,
Bgpattrtab);
Sint64 (*DumpRib)(const Mrthdr *,
const Mrtribent *,
void *, const StmOps *,
Bgpattrtab);
Sint64 (*DumpBgp4mp)(const Mrthdr *,
void *, const StmOps *,
Bgpattrtab);
Sint64 (*DumpZebra)(const Mrthdr *,
void *, const StmOps *,
Bgpattrtab);
} BgpDumpfmt;
// Standard dump formatters
extern const BgpDumpfmt *const Bgp_IsolarioFmt; ///< Isolario `bgpscanner` like output format
extern const BgpDumpfmt *const Bgp_IsolarioFmtWc; ///< Isolario `bgpscanner` like output format, with colors
// extern const BgpDumpfmt *const Bgp_BgpdumpFmt; ///< `bgpdump` style output format
// extern const BgpDumpfmt *const Bgp_RawFmt; ///< output message raw bytes
// extern const BgpDumpfmt *const Bgp_HexFmt; ///< perform BGP message hexadecimal dump
// extern const BgpDumpfmt *const Bgp_CFmt; ///< outputs BGP message as a C-style array
FORCE_INLINE Sint64 Bgp_DumpMsg(Bgpmsg *msg,
void *streamp, const StmOps *ops,
const BgpDumpfmt *fmt)
{
extern Judgement _Bgp_SetErrStat(BgpRet,
const char *,
const char *,
unsigned long long,
unsigned);
Sint64 res = 0;
if (ops->Write && fmt->DumpMsg)
res = fmt->DumpMsg(BGP_HDR(msg), msg->flags, streamp, ops, msg->table);
else
_Bgp_SetErrStat(BGPENOERR, NULL, NULL, 0, 0);
return res;
}
Sint64 Bgp_DumpMrtUpdate(const Mrthdr *hdr,
void *streamp, const StmOps *ops,
const BgpDumpfmt *fmt);
Sint64 Bgp_DumpMrtRibv2(const Mrthdr *hdr,
const Mrtpeerentv2 *peer, const Mrtribentv2 *ent,
void *streamp, const StmOps *ops,
const BgpDumpfmt *fmt);
Sint64 Bgp_DumpMrtRib(const Mrthdr *hdr,
const Mrtribent *ent,
void *streamp, const StmOps *ops,
const BgpDumpfmt *fmt);
#endif

888
lonetix/include/df/bgp/mrt.h Executable file
View File

@ -0,0 +1,888 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
/**
* \file bgp/mrt.h
*
* Multithreaded Routing Toolkit (MRT) types and functions.
*
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
* \author Lorenzo Cogotti
*/
#ifndef DF_BGP_MRT_H_
#define DF_BGP_MRT_H_
#include "bgp/bgp.h"
#include "stm.h"
/**
* \name MRT record types
*
* \note Values are in network order (big endian)
*
* @{
*/
#define MRT_NULL BE16(0) // Deprecated
#define MRT_START BE16(1) // Deprecated
#define MRT_DIE BE16(2) // Deprecated
#define MRT_I_AM_DEAD BE16(3) // Deprecated
#define MRT_PEER_DOWN BE16(4) // Deprecated
#define MRT_BGP BE16(5) // Deprecated, also known as ZEBRA_BGP
#define MRT_RIP BE16(6) // Deprecated
#define MRT_IDRP BE16(7) // Deprecated
#define MRT_RIPNG BE16(8) // Deprecated
#define MRT_BGP4PLUS BE16(9) // Deprecated
#define MRT_BGP4PLUS_01 BE16(10) // Deprecated
#define MRT_OSPFV2 BE16(11)
#define MRT_TABLE_DUMP BE16(12)
#define MRT_TABLE_DUMPV2 BE16(13)
#define MRT_BGP4MP BE16(16)
#define MRT_BGP4MP_ET BE16(17)
#define MRT_ISIS BE16(32)
#define MRT_ISIS_ET BE16(33)
#define MRT_OSPFV3 BE16(48)
#define MRT_OSPFV3_ET BE16(49)
/// 2-octets type for MRT record header type field, network byte order (big endian).
typedef Uint16 MrtType;
/** @} */
/**
* \name MRT subtypes enumeration for records of type BGP/ZEBRA
*
* \warning ZEBRA BGP has been deprecated in favor of BGP4MP.
*
* \see `MrtSubType`
*
* @{
*/
#define ZEBRA_NULL BE16(0)
#define ZEBRA_UPDATE BE16(1)
#define ZEBRA_PREF_UPDATE BE16(2)
#define ZEBRA_STATE_CHANGE BE16(3)
#define ZEBRA_SYNC BE16(4)
#define ZEBRA_OPEN BE16(5)
#define ZEBRA_NOTIFY BE16(6)
#define ZEBRA_KEEPALIVE BE16(7)
/** @} */
/**
* \name MRT subtypes for records of type BGP4MP
*
* BGP4MP is defined in [RFC 6396](https://tools.ietf.org/html/rfc6396#section-4.2),
* and extended by [RFC 8050](https://tools.ietf.org/html/rfc8050#page-2).
*
* \see [IANA BGP4MP Subtype Codes](https://www.iana.org/assignments/mrt/mrt.xhtml#BGP4MP-codes)
* \see `MrtSubType`
*
* @{
*/
#define BGP4MP_STATE_CHANGE BE16(0) ///< RFC 6396
#define BGP4MP_MESSAGE BE16(1) ///< RFC 6396
#define BGP4MP_ENTRY BE16(2) ///< Deprecated
#define BGP4MP_SNAPSHOT BE16(3) ///< Deprecated
#define BGP4MP_MESSAGE_AS4 BE16(4) ///< RFC 6396
#define BGP4MP_STATE_CHANGE_AS4 BE16(5) ///< RFC 6396
#define BGP4MP_MESSAGE_LOCAL BE16(6) ///< RFC 6396
#define BGP4MP_MESSAGE_AS4_LOCAL BE16(7) ///< RFC 6396
#define BGP4MP_MESSAGE_ADDPATH BE16(8) ///< RFC 8050
#define BGP4MP_MESSAGE_AS4_ADDPATH BE16(9) ///< RFC 8050
#define BGP4MP_MESSAGE_LOCAL_ADDPATH BE16(10) ///< RFC 8050
#define BGP4MP_MESSAGE_AS4_LOCAL_ADDPATH BE16(11) ///< RFC 8050
/** @} */
/**
* \name MRT subtypes for records of type TABLE_DUMPV2
*
* Table Dump version 2 is defined in [RFC 6396](https://tools.ietf.org/html/rfc6396#section-4.3).
*
* \see [IANA Table Dump version 2 Subtype Codes](https://www.iana.org/assignments/mrt/mrt.xhtml#table-dump-v2-subtype-codes)
* \see `MrtSubType`
*
* @{
*/
#define TABLE_DUMPV2_PEER_INDEX_TABLE BE16(1) ///< RFC 6396
#define TABLE_DUMPV2_RIB_IPV4_UNICAST BE16(2) ///< RFC 6396
#define TABLE_DUMPV2_RIB_IPV4_MULTICAST BE16(3) ///< RFC 6396
#define TABLE_DUMPV2_RIB_IPV6_UNICAST BE16(4) ///< RFC 6396
#define TABLE_DUMPV2_RIB_IPV6_MULTICAST BE16(5) ///< RFC 6396
#define TABLE_DUMPV2_RIB_GENERIC BE16(6) ///< RFC 6396
#define TABLE_DUMPV2_GEO_PEER_TABLE BE16(7) ///< RFC 6397
#define TABLE_DUMPV2_RIB_IPV4_UNICAST_ADDPATH BE16(8) ///< RFC 8050
#define TABLE_DUMPV2_RIB_IPV4_MULTICAST_ADDPATH BE16(9) ///< RFC 8050
#define TABLE_DUMPV2_RIB_IPV6_UNICAST_ADDPATH BE16(10) ///< RFC 8050
#define TABLE_DUMPV2_RIB_IPV6_MULTICAST_ADDPATH BE16(11) ///< RFC 8050
#define TABLE_DUMPV2_RIB_GENERIC_ADDPATH BE16(12) ///< RFC 8050
/** @} */
/// Type for MRT header subtype field, 2-octets type, network byte order (big endian).
typedef Uint16 MrtSubType;
/// Lookup table for TABLE_DUMPV2 PEER_INDEX.
typedef struct {
// NOTE: Data in this table is accessed atomically,
// despite these fields being signed they are effectively treated as
// unsigned by the library.
Sint16 validCount; ///< Count of valid entries inside `offsets`
Sint16 peerCount; ///< Cached PEER_INDEX_TABLE peer count value
Sint32 offsets[FLEX_ARRAY]; ///< Offset table, `peerCount` entries
} Mrtpeertabv2;
/**
* \brief MRT record type.
*
* Behaves in a similar way to `Bgpmsg`.
* Performs allocations via possibly custom allocator defined in fields
* `allocp` and `memOps`.
* If those fields are left `NULL', it uses `Mem_StdOps`.
*/
typedef struct {
Uint8 *buf; ///< Raw MRT record contents
void *allocp; ///< Optional custom memory allocator
const MemOps *memOps; ///< Optional custom memory operations
/**
* Fast peer offset table, only meaningful for
* `TABLE_DUMPV2` `PEER_INDEX_TABLE`.
*
* \note This is an atomic pointer to a [Mrtpeertabv2](\ref Mrtpeertabv2).
*
* \warning Honest, this field **MUST NOT** be accessed directly.
*/
void *peerOffTab;
} Mrtrecord;
/// Move `Mrtrecord` contents from `src` to `dest`, leaving `src` empty.
FORCE_INLINE void MRT_MOVEREC(Mrtrecord *dest, Mrtrecord *src)
{
EXTERNC void *memcpy(void *, const void *, size_t);
memcpy(dest, src, sizeof(*dest));
src->buf = NULL;
src->peerOffTab = NULL;
}
// Retrieve `MemOps` associated with record `rec`.
FORCE_INLINE const MemOps *MRT_MEMOPS(const Mrtrecord *rec)
{
return rec->memOps ? rec->memOps : Mem_StdOps;
}
#define _MRT_EXHDRMASK ( \
BIT(BE16(MRT_BGP4MP_ET)) | \
BIT(BE16(MRT_ISIS_ET)) | \
BIT(BE16(MRT_OSPFV3_ET)) \
)
#define _TABLE_DUMPV2_RIBMASK ( \
BIT(BE16(TABLE_DUMPV2_RIB_IPV4_UNICAST)) | \
BIT(BE16(TABLE_DUMPV2_RIB_IPV4_MULTICAST)) | \
BIT(BE16(TABLE_DUMPV2_RIB_IPV6_UNICAST)) | \
BIT(BE16(TABLE_DUMPV2_RIB_IPV6_MULTICAST)) | \
BIT(BE16(TABLE_DUMPV2_RIB_GENERIC)) | \
BIT(BE16(TABLE_DUMPV2_RIB_IPV4_UNICAST_ADDPATH)) | \
BIT(BE16(TABLE_DUMPV2_RIB_IPV4_MULTICAST_ADDPATH)) | \
BIT(BE16(TABLE_DUMPV2_RIB_IPV6_UNICAST_ADDPATH)) | \
BIT(BE16(TABLE_DUMPV2_RIB_IPV6_MULTICAST_ADDPATH)) | \
BIT(BE16(TABLE_DUMPV2_RIB_GENERIC_ADDPATH)) \
)
#define _TABLE_DUMPV2_V4RIBMASK ( \
BIT(BE16(TABLE_DUMPV2_RIB_IPV4_UNICAST)) | \
BIT(BE16(TABLE_DUMPV2_RIB_IPV4_MULTICAST)) | \
BIT(BE16(TABLE_DUMPV2_RIB_IPV4_UNICAST_ADDPATH)) | \
BIT(BE16(TABLE_DUMPV2_RIB_IPV4_MULTICAST_ADDPATH)) \
)
#define _TABLE_DUMPV2_V6RIBMASK ( \
BIT(BE16(TABLE_DUMPV2_RIB_IPV6_UNICAST)) | \
BIT(BE16(TABLE_DUMPV2_RIB_IPV6_MULTICAST)) | \
BIT(BE16(TABLE_DUMPV2_RIB_IPV6_UNICAST_ADDPATH)) | \
BIT(BE16(TABLE_DUMPV2_RIB_IPV6_MULTICAST_ADDPATH)) \
)
#define _TABLE_DUMPV2_UNICASTRIBMASK ( \
BIT(BE16(TABLE_DUMPV2_RIB_IPV4_UNICAST)) | \
BIT(BE16(TABLE_DUMPV2_RIB_IPV6_UNICAST)) | \
BIT(BE16(TABLE_DUMPV2_RIB_IPV4_UNICAST_ADDPATH)) | \
BIT(BE16(TABLE_DUMPV2_RIB_IPV6_UNICAST_ADDPATH)) \
)
#define _TABLE_DUMPV2_MULTICASTRIBMASK ( \
BIT(BE16(TABLE_DUMPV2_RIB_IPV4_MULTICAST)) | \
BIT(BE16(TABLE_DUMPV2_RIB_IPV6_MULTICAST)) | \
BIT(BE16(TABLE_DUMPV2_RIB_IPV4_MULTICAST_ADDPATH)) | \
BIT(BE16(TABLE_DUMPV2_RIB_IPV6_MULTICAST_ADDPATH)) \
)
#define _TABLE_DUMPV2_ADDPATHRIBMASK ( \
BIT(BE16(TABLE_DUMPV2_RIB_IPV4_UNICAST_ADDPATH)) | \
BIT(BE16(TABLE_DUMPV2_RIB_IPV4_MULTICAST_ADDPATH)) | \
BIT(BE16(TABLE_DUMPV2_RIB_IPV6_UNICAST_ADDPATH)) | \
BIT(BE16(TABLE_DUMPV2_RIB_IPV6_MULTICAST_ADDPATH)) | \
BIT(BE16(TABLE_DUMPV2_RIB_GENERIC_ADDPATH)) \
)
#define _BGP4MP_AS4MASK ( \
BIT(BE16(BGP4MP_MESSAGE_AS4)) | \
BIT(BE16(BGP4MP_STATE_CHANGE_AS4)) | \
BIT(BE16(BGP4MP_MESSAGE_AS4_LOCAL)) | \
BIT(BE16(BGP4MP_MESSAGE_AS4_ADDPATH)) | \
BIT(BE16(BGP4MP_MESSAGE_AS4_LOCAL_ADDPATH)) \
)
#define _BGP4MP_ADDPATHMASK ( \
BIT(BE16(BGP4MP_MESSAGE_ADDPATH)) | \
BIT(BE16(BGP4MP_MESSAGE_AS4_ADDPATH)) | \
BIT(BE16(BGP4MP_MESSAGE_LOCAL_ADDPATH)) | \
BIT(BE16(BGP4MP_MESSAGE_AS4_LOCAL_ADDPATH)) \
)
#define _BGP4MP_MESSAGEMASK ( \
BIT(BE16(BGP4MP_MESSAGE)) | \
BIT(BE16(BGP4MP_MESSAGE_AS4)) | \
BIT(BE16(BGP4MP_MESSAGE_LOCAL)) | \
BIT(BE16(BGP4MP_MESSAGE_AS4_LOCAL)) | \
BIT(BE16(BGP4MP_MESSAGE_ADDPATH)) | \
BIT(BE16(BGP4MP_MESSAGE_AS4_ADDPATH)) | \
BIT(BE16(BGP4MP_MESSAGE_LOCAL_ADDPATH)) | \
BIT(BE16(BGP4MP_MESSAGE_AS4_LOCAL_ADDPATH)) \
)
#define _ZEBRA_MESSAGEMASK ( \
BIT(BE16(ZEBRA_UPDATE)) | \
BIT(BE16(ZEBRA_OPEN)) | \
BIT(BE16(ZEBRA_NOTIFY)) | \
BIT(BE16(ZEBRA_KEEPALIVE)) \
)
/// Test whether a MRT record type provides extended precision timestamp header.
FORCE_INLINE Boolean MRT_ISEXHDRTYPE(MrtType type)
{
return BE16(type) <= 49 && (_MRT_EXHDRMASK & BIT(BE16(type))) != 0;
}
/// Test whether a MRT record type is BGP4MP.
FORCE_INLINE Boolean MRT_ISBGP4MP(MrtType type)
{
return type == MRT_BGP4MP || type == MRT_BGP4MP_ET;
}
/// Test whether a MRT record type is ISIS.
FORCE_INLINE Boolean MRT_ISISIS(MrtType type)
{
return type == MRT_ISIS || type == MRT_ISIS_ET;
}
/// Test whether a MRT record type is OSPFV3.
FORCE_INLINE Boolean MRT_ISOSPFV3(MrtType type)
{
return type == MRT_OSPFV3 || type == MRT_OSPFV3_ET;
}
/// Test whether a BGP4MP MRT record subtype contains 32-bits ASN.
FORCE_INLINE Boolean BGP4MP_ISASN32BIT(MrtSubType subtype)
{
return BE16(subtype) <= 11 && (_BGP4MP_AS4MASK & BIT(BE16(subtype))) != 0;
}
/// Test whether a BGP4MP MRT subtype belongs has ADD_PATH information.
FORCE_INLINE Boolean BGP4MP_ISADDPATH(MrtSubType subtype)
{
return BE16(subtype) <= 11 &&
(_BGP4MP_ADDPATHMASK & BIT(BE16(subtype))) != 0;
}
/**
* \brief Test whether a BGP4MP MRT subtype wraps a BGP message.
*
* \see `Bgp_UnwrapBgp4mp()`
*/
FORCE_INLINE Boolean BGP4MP_ISMESSAGE(MrtSubType subtype)
{
return BE16(subtype) <= 11 &&
(_BGP4MP_MESSAGEMASK & BIT(BE16(subtype))) != 0;
}
FORCE_INLINE Boolean BGP4MP_ISSTATECHANGE(MrtSubType subtype)
{
return subtype == BGP4MP_STATE_CHANGE ||
subtype == BGP4MP_STATE_CHANGE_AS4;
}
FORCE_INLINE Boolean TABLE_DUMPV2_ISRIB(MrtSubType subtype)
{
return BE16(subtype) <= 12 &&
(_TABLE_DUMPV2_RIBMASK & BIT(BE16(subtype))) != 0;
}
FORCE_INLINE Boolean TABLE_DUMPV2_ISIPV4RIB(MrtSubType subtype)
{
return BE16(subtype) <= 12 &&
(_TABLE_DUMPV2_V4RIBMASK & BIT(BE16(subtype))) != 0;
}
FORCE_INLINE Boolean TABLE_DUMPV2_ISIPV6RIB(MrtSubType subtype)
{
return BE16(subtype) <= 12 &&
(_TABLE_DUMPV2_V6RIBMASK & BIT(BE16(subtype))) != 0;
}
FORCE_INLINE Boolean TABLE_DUMPV2_ISGENERICRIB(MrtSubType subtype)
{
return subtype == TABLE_DUMPV2_RIB_GENERIC ||
subtype == TABLE_DUMPV2_RIB_GENERIC_ADDPATH;
}
FORCE_INLINE Boolean TABLE_DUMPV2_ISUNICASTRIB(MrtSubType subtype)
{
return BE16(subtype) <= 12 &&
(_TABLE_DUMPV2_UNICASTRIBMASK & BIT(BE16(subtype))) != 0;
}
FORCE_INLINE Boolean TABLE_DUMPV2_ISMULTICASTRIB(MrtSubType subtype)
{
return BE16(subtype) <= 12 &&
(_TABLE_DUMPV2_MULTICASTRIBMASK & BIT(BE16(subtype))) != 0;
}
FORCE_INLINE Boolean TABLE_DUMPV2_ISADDPATHRIB(MrtSubType subtype)
{
return BE16(subtype) <= 12 &&
(_TABLE_DUMPV2_ADDPATHRIBMASK & BIT(BE16(subtype))) != 0;
}
/**
* \brief Test whether a ZEBRA BGP message wraps BGP message.
*
* \see `Bgp_UnwrapZebra()`
*/
FORCE_INLINE Boolean ZEBRA_ISMESSAGE(MrtSubType subtype)
{
return BE16(subtype) <= 7 &&
(_ZEBRA_MESSAGEMASK & BIT(BE16(subtype))) != 0;
}
#pragma pack(push, 1)
/**
* \brief MRT header type.
*
* \warning **Misaligned struct**.
* \note Fields are in network order (big endian).
*/
typedef ALIGNED(1, struct) {
Uint32 timestamp; ///< Unix timestamp
MrtType type; ///< MRT record type
MrtSubType subtype; ///< MRT record subtype
Uint32 len; ///< Record length in bytes **not including** header itself
} Mrthdr;
/**
* \brief Extended MRT header, includes microseconds information.
*
* \warning **Misaligned struct**.
* \note Fields are in network order (big endian).
*
* \see `Mrthdr`
*/
typedef ALIGNED(1, struct) {
Uint32 timestamp;
MrtType type;
MrtSubType subtype;
Uint32 len;
Uint32 microsecs;
} Mrthdrex;
typedef ALIGNED(1, struct) {
Uint32 peerBgpId; ///< Peer BGP identifier
Uint16 viewNameLen; ///< `viewName` field length in chars
char viewName[FLEX_ARRAY]; ///< Optional view name, **not** `\0` terminated
// Uint16 peerCount;
// Mrtpeerent peers[FLEX_ARRAY];
} Mrtpeeridx;
#define MRT_PEER_IP6 BIT(0)
#define MRT_PEER_ASN32BIT BIT(1)
typedef ALIGNED(1, struct) {
Uint8 type;
Uint32 bgpId;
union {
struct {
Ipv4adr addr;
Asn16 asn;
} a16v4;
struct {
Ipv4adr addr;
Asn32 asn;
} a32v4;
struct {
Ipv6adr addr;
Asn16 asn;
} a16v6;
struct {
Ipv6adr addr;
Asn32 asn;
} a32v6;
};
} Mrtpeerentv2;
FORCE_INLINE Boolean MRT_ISPEERIPV6(Uint8 type)
{
return (type & MRT_PEER_IP6) != 0;
}
FORCE_INLINE Boolean MRT_ISPEERASN32BIT(Uint8 type)
{
return (type & MRT_PEER_ASN32BIT) != 0;
}
FORCE_INLINE Asn MRT_GETPEERADDR(Ipadr *dest, const Mrtpeerentv2 *peer)
{
switch (peer->type & (MRT_PEER_IP6|MRT_PEER_ASN32BIT)) {
case MRT_PEER_IP6|MRT_PEER_ASN32BIT:
dest->family = IP6;
dest->v6 = peer->a32v6.addr;
return ASN32BIT(peer->a32v6.asn);
case MRT_PEER_IP6:
dest->family = IP6;
dest->v6 = peer->a16v6.addr;
return ASN16BIT(peer->a16v6.asn);
case MRT_PEER_ASN32BIT:
dest->family = IP4;
dest->v4 = peer->a32v4.addr;
return ASN32BIT(peer->a32v4.asn);
default:
dest->family = IP4;
dest->v4 = peer->a16v4.addr;
return ASN16BIT(peer->a16v4.asn);
}
}
#define _MRT_RIBENT_FIELDS(IpT) \
IpT prefix; \
Uint8 prefixLen; \
Uint8 status; \
Uint32 originatedTime; \
IpT peerAddr; \
Uint16 peerAs; \
Uint16 attrLen
/**
* \brief Legacy TABLE_DUMP RIB entry.
*
* \warning **Misaligned struct**.
* \note Fields are in network order (big endian).
*/
typedef ALIGNED(1, struct) {
Uint16 viewno;
Uint16 seqno;
union {
struct {
_MRT_RIBENT_FIELDS(Ipv4adr);
// Uint8 attrs[FLEX_ARRAY];
} v4;
struct {
_MRT_RIBENT_FIELDS(Ipv6adr);
// Uint8 attrs[FLEX_ARRAY];
} v6;
};
} Mrtribent;
FORCE_INLINE Boolean RIB_GETPFX(Afi afi,
RawPrefix *dest,
const Mrtribent *rib)
{
EXTERNC void *memcpy(void *, const void *, size_t);
switch (afi) {
case AFI_IP:
if (rib->v4.prefixLen > IPV4_WIDTH)
return FALSE;
dest->width = rib->v4.prefixLen;
memcpy(dest->bytes, &rib->v4.prefix, PFXLEN(rib->v4.prefixLen));
return TRUE;
case AFI_IP6:
if (rib->v6.prefixLen > IPV6_WIDTH)
return FALSE;
dest->width = rib->v6.prefixLen;
memcpy(dest->bytes, &rib->v6.prefix, PFXLEN(rib->v6.prefixLen));
return TRUE;
default:
UNREACHABLE;
return FALSE;
}
}
FORCE_INLINE Uint32 RIB_GETORIGINATED(Afi afi, const Mrtribent *rib)
{
switch (afi) {
case AFI_IP: return rib->v4.originatedTime;
case AFI_IP6: return rib->v6.originatedTime;
default:
UNREACHABLE;
return 0;
}
}
FORCE_INLINE Asn RIB_GETPEERADDR(Afi afi, Ipadr *dest, const Mrtribent *rib)
{
switch (afi) {
case AFI_IP:
dest->family = afi;
dest->v4 = rib->v4.peerAddr;
return ASN16BIT(rib->v4.peerAs);
case AFI_IP6:
dest->family = afi;
dest->v6 = rib->v6.peerAddr;
return ASN16BIT(rib->v6.peerAs);
default:
UNREACHABLE;
return -1;
}
}
// NOTE: RIB AFI is stored inside `Mrthdr subtype` field.
FORCE_INLINE Bgpattrseg *RIB_GETATTRIBS(Afi afi, const Mrtribent *rib)
{
switch (afi) {
case AFI_IP: return (Bgpattrseg *) &rib->v4.attrLen;
case AFI_IP6: return (Bgpattrseg *) &rib->v6.attrLen;
default: UNREACHABLE; return NULL;
}
}
typedef ALIGNED(1, struct) {
Uint32 seqno;
union {
RawPrefix nlri;
struct {
Afi afi;
Safi safi;
RawPrefix nlri;
} gen;
};
} Mrtribhdrv2;
FORCE_INLINE Mrtribhdrv2 *RIBV2_HDR(const Mrthdr *hdr)
{
return (Mrtribhdrv2 *) (hdr + 1);
}
typedef ALIGNED(1, struct) {
Uint16 entryCount;
Uint8 entries[FLEX_ARRAY];
} Mrtribentriesv2;
/**
* \brief TABLE_DUMPV2 RIB entry.
*
* \warning **Misaligned struct**.
* \note Fields are in network order (big endian).
*/
typedef ALIGNED(1, struct) {
Uint16 peerIndex; ///< `Mrtpeerentv2` index inside PEER_INDEX_TABLE
Uint32 originatedTime;
Uint8 data[FLEX_ARRAY];
} Mrtribentv2;
FORCE_INLINE Bgpattrseg *RIBV2_GETATTRIBS(MrtSubType subtype,
const Mrtribentv2 *rib)
{
return (Bgpattrseg *) (TABLE_DUMPV2_ISADDPATHRIB(subtype) ?
rib->data + 4 :
rib->data);
}
FORCE_INLINE Uint32 RIBV2_GETPATHID(const Mrtribentv2 *ent)
{
EXTERNC void *memcpy(void *, const void *, size_t);
Uint32 pathid;
memcpy(&pathid, ent->data, sizeof(pathid));
return pathid;
}
FORCE_INLINE void RIBV2_SETPATHID(Mrtribentv2 *ent, Uint32 pathid)
{
EXTERNC void *memcpy(void *, const void *, size_t);
memcpy(ent->data, &pathid, sizeof(pathid));
}
FORCE_INLINE void RIBV2_GETNLRI(MrtSubType subtype,
Prefix *dest,
const Mrtribhdrv2 *hdr,
const Mrtribentv2 *ent)
{
EXTERNC void *memcpy(void *, const void *, size_t);
if (TABLE_DUMPV2_ISGENERICRIB(subtype)) {
dest->afi = hdr->gen.afi;
dest->safi = hdr->gen.safi;
memcpy(PLAINPFX(dest), &hdr->gen.nlri, 1 + PFXLEN(hdr->gen.nlri.width));
} else {
dest->afi = TABLE_DUMPV2_ISIPV6RIB(subtype) ?
AFI_IP6 :
AFI_IP;
dest->safi = TABLE_DUMPV2_ISMULTICASTRIB(subtype) ?
SAFI_MULTICAST :
SAFI_UNICAST;
memcpy(PLAINPFX(dest), &hdr->nlri, 1 + PFXLEN(hdr->nlri.width));
}
dest->isAddPath = TABLE_DUMPV2_ISADDPATHRIB(subtype);
dest->pathId = (dest->isAddPath) ? RIBV2_GETPATHID(ent) : 0;
}
#define _MRT_BGP4MP_COMFIELDS(AsnT) \
AsnT peerAs, localAs; \
Uint16 iface; \
Afi afi
#define _MRT_BGP4MP_HDRFIELDS(AsnT, IpT) \
_MRT_BGP4MP_COMFIELDS(AsnT); \
IpT peerAddr, localAddr
/// Common header fields to all BGP4MP MRT records.
typedef ALIGNED(1, union) {
struct {
_MRT_BGP4MP_COMFIELDS(Asn32);
} a32;
struct {
_MRT_BGP4MP_COMFIELDS(Asn16);
} a16;
struct {
_MRT_BGP4MP_HDRFIELDS(Asn16, Ipv4adr);
} a16v4;
struct {
_MRT_BGP4MP_HDRFIELDS(Asn16, Ipv6adr);
} a16v6;
struct {
_MRT_BGP4MP_HDRFIELDS(Asn32, Ipv4adr);
} a32v4;
struct {
_MRT_BGP4MP_HDRFIELDS(Asn32, Ipv6adr);
} a32v6;
} Bgp4mphdr;
FORCE_INLINE Bgp4mphdr *BGP4MP_HDR(const Mrthdr *hdr)
{
return (hdr->subtype == MRT_BGP4MP_ET) ?
(Bgp4mphdr *) ((const Mrthdrex *) hdr + 1) :
(Bgp4mphdr *) (hdr + 1);
}
FORCE_INLINE Asn BGP4MP_GETPEERADDR(MrtSubType subtype,
Ipadr *dest,
const Bgp4mphdr *bgp4mp)
{
if (BGP4MP_ISASN32BIT(subtype)) {
switch (bgp4mp->a32.afi) {
case AFI_IP:
dest->family = IP4;
dest->v4 = bgp4mp->a32v4.peerAddr;
break;
case AFI_IP6:
dest->family = IP6;
dest->v6 = bgp4mp->a32v6.peerAddr;
break;
default:
UNREACHABLE;
break;
}
return ASN32BIT(bgp4mp->a32.peerAs);
} else {
switch (bgp4mp->a16.afi) {
case AFI_IP:
dest->family = IP4;
dest->v4 = bgp4mp->a16v4.peerAddr;
break;
case AFI_IP6:
dest->family = IP6;
dest->v6 = bgp4mp->a16v6.peerAddr;
break;
default:
UNREACHABLE;
break;
}
return ASN16BIT(bgp4mp->a16.peerAs);
}
}
FORCE_INLINE Asn BGP4MP_GETLOCALADDR(MrtSubType subtype,
Ipadr *dest,
const Bgp4mphdr *bgp4mp)
{
if (BGP4MP_ISASN32BIT(subtype)) {
switch (bgp4mp->a32.afi) {
case AFI_IP:
dest->family = IP4;
dest->v4 = bgp4mp->a32v4.localAddr;
break;
case AFI_IP6:
dest->family = IP6;
dest->v6 = bgp4mp->a32v6.localAddr;
break;
default:
UNREACHABLE;
return -1;
}
return ASN32BIT(bgp4mp->a32.localAs);
} else {
switch (bgp4mp->a16.afi) {
case AFI_IP:
dest->family = IP4;
dest->v4 = bgp4mp->a16v4.localAddr;
break;
case AFI_IP6:
dest->family = IP6;
dest->v6 = bgp4mp->a16v6.localAddr;
break;
default:
UNREACHABLE;
return -1;
}
return ASN16BIT(bgp4mp->a16.localAs);
}
}
/// BGP4MP message providing BGP Finite State Machine (FSM) state change information.
typedef ALIGNED(1, union) {
Bgp4mphdr hdr;
struct {
_MRT_BGP4MP_HDRFIELDS(Asn16, Ipv4adr);
BgpFsmState oldState, newState;
} a16v4;
struct {
_MRT_BGP4MP_HDRFIELDS(Asn16, Ipv6adr);
BgpFsmState oldState, newState;
} a16v6;
struct {
_MRT_BGP4MP_HDRFIELDS(Asn32, Ipv4adr);
BgpFsmState oldState, newState;
} a32v4;
struct {
_MRT_BGP4MP_HDRFIELDS(Asn32, Ipv6adr);
BgpFsmState oldState, newState;
} a32v6;
} Bgp4mpstatchng;
typedef ALIGNED(1, struct) {
Uint16 peerAs;
Ipv4adr peerAddr;
} Zebrahdr;
FORCE_INLINE Zebrahdr *ZEBRA_HDR(const Mrthdr *hdr)
{
return (Zebrahdr *) (hdr + 1);
}
typedef ALIGNED(1, struct) {
Zebrahdr hdr;
Uint16 localAs;
Ipv4adr localAddr;
Uint8 msg[FLEX_ARRAY];
} Zebramsghdr;
typedef ALIGNED(1, struct) {
Zebrahdr hdr;
BgpFsmState oldState, newState;
} Zebrastatchng;
#pragma pack(pop)
/// MRT record header size in bytes.
#define MRT_HDRSIZ (4uLL + 2uLL + 2uLL + 4uLL)
/// Extended timestamp MRT record header size in bytes.
#define MRT_EXHDRSIZ (MRT_HDRSIZ + 4uLL)
STATIC_ASSERT(MRT_HDRSIZ == sizeof(Mrthdr), "MRT_HDRSIZ vs Mrthdr size mismatch");
STATIC_ASSERT(MRT_EXHDRSIZ == sizeof(Mrthdrex), "MRT_EXHDRSIZ vs Mrthdrex size mismatch");
/// Retrieve a pointer to a MRT record header.
FORCE_INLINE Mrthdr *MRT_HDR(const Mrtrecord *rec)
{
return (Mrthdr *) rec->buf;
}
typedef struct {
Uint8 *base, *lim;
Uint8 *ptr;
Uint16 peerCount;
Uint16 nextIdx;
} Mrtpeeriterv2;
typedef struct {
Uint8 *base, *lim;
Uint8 *ptr;
Boolean8 isAddPath;
Uint16 entryCount;
Uint16 nextIdx;
} Mrtribiterv2;
Judgement Bgp_MrtFromBuf(Mrtrecord *rec, const void *data, size_t nbytes);
Judgement Bgp_ReadMrt(Mrtrecord *rec, void *streamp, const StmOps *ops);
void Bgp_ClearMrt(Mrtrecord *rec);
// =========================================
// TABLE_DUMPV2 - PEER_INDEX_TABLE
Mrtpeeridx *Bgp_GetMrtPeerIndex(Mrtrecord *rec);
void *Bgp_GetMrtPeerIndexPeers(Mrtrecord *rec, size_t *peersCount, size_t *nbytes);
Judgement Bgp_StartMrtPeersv2(Mrtpeeriterv2 *it, Mrtrecord *rec);
Mrtpeerentv2 *Bgp_NextMrtPeerv2(Mrtpeeriterv2 *it);
Mrtpeerentv2 *Bgp_GetMrtPeerByIndex(Mrtrecord *rec, Uint16 idx);
// =========================================
// TABLE_DUMPV2 - RIB SubTypes
Mrtribhdrv2 *Bgp_GetMrtRibHdrv2(Mrtrecord *rec, size_t *nbytes);
Mrtribentriesv2 *Bgp_GetMrtRibEntriesv2(Mrtrecord *rec, size_t *nbytes);
Judgement Bgp_StartMrtRibEntriesv2(Mrtribiterv2 *it, Mrtrecord *rec);
Mrtribentv2 *Bgp_NextRibEntryv2(Mrtribiterv2 *rec);
// =========================================
// BGP4MP
Bgp4mphdr *Bgp_GetBgp4mpHdr(Mrtrecord *rec, size_t *nbytes);
Judgement Bgp_UnwrapBgp4mp(Mrtrecord *rec, Bgpmsg *msg, unsigned flags);
// =========================================
// DEPRECATED - TABLE_DUMP
Mrtribent *Bgp_GetMrtRibHdr(Mrtrecord *rec);
void *Bgp_GetMrtRibEntry(Mrtrecord *rec, size_t *nbytes);
// =========================================
// DEPRECATED - ZEBRA
Zebrahdr *Bgp_GetZebraHdr(Mrtrecord *rec, size_t *nbytes);
Judgement Bgp_UnwrapZebra(Mrtrecord *rec, Bgpmsg *msg, unsigned flags);
// ==========================================
// RIB attribute segment encoding formats
Judgement Bgp_StartAllRibv2NextHops(Nexthopiter *it,
const Mrthdr *hdr,
const Bgpattrseg *tpa,
Bgpattrtab tab);
Judgement Bgp_RebuildMsgFromRib(const Prefix *nlri,
const Bgpattrseg *tpa,
Bgpmsg *msg,
unsigned flags);
#endif

View File

@ -0,0 +1,44 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
/**
* \file bgp/patricia.h
*
* PATRICIA trie implementation.
*
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
* \author Lorenzo Cogotti
*/
#ifndef DF_BGP_PATRICIA_H_
#define DF_BGP_PATRICIA_H_
#include "bgp/prefix.h"
/// Opaque type, trie memory chunk block.
typedef struct Patblock Patblock;
/// Opaque type, concrete trie node.
typedef union Patnode Patnode;
/// PATRICIA trie.
typedef struct {
Afi afi; ///< AFI type
unsigned nprefixes; ///< Prefixes count stored inside trie
Patnode *head; ///< Trie root node
Patblock *blocks; ///< PATRICIA memory blocks
Patnode *freeBins[IPV6_SIZE / 4]; ///< Fast free bins
} Patricia;
RawPrefix *Pat_Insert(Patricia *trie, const RawPrefix *pfx);
Boolean Pat_Remove(Patricia *trie, const RawPrefix *pfx);
RawPrefix *Pat_SearchExact(const Patricia *trie, const RawPrefix *pfx);
Boolean Pat_IsSubnetOf(const Patricia *trie, const RawPrefix *pfx);
Boolean Pat_IsSupernetOf(const Patricia *trie, const RawPrefix *pfx);
Boolean Pat_IsRelatedOf(const Patricia *trie, const RawPrefix *pfx);
/// Reset `trie` and free all memory.
void Pat_Clear(Patricia *trie);
#endif

222
lonetix/include/df/bgp/prefix.h Executable file
View File

@ -0,0 +1,222 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
/**
* \file bgp/prefix.h
*
* Network prefixes types and utilities.
*
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
* \author Lorenzo Cogotti
*/
#ifndef DF_BGP_PREFIX_H_
#define DF_BGP_PREFIX_H_
#include "sys/ip.h"
/**
* \brief `Afi` values.
*
* \see [Address family numbers](https://www.iana.org/assignments/address-family-numbers/address-family-numbers.xhtml)
*
* \note Address family numbers are in network order (big endian).
*
* @{
*/
#define AFI_IP BE16(1)
#define AFI_IP6 BE16(2)
/**
* \brief Address Family Identifier, as defined by the BGP protocol.
*
* \note Address family numbers are in network order (big endian).
*/
typedef Uint16 Afi;
/** @} */
/**
* \brief `Safi` values.
*
* \see [SAFI namespace](https://www.iana.org/assignments/safi-namespace/safi-namespace.xhtml)
* \see [RFC 4760](http://www.iana.org/go/rfc4760)
*
* @{
*/
#define SAFI_UNICAST U8_C(1)
#define SAFI_MULTICAST U8_C(2)
/// Subsequent Address Family Identifier, as defined by the BGP protocol.
typedef Uint8 Safi;
/** @} */
#pragma pack(push, 1)
/**
* \brief BGP prefix with PATH ID information
*
* \warning **Misaligned struct**.
* \note Fields are in network order (big endian).
*/
typedef ALIGNED(1, struct) {
Uint32 pathId; ///< Path identifier
Uint8 width; ///< Prefix length in bits
/**
* \brief Prefix content `union`.
*
* \warning Only `PFXLEN(width)` bytes are relevant.
*/
union {
Uint8 bytes[16]; ///< Prefix raw bytes
Uint16 words[8]; ///< Prefix contents as a sequence of 16-bits values
Uint32 dwords[4]; ///< Prefix contents as a sequence of 32-bits values
Ipv4adr v4; ///< Prefix as a **full** IPv4 address
Ipv6adr v6; ///< Prefix as a **full** IPv6 address
};
} ApRawPrefix;
/**
* \brief BGP prefix with no PATH ID information.
*
* \warning **Misaligned struct**.
* \note Fields are in network order (big endian).
*/
typedef ALIGNED(1, struct) {
Uint8 width; ///< Prefix length in bits
/**
* \brief Prefix content `union`.
*
* \warning Only `PFXLEN(width)` bytes are relevant.
*/
union {
Uint8 bytes[16]; ///< Prefix raw bytes
Uint16 words[8]; ///< Prefix contents as a sequence of 16-bits values
Uint32 dwords[4]; ///< Prefix contents as a sequence of 32-bits values
Ipv4adr v4; ///< Prefix as a **full** IPv4 address
Ipv6adr v6; ///< Prefix as a **full** IPv6 address
};
} RawPrefix;
/**
* \brief "Fat" prefix structure, contains both the actual data and metadata about the BGP prefix itself.
*
* The structure doesn't reflect the actual BGP protocol format,
* it is used whenever a raw BGP data pointer isn't sufficient
* to convey enough context for a prefix (e.g. iterating every prefix available
* inside a BGP message).
*
* \warning **Misaligned struct**.
* \note Fields are in network order (big endian).
*
* \note Given the significant amount of metadata, it
* should be used sparingly, raw prefixes should be preferred
* whenever possible to save the overhead.
*/
typedef ALIGNED(1, struct) {
Boolean8 isAddPath; ///< Whether the path identifier is meaningful or not
Afi afi; ///< Prefix address family
Safi safi; ///< Prefix subsequent AFI
Uint32 pathId; ///< Path identifier (only meaningful if `isAddPath` is `TRUE`)
Uint8 width; ///< Prefix width, in bits (maximum legal value depends on AFI)
/**
* \brief Prefix content `union`.
*
* \warning Only `PFXLEN(width)` bytes are relevant.
*/
union {
Uint8 bytes[16]; ///< Prefix raw bytes
Uint16 words[8]; ///< Prefix contents as a sequence of 16-bits values
Uint32 dwords[4]; ///< Prefix contents as a sequence of 32-bits values
Ipv4adr v4; ///< Prefix as a **full** IPv4 address
Ipv6adr v6; ///< Prefix as a **full** IPv6 address
};
} Prefix;
#pragma pack(pop)
/// Calculate prefix length in bytes from bit width.
#define PFXLEN(width) ((size_t) (((width) >> 3) + (((width) & 7) != 0)))
/**
* \brief Return pointer to `RawPrefix` portion out of `ApRawPrefix`, `Prefix` or `RawPrefix` itself.
*
* Cast operation is useful to discard PATH ID information where irrelevant.
*/
#define PLAINPFX(prefix) ((RawPrefix *) (&(prefix)->width))
/// Return pointer to `ApRawPrefix` out of `Prefix` or `ApRawPrefix` itself.
#define APPFX(prefix) ((ApRawPrefix *) (&(prefix)->pathId))
/// Maximum length of an IPv6 prefix encoded as a string.
#define PFX6_STRLEN (IPV6_STRLEN + 1 + 3)
/// Maximum length of an IPv4 prefix encoded as a string.
#define PFX4_STRLEN (IPV4_STRLEN + 1 + 2)
/// Maximum length of an IPv6 prefix with PATH ID, encoded as a string.
#define APPFX6_STRLEN (10 + 1 + PFX6_STRLEN)
/// Maximum length of an IPv4 prefix with PATH ID, encoded as a string.
#define APPFX4_STRLEN (10 + 1 + PFX4_STRLEN)
/// Maximum length of a prefix encoded as a string.
#define PFX_STRLEN PFX6_STRLEN
/// Maximum length of a prefix with PATH ID, encoded as a string.
#define APPFX_STRLEN APPFX6_STRLEN
/**
* Convert a `RawPrefix` of the specified `Afi` to its string representation.
*
* \return Pointer to the trailing `\0` inside `dest`.
*
* \note Assumes `dest` is large enough to hold result (use `[PFX_STRLEN + 1]`)
*/
char *Bgp_PrefixToString(Afi afi, const RawPrefix *pfx, char *dest);
/**
* Convert an `ApRawPrefix` of the specified `Afi` to its string representation.
*
* \return Pointer to the trailing `\0` inside `dest`.
*
* \note Assumes `dest` is large enough to hold result (use `[APPFX_STRLEN + 1]`)
*/
char *Bgp_ApPrefixToString(Afi afi, const ApRawPrefix *pfx, char *dest);
/**
* \brief Convert string with format `address/width` to `Prefix`.
*
* \return `OK` on success, `NG` on invalid prefix string.
*
* \note Does not handle PATH ID, may only return plain prefixes.
*/
Judgement Bgp_StringToPrefix(const char *s, Prefix *dest);
/**
* \brief Direct iterator over raw prefix data.
*
* \note `struct` should be considered opaque.
*/
typedef struct {
Afi afi;
Safi safi;
Boolean8 isAddPath;
Uint8 *base, *lim;
Uint8 *ptr;
} Prefixiter;
/**
* \brief Start iterating `nbytes` bytes from `data` for prefixes of the specified `afi` and `safi`.
*
* \return `OK` on success, `NG` on error. Sets BGP error, see `Bgp_GetErrStat()`.
*/
Judgement Bgp_StartPrefixes(Prefixiter *it, Afi afi, Safi safi, const void *data, size_t nbytes, Boolean isAddPath);
/**
* \brief Get current prefix and advance iterator.
*
* \return Current prefix on success, depending on `isAddPath` prefix type
* may be either `RawPrefix` or `ApRawPrefix`. `NULL` on iteration end or
* error. Sets BGP error, see `Bgp_GetErrStat()`.
*/
void *Bgp_NextPrefix(Prefixiter *it);
#endif

385
lonetix/include/df/bgp/vm.h Executable file
View File

@ -0,0 +1,385 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
/**
* \file bgp/vm.h
*
* BGP message filtering engine.
*
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
* \author Lorenzo Cogotti
*/
#ifndef DF_BGP_VM_H_
#define DF_BGP_VM_H_
#include "bgp.h"
/**
* \name BGP VM bytecode
*
* \brief Filtering engine instruction OPCODEs and arguments.
*
* Bytecode format is:
* ```
* LSB MSB (NATIVE endianness)
* +--------+--------+
* | OPCODE | ARG |
* +--------+--------+
* 0 16 bit
*
* (2 bytes per instruction)
* ```
*
* Bytecode follows native machine endianness, it is NOT endian independent.
*
* @{
*/
/// BGP filter VM OPCODE
typedef Uint8 Bgpvmopc;
/// BGP filter VM bytecode instruction
typedef Uint16 Bgpvmbytec;
/// Returns a VM bytecode with the specified opcode and argument.
#define BGP_VMOP(opc, arg) ((Bgpvmbytec) ((((Uint8) (arg)) << 8) | ((Bgpvmopc) opc)))
/// Extract OPCODE from the specified bytecode.
FORCE_INLINE Bgpvmopc BGP_VMOPC(Bgpvmbytec bytec)
{
return (Bgpvmopc) (bytec & 0xff);
}
/// Extract direct argument from the specified bytecode.
FORCE_INLINE Uint8 BGP_VMOPARG(Bgpvmbytec bytec)
{
return (bytec >> 8);
}
/// NO-OPERATION - does nothing and moves on
#define BGP_VMOP_NOP U8_C(0)
/// LOAD - Pushes `ARG` constant on stack, interpreting it as a `Sint8`
#define BGP_VMOP_LOAD U8_C(1)
/// LOAD UNSIGNED - Pushes `ARG` constant on stack, interpreting it as an `Uint8`
#define BGP_VMOP_LOADU U8_C(2)
/// LOAD NULL - Push a `NULL` address on stack
#define BGP_VMOP_LOADN U8_C(3)
/// LOADK - Push a new constant (K) on stack, `ARG` is the index inside `K`
#define BGP_VMOP_LOADK U8_C(4)
/// CALL - invoke an external native function
#define BGP_VMOP_CALL U8_C(5)
/// BLOCK OPEN - Push a new matching block (used to implement AND/OR chains)
#define BGP_VMOP_BLK U8_C(6)
/// END BLOCK - Pops the current matching block
#define BGP_VMOP_ENDBLK U8_C(7)
/// TAG LAST MATCH - Tags last operation's match with `ARG`
#define BGP_VMOP_TAG U8_C(8)
/// NOT - Boolean negate the topmost stack element
#define BGP_VMOP_NOT U8_C(9)
/// CONDITIONAL FAIL If TRUE - Fail the current matching `BLK` if topmost stack element is non-zero
#define BGP_VMOP_CFAIL U8_C(10)
/// CONDITIONAL PASS If TRUE - Pass the current matching `BLK` if topmost stack element is non-zero
#define BGP_VMOP_CPASS U8_C(11)
/// Jump if zero - Skip over a positive number of instructions if topmost stack element is 0.
#define BGP_VMOP_JZ U8_C(12)
/// Jump if non-zero - Skip over a positive number of instructions if topmost stack element is not 0.
#define BGP_VMOP_JNZ U8_C(13)
/// CHECK TYPE - ARG is the `BgpType` to test against
#define BGP_VMOP_CHKT U8_C(14)
/// CHECK ATTRIBUTE - ARG is the `BgpAttrCode` to test for existence
#define BGP_VMOP_CHKA U8_C(15)
#define BGP_VMOP_EXCT U8_C(16)
#define BGP_VMOP_SUPN U8_C(17)
#define BGP_VMOP_SUBN U8_C(18)
/// RELATED - Tests whether the BGP message contains prefixes related with the provided ones
#define BGP_VMOP_RELT U8_C(19)
/// Returns `TRUE` if `opc` belongs to an instruction operating on NETwork prefixes.
FORCE_INLINE Boolean BGP_ISVMOPNET(Bgpvmopc opc)
{
return opc >= BGP_VMOP_EXCT && opc <= BGP_VMOP_RELT;
}
/// AS PATH MATCH - Tests BGP message AS PATH against a match expression
#define BGP_VMOP_ASMTCH U8_C(20)
/// FAST AS PATH MATCH - AS PATH test using precompiled AS PATH match expression
#define BGP_VMOP_FASMTC U8_C(21)
/// COMMUNITY MATCH - COMMUNITY test using a precompiled COMMUNITY match expression
#define BGP_VMOP_COMTCH U8_C(22)
/// ALL COMMUNITY MATCH - Like COMTCH, but requires all communities to be present inside message
#define BGP_VMOP_ACOMTC U8_C(23)
/// END - Terminate VM execution with the latest result
#define BGP_VMOP_END U8_C(24)
// #define BGP_VMOP_MOVK MOVE K - Move topmost K index to ARG K index
// #define BGP_VMOP_DISCRD DISCARD - discard vm->curMatch if any
// Bytecode `ARG` values for `NET` class instructions
#define BGP_VMOPA_NLRI U8_C(0)
#define BGP_VMOPA_MPREACH U8_C(1)
#define BGP_VMOPA_ALL_NLRI U8_C(2)
#define BGP_VMOPA_WITHDRAWN U8_C(3)
#define BGP_VMOPA_MPUNREACH U8_C(4)
#define BGP_VMOPA_ALL_WITHDRAWN U8_C(5)
// Special `Asn` values for `ASMTCH` and `FASMTC` instructions
#define ASNNOTFLAG BIT(61)
/// Matches any ASN **except** the provided one.
#define ASNNOT(asn) ((Asn) ((asn) | ASNNOTFLAG))
/// Tests whether `asn` is a negative ASN match.
FORCE_INLINE Boolean ISASNNOT(Asn asn)
{
return (asn & ASNNOTFLAG) != 0;
}
/// Match with the AS_PATH start (^)
#define ASN_START ((Asn) 0x0000000100000000LL)
/// Match with the AS PATH end ($).
#define ASN_END ((Asn) 0x0000000200000000LL)
/// Match with any ASN (.).
#define ASN_ANY ((Asn) 0x0000000300000000LL)
/// Match zero or more ASN (*).
#define ASN_STAR ((Asn) 0x0000000400000000LL)
/// Match with zero or one ASN (?).
#define ASN_QUEST ((Asn) 0x0000000500000000LL)
/// Match the previous ASN one or more times (+).
#define ASN_PLUS ((Asn) 0x0000000600000000LL)
/// Introduce a new group (opening paren for expressions like `( a b c )`)
#define ASN_NEWGRP ((Asn) 0x0000000700000000LL)
/// Introduce alternative inside matching expression (pipe symbol for expressions like `( a b | b c )`)
#define ASN_ALT ((Asn) 0x0000000800000000LL)
/// Terminate a group expression (closing paren for expressions like `( a b c )`)
#define ASN_ENDGRP ((Asn) 0x0000000900000000LL)
/** @} */
/**
* \brief Stack slot.
*
* The VM stack is a sequence of `Bgpvmval` cells,
* interpreted by each instruction as dictated by the opcode.
*/
typedef union Bgpvmval Bgpvmval;
union Bgpvmval {
void *ptr; ///< Value as a pointer
Sint64 val; ///< Value as a signed integral
};
/// Match info for AS matching expressions.
typedef struct Bgpvmasmatch Bgpvmasmatch;
struct Bgpvmasmatch {
Bgpvmasmatch *next;
Aspathiter spos;
Aspathiter epos;
};
/// Optimization modes for some matching instructions (e.g. BGP_VMOP_COMTCH/BGP_VMOP_ACOMTC).
typedef enum {
BGP_VMOPT_NONE, ///< Do not optimize.
// For COMTCH/ACOMTC
BGP_VMOPT_ASSUME_COMTCH, ///< Assume instruction is going to be `COMTCH`
BGP_VMOPT_ASSUME_ACOMTC ///< Assume instruction is going to be `ACOMTC`
} BgpVmOpt;
typedef struct Bgpmatchcomm Bgpmatchcomm;
struct Bgpmatchcomm {
Boolean8 maskHi; // don't match HI (match of type *:N)
Boolean8 maskLo; // don't match LO (match of type N:*)
Bgpcomm c;
};
/**
* \brief Matching operation result on a BGP message.
*
* Collect relevant information on a matching operation, including the
* direct byte range and position inside BGP message data.
*
* This structure may be used to further process BGP data after the filtering
* is complete. A `Bgpvmmatch` structure is generated for several BGP VM
* OPCODEs, and is only valid up to the next [Bgp_VmExec()](@ref Bgp_VmExec)
* call on the same VM that originated them.
*/
typedef struct Bgpvmmatch Bgpvmmatch;
struct Bgpvmmatch {
Uint32 pc; ///< instruction index that originated this match
Boolean8 isMatching; ///< whether this result declares a match or a mismatch
Boolean8 isPassing; ///< whether this result produced a `PASS` or a `FAIL` inside filter
Uint8 tag; ///< optional tag id for this match (as set by `TAG`)
Uint8 *base, *lim; ///< relevant BGP message segment, if any
void *pos; ///< pointer to detailed match-specific information
Bgpvmmatch *nextMatch; ///< next match in chain (`NULL` if this is the last element)
};
/// Filtering engine error code (a subset of [BgpRet](@ref BgpRet).
typedef Sint8 BgpvmRet;
/// Maximum number of VM constants inside [Bgpvm](@ref Bgpvm).
#define MAXBGPVMK 256
/// Maximum number of VM callable functions inside [Bgpvm](@ref Bgpvm).
#define MAXBGPVMFN 32
/// Maximum allowed nested grouping levels inside a `BGP_VMOP_ASMTCH`.
#define MAXBGPVMASNGRP 32
/**
* \brief Bytecode-based virtual machine operating on BGP messages.
*
* Extensible programmable BGP message matching and filtering engine.
*/
typedef struct Bgpvm Bgpvm;
struct Bgpvm {
Uint32 pc; ///< VM program counter inside `prog`
Uint32 nblk; ///< nested conditional block count
Uint32 nmatches; ///< current execution matches count (length of the `matches` list)
Uint16 si; ///< VM Stack index
Uint16 nk; ///< count of constants (K) available in `k`
Uint8 nfuncs; ///< count of functions (FN) available in `funcs`
Boolean8 setupFailed; ///< whether a `Bgp_VmEmit()` or `Bgp_VmPermAlloc()` on this VM ever failed.
Boolean8 isRunning; ///< whether the VM is being executed
BgpvmRet errCode; ///< whether the VM encountered an error
Uint32 hLowMark; ///< VM heap low memory mark
Uint32 hHighMark; ///< VM heap high memory mark
Uint32 hMemSiz; ///< VM heap size in bytes
Uint32 progLen; ///< bytecode program instruction count
Uint32 progCap; ///< bytecode segment instruction capacity
Bgpmsg *msg; ///< current BGP message being processed
Bgpvmbytec *prog; ///< VM program bytecode, `progLen` instructions (`prog[progLen]` is always `BGP_VMOP_END`)
/**
* Filtering VM heap, managed as follows:
* ```
* hLowMark hHighMark
* heap -+ v v
* v STACK grows upwards .-.-> <.-.-. TEMP grows downwards
* +=====================\-------------------/=============+
* | PERM ALLOCS | STACK > < TEMP ALLOCS |
* +=====================/-------------------\=============+
* [--------------------- hMemSiz -------------------------]
*
* PERM ALLOCS: Allocations that last forever (until the VM is freed)
* - such allocations CANNOT happen while VM is running
*
* TEMP ALLOCS: Allocations that last up to the next Bgp_VmExec()
* - such allocations may only take place while VM is running
* ```
*/
void *heap;
Bgpvmmatch *curMatch; ///< current match being updated during VM execution
Bgpvmmatch *matches; ///< matches produced during execution (contains `nmatches` elements)
Bgpvmval k[MAXBGPVMK]; ///< VM constants (K), `nk` allocated
void (*funcs[MAXBGPVMFN])(Bgpvm *); ///< VM functions (FN), `nfuncs` allocated
};
/// Clear the `errCode` error flag on `vm`.
FORCE_INLINE void BGP_VMCLRERR(Bgpvm *vm)
{
vm->errCode = BGPENOERR;
}
/// Clear the `setupFailed` error flag on `vm`.
FORCE_INLINE void BGP_VMCLRSETUP(Bgpvm *vm)
{
vm->setupFailed = FALSE;
}
FORCE_INLINE Sint32 Bgp_VmNewk(Bgpvm *vm)
{
if (vm->nk >= MAXBGPVMK)
return -1;
return vm->nk++;
}
FORCE_INLINE Sint32 Bgp_VmNewFn(Bgpvm *vm)
{
if (vm->nfuncs >= MAXBGPVMFN)
return -1;
return vm->nfuncs++;
}
FORCE_INLINE Sint32 BGP_VMSETKA(Bgpvm *vm, Sint32 kidx, void *ptr)
{
if (kidx >= 0)
vm->k[kidx].ptr = ptr;
return kidx;
}
FORCE_INLINE Sint32 BGP_VMSETK(Bgpvm *vm, Sint32 kidx, Sint64 val)
{
if (kidx >= 0)
vm->k[kidx].val = val;
return kidx;
}
FORCE_INLINE Sint32 BGP_VMSETFN(Bgpvm *vm, Sint32 idx, void (*fn)(Bgpvm *))
{
if (idx >= 0)
vm->funcs[idx] = fn;
return idx;
}
/**
* \brief Initialize a new VM with the specified heap size.
*
* \return `OK` on success, `NG` on out of memory, sets BGP error, VM error,
* and, on failure, VM setup failure flag.
*/
Judgement Bgp_InitVm(Bgpvm *vm, size_t heapSiz);
/**
* \brief Emit a VM bytecode instruction to `vm`.
*
* \return `OK` if instruction was added successfully, `NG` on out of memory,
* sets BGP error, VM error and, on failure, VM setup failure flag.
*
* \note Emitting `BGP_VMOP_END` has no effect.
*/
Judgement Bgp_VmEmit(Bgpvm *vm, Bgpvmbytec bytec);
/**
* \brief Precompile an AS PATH match expression for use with BGP_VMOP_FASMTC.
*
* \return Pointer suitable as the argument of `BGP_VMOP_FASMTC` instruction
* on success, `NULL` on failure.
* Precompiled expression is stored inside `vm` permanent heap.
*/
void *Bgp_VmCompileAsMatch(Bgpvm *vm, const Asn *match, size_t n);
void *Bgp_VmCompileCommunityMatch(Bgpvm *vm, const Bgpmatchcomm *match, size_t n, BgpVmOpt opt);
/**
* \brief Perform a permanent heap allocation of `size` bytes to `vm`.
*
* \return `vm` heap pointer to memory zone on success, `NULL` on failure,
* sets BGP error, VM error, and, on failure, `vm` setup failure flag.
*
* \note This function may only be called if `vm` is not executing!
*/
void *Bgp_VmPermAlloc(Bgpvm *vm, size_t size);
void *Bgp_VmTempAlloc(Bgpvm *vm, size_t size);
void Bgp_VmTempFree(Bgpvm *vm, size_t size);
/**
* \brief Execute `vm` bytecode on `msg`.
*
* \return `TRUE` if `vm` terminated with PASS, `FALSE` if it terminated on
* FAIL, or if an error was encountered. Sets BGP error and VM error.
*/
Boolean Bgp_VmExec(Bgpvm *vm, Bgpmsg *msg);
/// Print `vm` bytecode dump to stream.
void Bgp_VmDumpProgram(Bgpvm *vm, void *streamp, const StmOps *ops);
/// Reset `vm` state, but keep allocated memory for further setup.
void Bgp_ResetVm(Bgpvm *vm);
/// Clear `vm` and free all memory.
void Bgp_ClearVm(Bgpvm *vm);
#endif

250
lonetix/include/df/bgp/vmintrin.h Executable file
View File

@ -0,0 +1,250 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
/**
* \file bgp/vmintrin.h
*
* BGP VM engine operation intrinsics.
*
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
* \author Lorenzo Cogotti
*
* Utilities in this file are meant for low level VM interaction,
* usually to implement actual VM extensions.
*/
#ifndef DF_BGP_VMINTRIN_H_
#define DF_BGP_VMINTRIN_H_
#include "bgp/vm.h"
/// Get current VM program counter.
FORCE_INLINE Uint32 BGP_VMCURPC(Bgpvm *vm)
{
return vm->pc - 1; // PC always references *next* instruction
}
/// Get VM stack base pointer.
FORCE_INLINE Bgpvmval *BGP_VMSTK(Bgpvm *vm)
{
return (Bgpvmval *) ((Uint8 *) vm->heap + vm->hLowMark);
}
/// Get stack value at index `idx`, -1 is topmost value, -2 is second to topmost...
FORCE_INLINE Bgpvmval *BGP_VMSTKGET(Bgpvm *vm, Sint32 idx)
{
Bgpvmval *stk = BGP_VMSTK(vm);
return &stk[vm->si + idx];
}
/// Equivalent to `BGP_VMSTKGET()`, but returns stack content as `Sint64`.
FORCE_INLINE Sint64 BGP_VMPEEK(Bgpvm *vm, Sint32 idx)
{
return BGP_VMSTKGET(vm, idx)->val;
}
/// Equivalent to `BGP_VMSTKGET()`, but returns stack content as `void *`.
FORCE_INLINE void *BGP_VMPEEKA(Bgpvm *vm, Sint32 idx)
{
return BGP_VMSTKGET(vm, idx)->ptr;
}
/// Pop `n` values from VM stack, **assumes stack is large enough**.
FORCE_INLINE void BGP_VMPOPN(Bgpvm *vm, Uint32 n)
{
vm->si -= n;
}
/// Pop topmost stack value in VM, returning its value as `Sint64`, **assumes stack is not empty**.
FORCE_INLINE Sint64 BGP_VMPOP(Bgpvm *vm)
{
Bgpvmval *stk = BGP_VMSTK(vm);
return stk[--vm->si].val;
}
/// Like `BGP_VMPOP()`, but returns value as `void *`.
FORCE_INLINE void *BGP_VMPOPA(Bgpvm *vm)
{
Bgpvmval *stk = BGP_VMSTK(vm);
return stk[--vm->si].ptr;
}
/// Push `v` to stack, **assumes enough stack space is available**.
FORCE_INLINE void BGP_VMPUSH(Bgpvm *vm, Sint64 v)
{
Bgpvmval *stk = BGP_VMSTK(vm);
stk[vm->si++].val = v;
}
/// Like `BGP_VMPUSH()`, but pushes a pointer.
FORCE_INLINE void BGP_VMPUSHA(Bgpvm *vm, void *p)
{
Bgpvmval *stk = BGP_VMSTK(vm);
stk[vm->si++].ptr = p;
}
/// Ensure at least `n` elements may be popped from the stack.
FORCE_INLINE Boolean BGP_VMCHKSTKSIZ(Bgpvm *vm, Uint32 n)
{
if (vm->si < n) {
vm->errCode = BGPEVMUFLOW;
return FALSE;
}
return TRUE;
}
/// Ensure at least `n` elements may be pushed to the stack.
FORCE_INLINE Boolean BGP_VMCHKSTK(Bgpvm *vm, Uint32 n)
{
size_t siz = vm->si + n;
siz *= sizeof(Bgpvmval);
if (vm->hHighMark - vm->hLowMark < siz) {
vm->errCode = BGPEVMOFLOW;
return FALSE;
}
return TRUE;
}
/**
* \brief Test whether `vm->msg` header type matches `type`.
*
* \return Pointer to message header on successful match, `NULL`
* otherwise.
*/
FORCE_INLINE Bgphdr *BGP_VMCHKMSGTYPE(Bgpvm *vm, BgpType type)
{
Bgphdr *hdr = BGP_HDR(vm->msg);
return (hdr->type == type) ? hdr : NULL;
}
Judgement Bgp_VmStoreMsgTypeMatch(Bgpvm *vm, Boolean);
void Bgp_VmStoreMatch(Bgpvm *vm);
/// Implement `LOAD`.
FORCE_INLINE void Bgp_VmDoLoad(Bgpvm *vm, Sint8 val)
{
if (!BGP_VMCHKSTK(vm, 1))
return;
BGP_VMPUSH(vm, val);
}
/// Implement `LOADU`.
FORCE_INLINE void Bgp_VmDoLoadu(Bgpvm *vm, Uint8 val)
{
if (!BGP_VMCHKSTK(vm, 1))
return;
BGP_VMPUSH(vm, val);
}
/// Implement `LOADK` of `vm->k[idx]`.
FORCE_INLINE void Bgp_VmDoLoadk(Bgpvm *vm, Uint8 idx)
{
if (idx >= vm->nk) {
vm->errCode = BGPEVMBADK;
return;
}
if (!BGP_VMCHKSTK(vm, 1))
return;
Bgpvmval *stk = BGP_VMSTK(vm);
stk[vm->si++] = vm->k[idx];
}
/// Implement `LOADN`.
FORCE_INLINE void Bgp_VmDoLoadn(Bgpvm *vm)
{
if (!BGP_VMCHKSTK(vm, 1)) {
vm->errCode = BGPEVMOFLOW;
return;
}
BGP_VMPUSHA(vm, NULL);
}
/// Break out of current `BLK`.
FORCE_INLINE void Bgp_VmDoBreak(Bgpvm *vm)
{
Bgpvmopc opc;
do
opc = BGP_VMOPC(vm->prog[vm->pc++]);
while (opc != BGP_VMOP_ENDBLK && opc != BGP_VMOP_END);
if (opc == BGP_VMOP_ENDBLK)
vm->nblk--;
}
/// Execute `CALL` of function `vm->funcs[idx]`.
FORCE_INLINE void Bgp_VmDoCall(Bgpvm *vm, Uint8 idx)
{
void (*fn)(Bgpvm *);
if (idx >= vm->nfuncs) {
vm->errCode = BGPEVMBADFN;
return;
}
fn = vm->funcs[idx];
if (fn) fn(vm);
}
/// Implement `CPASS` (Conditional `PASS` if `TRUE`).
Boolean Bgp_VmDoCpass(Bgpvm *vm);
/// Implement `CFAIL` (Conditional `FAIL` if `TRUE`).
Boolean Bgp_VmDoCfail(Bgpvm *vm);
/// Implement `TAG` instruction with argument `tag`.
FORCE_INLINE void Bgp_VmDoTag(Bgpvm *vm, Uint8 tag)
{
vm->curMatch->tag = tag;
}
/**
* \brief Implements VM `NOT` instruction.
*
* Negate stack topmost value.
*/
FORCE_INLINE void Bgp_VmDoNot(Bgpvm *vm)
{
// Expected STACK:
// -1: Any value interpreted as Sint64
if (!BGP_VMCHKSTKSIZ(vm, 1))
return;
Bgpvmval *v = BGP_VMSTKGET(vm, -1);
v->val = !v->val;
}
/// Implements `CHKT` with argument `type`.
void Bgp_VmDoChkt(Bgpvm *vm, BgpType type);
/// Implements `CHKA` with argument `code`.
void Bgp_VmDoChka(Bgpvm *vm, BgpAttrCode code);
void Bgp_VmDoExct(Bgpvm *vm, Uint8 arg);
void Bgp_VmDoSupn(Bgpvm *vm, Uint8 arg);
void Bgp_VmDoSubn(Bgpvm *vm, Uint8 arg);
void Bgp_VmDoRelt(Bgpvm *vm, Uint8 arg);
void Bgp_VmDoAsmtch(Bgpvm *vm);
void Bgp_VmDoFasmtc(Bgpvm *vm);
void Bgp_VmDoComtch(Bgpvm *vm);
void Bgp_VmDoAcomtc(Bgpvm *vm);
#endif

162
lonetix/include/df/bufio.h Normal file
View File

@ -0,0 +1,162 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
/**
* \file bufio.h
*
* Buffered stream writing utilities.
*
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
* \author Lorenzo Cogotti
*/
#ifndef DF_BUFIO_H_
#define DF_BUFIO_H_
#include "stm.h"
#include <stdarg.h>
/// `Stmbuf` buffer size in bytes
#define STM_BUFSIZ 8192uLL
/**
* Buffered output helper structure.
*
* A small `struct` holding an output buffer to help
* and reduce calls to a stream's `Write()` operation.
*/
typedef struct {
Sint64 total; ///< Total bytes flushed to output
Uint32 len; ///< Bytes currently buffered
char buf[STM_BUFSIZ]; ///< Output buffer
void *streamp; ///< Output stream pointer
const StmOps *ops; ///< Output stream operations
} Stmbuf;
/**
* Flush the buffer to output stream.
*
* \return On success returns the **total** bytes written to output
* stream since last call to `Bufio_Init()`,
* that is the value stored inside `sb->total` field after the flush
* operations. Otherwise returns -1.
*
* \note Partial flushes are possible on partial writes, in which case
* some amount of data will remain buffered in `sb` and may be
* flushed later on; `sb->total` and `sb->len` will still be updated
* consistently.
*/
Sint64 Bufio_Flush(Stmbuf *sb);
/**
* Initialize the buffer for writing to `streamp` using the
* `ops` stream operations.
*
* \param [out] sb Buffer to be initialized, must not be `NULL`
* \param [in] streamp Output stream pointer
* \param [in] ops Output stream operations, must not be `NULL`
* and must provide a `Write()` operation
*/
FORCE_INLINE void Bufio_Init(Stmbuf *sb,
void *streamp,
const StmOps *ops)
{
sb->total = 0;
sb->len = 0;
sb->streamp = streamp;
sb->ops = ops;
}
/**
* Write a value to buffer, formatted as string.
*
* \param [in,out] sb Buffer to write to, must not be `NULL`
* \param [in] val Value to be stringified and written to `sb`
*
* \return Number of bytes written to buffer on success,
* -1 on error.
*
* @{
* \fn Sint64 Bufio_Putu(Stmbuf *, unsigned long long)
* \fn Sint64 Bufio_Putx(Stmbuf *, unsigned long long)
* \fn Sint64 Bufio_Puti(Stmbuf *, long long)
* \fn Sint64 Bufio_Putf(Stmbuf *, double)
* @}
*/
Sint64 Bufio_Putu(Stmbuf *sb, unsigned long long val);
Sint64 Bufio_Putx(Stmbuf *sb, unsigned long long val);
Sint64 Bufio_Puti(Stmbuf *sb, long long val);
Sint64 Bufio_Putf(Stmbuf *sb, double val);
/**
* Write a single character to `sb`.
*
* \return Number of bytes written to `sb` on success (equals to 1),
* -1 on error.
*
* \note `\0` may be written and buffered like any other `char`.
*/
FORCE_INLINE Sint64 Bufio_Putc(Stmbuf *sb, char c)
{
if (sb->len == sizeof(sb->buf) && Bufio_Flush(sb) == -1)
return -1;
sb->buf[sb->len++] = c;
return 1;
}
/**
* \def Bufio_Putsn
*
* Write a fixed amount of characters from a string to buffer.
*
* \param [in,out] sb Buffer to write to, must not be `NULL`
* \param [in] s String to pick the characters from
* \param [in] nbytes Bytes count to be written to `sb`
*
* \return Number of bytes written to `sb` on success (equal to
* `nbytes`), -1 on error.
*/
Sint64 _Bufio_Putsn(Stmbuf *, const char *, size_t);
#ifdef __GNUC__
// Optimize to call Bufio_Putc() if 'nbytes' is statically known to be 1
// NOTE: Avoids needless EOLN overhead on Unix
#define Bufio_Putsn(sb, s, nbytes) ( \
(__builtin_constant_p(nbytes) && (nbytes) == 1) ? \
Bufio_Putc(sb, (s)[0]) : \
_Bufio_Putsn(sb, s, nbytes) \
)
#else
#define Bufio_Putsn(sb, s, nbytes) _Bufio_Putsn(sb, s, nbytes)
#endif
/**
* Write string to buffer.
*
* \return Number of bytes written to `sb` on success (equal
* to string length), -1 on error.
*/
FORCE_INLINE Sint64 Bufio_Puts(Stmbuf *sb, const char *s)
{
EXTERNC size_t strlen(const char *); // avoids #include
return Bufio_Putsn(sb, s, strlen(s));
}
/**
* `printf()`-like formatted text print to buffer.
*
* Write formatted string to buffer, like regular `fprintf()`.
*
* \return Number of bytes written to `sb` on success, -1 on error.
*
* @{
* \fn Sint64 Bufio_Printf(Stmbuf *, const char *, ...)
* \fn Sint64 Bufio_Vprintf(Stmbuf *, const char *, va_list)
* @}
*/
CHECK_PRINTF(2, 3) Sint64 Bufio_Printf(Stmbuf *, const char *, ...);
CHECK_PRINTF(2, 0) Sint64 Bufio_Vprintf(Stmbuf *, const char *, va_list);
#endif

347
lonetix/include/df/chkint.h Normal file
View File

@ -0,0 +1,347 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
/**
* \file chkint.h
*
* Overflow checked integer arithmetics.
*
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
* \author Lorenzo Cogotti
*
* This API was modeled after D Language `core.checkedint` module,
* available under the [Boost License 1.0](http://www.boost.org/LICENSE_1_0.txt),
* and originally written by Walter Bright.
*
* \see [How Should You Write a Fast Integer Overflow Check?](https://blog.regehr.org/archives/1139)
* \see [D Language Phobos documentation](https://dlang.org/phobos/core_checkedint.html)
*/
#ifndef DF_CHKINT_H_
#define DF_CHKINT_H_
#include "xpt.h"
#include <limits.h>
// Define this to force portable C implementation of the checked int API
// #define DF_C_ONLY_CHKINT
FORCE_INLINE long long Chk_NoAddll(long long lhs,
long long rhs,
Boolean *ovrflw)
{
#if defined(__GNUC__) && !defined(DF_C_ONLY_CHKINT)
long long res;
*ovrflw |= __builtin_saddll_overflow(lhs, rhs, &res);
return res;
#else
long long res = (unsigned long long) lhs + (unsigned long long) rhs;
*ovrflw |= ((lhs < 0 && rhs < 0 && res >= 0) ||
(lhs >= 0 && rhs >= 0 && res < 0));
return res;
#endif
}
FORCE_INLINE long Chk_NoAddl(long lhs, long rhs, Boolean *ovrflw)
{
#if defined(__GNUC__) && !defined(DF_C_ONLY_CHKINT)
long res;
*ovrflw |= __builtin_saddl_overflow(lhs, rhs, &res);
return res;
#elif LONG_MAX == LLONG_MAX
return Chk_NoAddll(lhs, rhs, ovrflw);
#else
long long res = (long long) lhs + (long long) rhs;
*ovrflw |= (res < LONG_MIN || res > LONG_MAX);
return (int) res;
#endif
}
FORCE_INLINE int Chk_NoAdd(int lhs, int rhs, Boolean *ovrflw)
{
#if defined(__GNUC__) && !defined(DF_C_ONLY_CHKINT)
int res;
*ovrflw |= __builtin_sadd_overflow(lhs, rhs, &res);
return res;
#elif INT_MAX == LONG_MAX
return Chk_NoAddl(lhs, rhs, ovrflw);
#else
long res = (long) lhs + (long) rhs;
*ovrflw |= (res < INT_MIN || res > INT_MAX);
return (int) res;
#endif
}
FORCE_INLINE unsigned long long Chk_NoAddull(unsigned long long lhs,
unsigned long long rhs,
Boolean *ovrflw)
{
#if defined(__GNUC__) && !defined(DF_C_ONLY_CHKINT)
unsigned long long res;
*ovrflw |= __builtin_uaddll_overflow(lhs, rhs, &res);
return res;
#else
unsigned long long res = lhs + rhs;
*ovrflw |= (res < lhs || res < rhs);
return res;
#endif
}
FORCE_INLINE unsigned long Chk_NoAddul(unsigned long lhs,
unsigned long rhs,
Boolean *ovrflw)
{
#if defined(__GNUC__) && !defined(DF_C_ONLY_CHKINT)
unsigned long res;
*ovrflw |= __builtin_uaddl_overflow(lhs, rhs, &res);
return res;
#else
unsigned long res = lhs + rhs;
*ovrflw |= (res < lhs || res < rhs);
return res;
#endif
}
FORCE_INLINE unsigned Chk_NoAddu(unsigned lhs, unsigned rhs, Boolean *ovrflw)
{
#if defined(__GNUC__) && !defined(DF_C_ONLY_CHKINT)
unsigned res;
*ovrflw |= __builtin_uadd_overflow(lhs, rhs, &res);
return res;
#else
unsigned res = lhs + rhs;
*ovrflw |= (res < lhs || res < rhs);
return res;
#endif
}
FORCE_INLINE long long Chk_NoNegll(long long rhs, Boolean *ovrflw)
{
*ovrflw |= (rhs == LLONG_MIN);
return -rhs;
}
FORCE_INLINE long Chk_NoNegl(long rhs, Boolean *ovrflw)
{
*ovrflw |= (rhs == LONG_MIN);
return -rhs;
}
FORCE_INLINE int Chk_NoNeg(int rhs, Boolean *ovrflw)
{
*ovrflw |= (rhs == INT_MIN);
return -rhs;
}
FORCE_INLINE long long Chk_NoSubll(long long lhs,
long long rhs,
Boolean *ovrflw)
{
#if defined(__GNUC__) && !defined(DF_C_ONLY_CHKINT)
long long res;
*ovrflw |= __builtin_ssubll_overflow(lhs, rhs, &res);
return res;
#else
long long res = (unsigned long long) lhs - (unsigned long long) rhs;
*ovrflw |= ((lhs < 0 && rhs >= 0 && res >= 0) ||
(lhs >= 0 && rhs < 0 && (res < 0 || rhs == LLONG_MIN)));
return res;
#endif
}
FORCE_INLINE long Chk_NoSubl(long lhs, long rhs, Boolean *ovrflw)
{
#if defined(__GNUC__) && !defined(DF_C_ONLY_CHKINT)
long res;
*ovrflw |= __builtin_ssubl_overflow(lhs, rhs, &res);
return res;
#elif LONG_MAX == LLONG_MAX
return Chk_NoSubll(lhs, rhs, ovrflw);
#else
long long res = (long long) lhs - (long long) rhs;
*ovrflw |= (res < LONG_MIN || res > LONG_MAX);
return res;
#endif
}
FORCE_INLINE int Chk_NoSub(int lhs, int rhs, Boolean *ovrflw)
{
#if defined(__GNUC__) && !defined(DF_C_ONLY_CHKINT)
int res;
*ovrflw |= __builtin_ssub_overflow(lhs, rhs, &res);
return res;
#elif INT_MAX == LONG_MAX
return Chk_NoSubl(lhs, rhs, &rhs);
#else
long res = (long) lhs - (long) rhs;
*ovrflw |= (res < INT_MIN || res > INT_MAX);
return res;
#endif
}
FORCE_INLINE unsigned long long Chk_NoSubull(unsigned long long lhs,
unsigned long long rhs,
Boolean *ovrflw)
{
#if defined(__GNUC__) && !defined(DF_C_ONLY_CHKINT)
unsigned long long _res;
*ovrflw |= __builtin_usubll_overflow(lhs, rhs, &res);
return res;
#else
*ovrflw |= (lhs < rhs);
return lhs - rhs;
#endif
}
FORCE_INLINE unsigned long Chk_NoSubul(unsigned long lhs,
unsigned long rhs,
Boolean *ovrflw)
{
#if defined(__GNUC__) && !defined(DF_C_ONLY_CHKINT)
unsigned long res;
*ovrflw |= __builtin_usubl_overflow(lhs, rhs, &res);
return res;
#else
*ovrflw |= (lhs < rhs);
return lhs - rhs;
#endif
}
FORCE_INLINE unsigned Chk_NoSubu(unsigned lhs, unsigned rhs, Boolean *ovrflw)
{
#if defined(__GNUC__) && !defined(DF_C_ONLY_CHKINT)
unsigned res;
*ovrflw |= __builtin_usub_overflow(lhs, rhs, &res);
return res;
#else
*ovrflw |= (lhs < rhs);
return lhs - rhs;
#endif
}
FORCE_INLINE long long Chk_NoMulll(long long lhs,
long long rhs,
Boolean *ovrflw)
{
#if defined(__GNUC__) && !defined(DF_C_ONLY_CHKINT)
long long res;
*ovrflw |= __builtin_smulll_overflow(lhs, rhs, &res);
return res;
#else
long long res = (unsigned long long) lhs * (unsigned long long) rhs;
*ovrflw |= ((lhs & (~1LL)) != 0 &&
(res == rhs ? res : (res / lhs) != rhs));
return res;
#endif
}
FORCE_INLINE long Chk_NoMull(long lhs, long rhs, Boolean *ovrflw)
{
#if defined(__GNUC__) && !defined(DF_C_ONLY_CHKINT)
long res;
*ovrflw |= __builtin_smull_overflow(lhs, rhs, &res);
return res;
#elif LONG_MAX == LLONG_MAX
return Chk_NoMulll(lhs, rhs, ovrflw);
#else
long long res = (long long) lhs * (long long) rhs;
*ovrflw |= (res < LONG_MIN || res > LONG_MAX);
return res;
#endif
}
FORCE_INLINE int Chk_NoMul(int lhs, int rhs, Boolean *ovrflw)
{
#if defined(__GNUC__) && !defined(DF_C_ONLY_CHKINT)
int res;
*ovrflw |= __builtin_smul_overflow(lhs, rhs, &res);
return res;
#elif INT_MAX == LONG_MAX
return Chk_NoMull(lhs, rhs, ovrflw);
#else
long res = (long) lhs * (long) rhs;
*ovrflw |= (res < INT_MIN || res > INT_MAX);
return res;
#endif
}
FORCE_INLINE unsigned long long Chk_NoMulull(unsigned long long lhs,
unsigned long long rhs,
Boolean *ovrflw)
{
#if defined(__GNUC__) && !defined(DF_C_ONLY_CHKINT)
unsigned long long res = 0;
*ovrflw |= __builtin_umulll_overflow(lhs, rhs, &res);
return res;
#else
unsigned long long res = lhs * rhs;
*ovrflw |= ((lhs | rhs) >> 32 && lhs && res / lhs != rhs);
return res;
#endif
}
FORCE_INLINE unsigned long Chk_NoMulul(unsigned long lhs,
unsigned long rhs,
Boolean *ovrflw)
{
#if defined(__GNUC__) && !defined(DF_C_ONLY_CHKINT)
unsigned long res;
*ovrflw |= __builtin_umull_overflow(lhs, rhs, &res);
return res;
#elif ULONG_MAX == ULLONG_MAX
return Chk_NoMulull(lhs, rhs, ovrflw);
#else
unsigned long long res = (unsigned long long) lhs *
(unsigned long long) rhs;
*ovrflw |= ((res >> (sizeof(unsigned long)*CHAR_BIT)) != 0);
return res;
#endif
}
FORCE_INLINE unsigned Chk_NoMulu(unsigned lhs,
unsigned rhs,
Boolean *ovrflw)
{
#if defined(__GNUC__) && !defined(DF_C_ONLY_CHKINT)
unsigned res;
*ovrflw |= __builtin_umul_overflow(lhs, rhs, &res);
return res;
#elif UINT_MAX == ULONG_MAX
return Chk_NoMulul(lhs, rhs, ovrflw);
#else
unsigned long res = (unsigned long) lhs * (unsigned long) rhs;
*ovrflw |= ((res >> (sizeof(unsigned)*CHAR_BIT)) != 0);
return res;
#endif
}
#endif

134
lonetix/include/df/cpr/bzip2.h Executable file
View File

@ -0,0 +1,134 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
/**
* \file cpr/bzip2.h
*
* BurrowsWheeler bzip2 compression streaming support library.
*
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
* \author Lorenzo Cogotti
*/
#ifndef DF_CPR_BZIP2_H_
#define DF_CPR_BZIP2_H_
#include "stm.h"
/// Bzip2 stream handle.
typedef struct Bzip2StmObj *Bzip2StmHn;
/**
* \brief BZip2 Compression options.
*
* \see `Bz2_InitCompress()`
*/
typedef struct {
/**
* \brief Compression level, from 1 to 9 inclusive.
*
* Higher values imply better compression, at the price of speed loss
* and memory consumption.
* Out of range values are silently clamped to allowed range.
*/
int compression;
/// Compressor load factor.
int factor;
/// Compressor buffer size in bytes, 0 to use suggested value.
size_t bufsiz;
/**
* \brief Debugging message verbosity.
*
* Values higher than 0 make the stream log debug messages to standard
* error, debugging level goes from 0 to 4 inclusive.
* Larger values are silently truncated to the maximum allowed.
*/
unsigned verbose;
} Bzip2CprOpts;
/**
* \brief Decompression options.
*
* \see `Bz2_InitDecompress()`
*/
typedef struct {
/// Decompressor buffer size in bytes, 0 to use suggested value.
size_t bufsiz;
/// Conserve memory during decompression, in spite of speed.
Boolean low_mem;
/**
* \brief Debugging message verbosity.
*
* Values higher than 0 make the stream log debug messages to standard
* error, debugging level goes from 0 to 4 inclusive.
* Larger values are implicitly truncated to the maximum allowed.
*/
unsigned verbose;
} Bzip2DecOpts;
/// BZip2 result status, returned by `Bzip2_GetErrStat()`.
typedef int Bzip2Ret; // 0 == OK
/// Implementation of `StmOps` for BZip2 compression/decompression.
extern const StmOps *const Bzip2_StmOps;
/// Non-closing variant of `Bzip2_StmOps`.
extern const StmOps *const Bzip2_NcStmOps;
/// Return last BZip2 operation result.
Bzip2Ret Bzip2_GetErrStat(void);
/// Convert `Bzip2Ret` value to human readable string.
const char *Bzip2_ErrorString(Bzip2Ret ret);
/**
* \brief Open stream for compression.
*
* \param [in,out] streamp Output stream for compressed data
* \param [in] ops Write operations interface for `streamp`, must not be `NULL` and provide `Write()`
* \param [in] opts Compression options, may be `NULL` for defaults
*
* \return The BZip2 compressor handle on success, `NULL` on failure.
*/
Bzip2StmHn Bzip2_OpenCompress(void *streamp, const StmOps *ops, const Bzip2CprOpts *opts);
/**
* \brief Open a stream for decompressing.
*
* \param [in,out] streamp Input stream for BZip2 compressed data
* \param [in] ops Read operations interface for `streamp`, must not be `NULL` and provide `Read()`
* \param [in] opts Decompression options, may be `NULL` for defaults
*
* \return The BZip2 decompressor handle on success, `NULL` on failure.
*/
Bzip2StmHn Bzip2_OpenDecompress(void *streamp, const StmOps *ops, const Bzip2DecOpts *opts);
/**
* \brief Decompress `nbytes` bytes from `hn` to `buf`.
*
* \return Number of actual bytes written to `buf`, 0 on end of stream,
* -1 on error.
*/
Sint64 Bzip2_Read(Bzip2StmHn hn, void *buf, size_t nbytes);
/**
* \brief Compress `nbytes` bytes from `buf` to `hn`.
*
* \return Number of bytes actually written to `hn`, which may be less
* than `nbytes`, -1 on error.
*
* \note Compression should be finalized with `Bzip2_Finish()` once all
* data is written.
*/
Sint64 Bzip2_Write(Bzip2StmHn hn, const void *buf, size_t nbytes);
/**
* \brief Flush Bzip2 encoder.
*
* Should be called before closing a BZip2 encoder.
*
* \param [in,out] hn Stream to be finalized, must not be `NULL`
*
* \return `OK` on success, `NG` on failure.
*/
Judgement Bzip2_Finish(Bzip2StmHn hn);
/// Close a Bzip2 stream.
void Bzip2_Close(Bzip2StmHn hn);
#endif

121
lonetix/include/df/cpr/flate.h Executable file
View File

@ -0,0 +1,121 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
/**
* \file cpr/flate.h
*
* Compressor DEFLATE and inflate stream implementation.
*
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
* \author Lorenzo Cogotti
*
* \see [RFC 1950](https://tools.ietf.org/html/rfc1950)
* \see [RFC 1951](https://tools.ietf.org/html/rfc1951)
* \see [RFC 1952](https://tools.ietf.org/html/rfc1952)
*/
#ifndef DF_CPR_FLATE_H_
#define DF_CPR_FLATE_H_
#include "stm.h"
/// DEFLATE or inflate stream handle.
typedef struct ZlibStmObj *ZlibStmHn;
/// Supported DEFLATE data formats.
typedef enum {
ZFMT_RFC1952, ///< gzip compression, see [RFC 1952](https://tools.ietf.org/html/rfc1952)
ZFMT_RFC1951, ///< Original deflate format, see [RFC 1951](https://tools.ietf.org/html/rfc1951)
ZFMT_RFC1950 ///< Zlib format, see [RFC 1950](https://tools.ietf.org/html/rfc1950)
} Zlibfmt;
/// Inflate (decompression) options.
typedef struct {
unsigned win_bits; ///< Compression window size in bits
Zlibfmt format; ///< Input DEFLATE encoding format
size_t bufsiz; ///< Input buffer size in bytes, 0 for default
} InflateOpts;
/// DEFLATE (compression) options.
typedef struct {
int compression; ///< Compression, range `[0-9]` (0 = none, 9 = best)
unsigned win_bits; ///< Compression window size in bits
Zlibfmt format; ///< Output DEFLATE format
size_t bufsiz; ///< Output buffer size in bytes, leave to 0 for default
} DeflateOpts;
/// Zlib result status.
typedef Sint64 ZlibRet; // 0 == OK
/**
* \brief Implementation of the `StmOps` interface for DEFLATE streams.
*
* Passing these interfaces to any API working with streams allows it to
* operate on DEFLATE streams.
* `Zlib_StmOps` implements the `Close()` method, while the non-closing
* `Zlib_NcStmOps` leaves `Close()` to `NULL`, effectively preventing any
* attempt to close such stream. Use this variant when this behavior is
* desirable (e.g. streams similar to `stdout` or `stdin`).
*
* @{
* \var Zlib_StmOps
* \var Zlib_NcStmOps
* @}
*/
extern const StmOps *const Zlib_StmOps;
extern const StmOps *const Zlib_NcStmOps;
/// Return last Zlib operation's return status.
ZlibRet Zlib_GetErrStat(void);
/// Convert `ZlibRet` result to human readable string.
const char *Zlib_ErrorString(ZlibRet res);
/**
* \brief Start Zlib decompression over a stream.
*
* \param [in,out] streamp Compressed input source stream
* \param [in] ops Read operations over `streamp`, must not be `NULL` and provide `Read()`
* \param [in] opts Decompression options, `NULL` for defaults
*
* \return Opened Zlib handle on success, `NULL` on failure.
*/
ZlibStmHn Zlib_InflateOpen(void *streamp, const StmOps *ops, const InflateOpts *opts);
/**
* \brief Start Zlib compression over a stream.
*
* \param [in,out] streamp Destination for compressed output
* \param [in] ops Write operations over `ßtreamp`, must not be `NULL` and provide `Write()`
* \param [in] opts Compression options, `NULL` for defaults.
*
* \return Opened Zlib handle on success, `NULL` on failure.
*/
ZlibStmHn Zlib_DeflateOpen(void *streamp, const StmOps *ops, const DeflateOpts *opts);
/**
* \brief Decompress `nbytes` bytes from `hn` into `buf`.
*
* \return Number of bytes actually written to `buf`, at most `nbytes`,
* 0 on end of stream, -1 on error.
*/
Sint64 Zlib_Read(ZlibStmHn hn, void *buf, size_t nbytes);
/**
* \brief Compresses `nbytes` bytes from `buf` to `hn`.
*
* \return Count of bytes actually compressed to `hn`, at most `nbytes`,
* -1 on error.
*
* \note Compression should be finalized with `Zlib_Finish()` once all
* data is written.
*/
Sint64 Zlib_Write(ZlibStmHn hn, const void *buf, size_t nbytes);
/**
* \brief Finalize DEFLATE compression.
*
* \return `OK` on success, `NG` otherwise.
*/
Judgement Zlib_Finish(ZlibStmHn hn);
/// Close Zlib stream handle.
void Zlib_Close(ZlibStmHn hn);
#endif

118
lonetix/include/df/cpr/xz.h Executable file
View File

@ -0,0 +1,118 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
/**
* \file cpr/xz.h
*
* Compressors, LZMA/LZMA2 encoding and decoding implementation.
*
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
* \author Lorenzo Cogotti
*/
#ifndef DF_CPR_XZ_H_
#define DF_CPR_XZ_H_
#include "stm.h"
/// LZMA/LZMA2 stream handle.
typedef struct XzStmObj *XzStmHn;
/// LZMA/LZMA2 data integrity checksum algorithms.
typedef enum {
XZCHK_NONE = 0, ///< Do not include any data checksum
XZCHK_CRC32 = 1, ///< Cyclic Redundancy Check, 32-bit
XZCHK_CRC64 = 4, ///< Cyclic Redundancy Check, 64-bit
XZCHK_SHA256 = 10 ///< Secure Hash Algorithm, 256 bit
} Xzchk;
/// LZMA/LZMA2 compressor flags.
typedef struct {
unsigned compress; ///< Compression level, range [0-9]
Boolean extreme; ///< Severely sacrifice speed for compression
Xzchk chk; ///< Checksum algorithm to use
size_t bufsiz; ///< Output buffer size in bytes
} XzEncOpts;
/// LZMA/LZMA2 decompression flags.
typedef struct {
Uint64 memlimit; ///< Decoder memory usage limit
Boolean no_concat; ///< Do not support concatenated xz streams
Boolean no_chk; ///< Disregard data checksum during decoding
size_t bufsiz; ///< Input buffer size in bytes
} XzDecOpts;
/// LZMA operation status result.
typedef int XzRet; // 0 == OK
/**
* \brief Implementation of the `StmOps` interface for LZMA streams.
*
* Allows any API working with streams to function with LZMA streams.
* `Xz_StmOps` implements the `Close()` method, while the non-closing
* `Xz_NcStmOps` leaves `Close()` to `NULL`, preventing any
* attempt to close the stream. Use this variant when such behavior is
* desirable (e.g. streams similar to `stdout` or `stdin`).
*
* @{
* \var Xz_StmOps
* \var Xz_NcStmOps
* @}
*/
extern const StmOps *const Xz_StmOps;
extern const StmOps *const Xz_NcStmOps;
/// Retrieve last operation's result status.
XzRet Xz_GetErrStat(void);
/// Convert `XzRet` to human readable string.
const char *Xz_ErrorString(XzRet res);
/**
* \brief Open stream for compression.
*
* \param [in,out] streamp Output stream for compressed LZMA data
* \param [in] ops Write operations interface for `streamp`, must not be `NULL` and provide `Write()`
* \param [in] opts Compression options, may be `NULL` for defaults
*
* \return The LZMA compressor handle on success, `NULL` on failure.
*/
XzStmHn Xz_OpenCompress(void *streamp, const StmOps *ops, const XzEncOpts *opts);
/**
* \brief Open an LZMA stream for decompressing.
*
* \param [in,out] streamp Input stream for LZMA compressed data
* \param [in] ops Read operations interface for `streamp`, must not be `NULL` and provide `Read()`
* \param [in] opts Decompression options, may be `NULL` for defaults
*
* \return The LZMA decompressor handle on success, `NULL` on failure.
*/
XzStmHn Xz_OpenDecompress(void *streamp, const StmOps *ops, const XzDecOpts *opts);
/**
* \brief Decompress `nbytes` bytes from `hn` into `buf`.
*
* \return Number of bytes actually written to `buf`, at most `nbytes`,
* 0 on end of stream, -1 on error.
*/
Sint64 Xz_Read(XzStmHn hn, void *buf, size_t nbytes);
/**
* \brief Compresses `nbytes` bytes from `buf` to `hn`.
*
* \return Count of bytes actually compressed to `hn`, at most `nbytes`,
* -1 on error.
*
* \note Compression should be finalized with `Xz_Finish()` once all
* data is written.
*/
Sint64 Xz_Write(XzStmHn hn, const void *buf, size_t nbytes);
/**
* \brief Finalize LZMA compression.
*
* \return `OK` on success, `NG` otherwise.
*/
Judgement Xz_Finish(XzStmHn hn);
/// Close LZMA stream.
void Xz_Close(XzStmHn hn);
#endif

475
lonetix/include/df/lexer.h Normal file
View File

@ -0,0 +1,475 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
/**
* \file lexer.h
*
* C-compliant non-allocating UTF-8 text lexer.
*
* \author Lorenzo Cogotti
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
*/
#ifndef DF_LEXER_H_
#define DF_LEXER_H_
#include "utf/utfdef.h"
/// Maximum allowed token length inside text parsed by `Lex`.
#define MAXTOKLEN 256
/// String token type
#define TT_STRING U16_C(1)
/// Literal token type
#define TT_LITERAL U16_C(2)
/// Numeric token type
#define TT_NUMBER U16_C(3)
/// Token type for names or identifiers
#define TT_NAME U16_C(4)
/// Punctuation token type
#define TT_PUNCT U16_C(5)
/**
* Token subtype flags for `TT_NUMBER`
*
* @{
*/
#define TT_INT BIT(0) ///< integer
#define TT_DEC BIT(1) ///< decimal number
#define TT_HEX BIT(2) ///< hexadecimal number
#define TT_OCT BIT(3) ///< octal number
#define TT_BIN BIT(4) ///< binary number
#define TT_LONG BIT(5) ///< long int
#define TT_LLONG BIT(6) ///< long long int
#define TT_UNSIGNED BIT(7) ///< unsigned int
#define TT_FLOAT BIT(8) ///< floating point number
#define TT_SINGLE_PREC BIT(9) ///< float
#define TT_DOUBLE_PREC BIT(10) ///< double
#define TT_EXT_PREC BIT(11) ///< long double
#define TT_INF BIT(12) ///< infinite 1.#INF
#define TT_INDEF BIT(13) ///< indefinite 1.#IND
#define TT_NAN BIT(14) ///< NaN
#define TT_IPADDR BIT(15) ///< ip address (address may still be ill-formed, e.g. `102948.22.999.1`)
#define TT_IPV4 BIT(16) ///< ipv4 address format
#define TT_IPV6 BIT(17) ///< ipv6 address format
#define TT_IPV6LIT BIT(18) ///< ipv6 address is expressed as literal (e.g. `[2001:db8:a::123]`)
#define TT_IPV6ZONE BIT(19) ///< ipv6 address contains a zone index/string (e.g. `fe80::1ff:fe23:4567:890a%3`)
#define TT_IPPORT BIT(20) ///< ip address includes a port
/** @} */
/**
* Token flags
*
* @{
*/
/// Indicates `Tok` originally exceeded `MAXTOKLEN` and was consequently truncated.
#define TT_TRUNC BIT(15)
/** @} */
/// Lexer punctuation token descriptor (text -> token `subtype`).
typedef struct Punctuation Punctuation;
struct Punctuation {
const char *p; ///< NULL for last element in punctuation list.
Uint32 id; ///< Puntuation identifier (returned in `Tok->subtype`)
};
// punctuation ids
#define P_RSHIFT_ASSIGN 1
#define P_LSHIFT_ASSIGN 2
#define P_PARMS 3
#define P_PRECOMPMERGE 4
#define P_LOGIC_AND 5
#define P_LOGIC_OR 6
#define P_LOGIC_GEQ 7
#define P_LOGIC_LEQ 8
#define P_LOGIC_EQ 9
#define P_LOGIC_UNEQ 10
#define P_MUL_ASSIGN 11
#define P_DIV_ASSIGN 12
#define P_MOD_ASSIGN 13
#define P_ADD_ASSIGN 14
#define P_SUB_ASSIGN 15
#define P_INC 16
#define P_DEC 17
#define P_BIN_AND_ASSIGN 18
#define P_BIN_OR_ASSIGN 19
#define P_BIN_XOR_ASSIGN 20
#define P_RSHIFT 21
#define P_LSHIFT 22
#define P_POINTERREF 23
#define P_MUL 24
#define P_DIV 25
#define P_MOD 26
#define P_ADD 27
#define P_SUB 28
#define P_ASSIGN 29
#define P_BIN_AND 30
#define P_BIN_OR 31
#define P_BIN_XOR 32
#define P_BIN_NOT 33
#define P_LOGIC_NOT 34
#define P_LOGIC_GREATER 35
#define P_LOGIC_LESS 36
#define P_REF 37
#define P_COMMA 38
#define P_SEMICOLON 39
#define P_COLON 40
#define P_QUESTIONMARK 41
#define P_PARENOPEN 42
#define P_PARENCLOSE 43
#define P_BRACEOPEN 44
#define P_BRACECLOSE 45
#define P_SQBRACKETOPEN 46
#define P_SQBRACKETCLOSE 47
#define P_BACKSLASH 48
#define P_PRECOMP 49
#define P_DOLLAR 50
/**
* \brief Token returned by `Lex`.
*
* Contains token text and information.
*/
typedef struct Tok Tok;
struct Tok {
Uint16 type;
Uint16 flags;
Uint32 subtype;
unsigned linesCrossed;
unsigned spacesBeforeToken;
unsigned line;
long long intvalue;
double floatvalue;
Tok *nextToken;
char text[MAXTOKLEN]; // NOTE: last element to allow partial allocation
};
/// Disregard lexer errors
#define L_NOERR BIT(0)
/// Disregard lexer warnings
#define L_NOWARN BIT(1)
/// Disregard both errors and warnings
#define L_QUIET (L_NOERR | L_NOWARN)
/// Use console colors when reporting errors and warnings
#define L_COLORED BIT(2)
/// Parse all tokens as strings, instead of breaking them using full-fledged C rules
#define L_STRONLY BIT(3)
/// Allow file paths within tokens
#define L_ALLOWPATHS BIT(4)
/// Do not allow escapes within strings
#define L_NOSTRESC BIT(5)
/// Do not concatenate consecutive strings
#define L_NOSTRCAT BIT(6)
/// Concatenate strings separated by a backslash+newline
#define L_ALLOWBACKSLASHSTRCAT BIT(7)
/// Allow multichar literals
#define L_ALLOWMULTICHARLIT BIT(8)
/// Accepts IP addresses (parsed as `TT_NUMBER`)
#define L_ALLOWIPADDR BIT(9)
/// IP addresses with port numbers, IPv6 literals or zone ids won't be accepted,
/// only meaningful if used with `L_ALLOWIPADDR`.
#define L_PLAINIPADDRONLY BIT(10)
/// Allow special floating point exception tokens (0.#INF, 0.#IND).
#define L_ALLOWFLOATEXC BIT(10)
/// Allow truncating tokens exceeding `MAXTOKLEN`.
#define L_ALLOWTRUNC BIT(11)
/// Do not search base `#include` paths (used by PC library).
#define L_NOBASEINCLUDES BIT(12)
/// Special callback, invokes immediate program termination after reporting a lexer message
#define LEX_QUIT ((void (*)(Lex *, const char *, void *)) -1)
/// Special callback, makes the lexer ignore the the warning or error
/// (same behavior as `L_NOERR` and `L_NOWARN`, but as an explicit callback).
#define LEX_IGN ((void (*)(Lex *, const char *, void *)) 0)
/// Special callback, makes the lexer print an error or warning message to `stderr`,
/// doesn't terminate execution.
#define LEX_WARN ((void (*)(Lex *, const char *, void *)) 1)
/**
* \brief A lexer, breaks text into single tokens, keeping track of the current position.
*
* \note This struct should be considered opaque.
*/
typedef struct Lex Lex;
struct Lex {
char *pos, *lim;
unsigned line;
Uint16 flags;
Boolean8 hasError;
Boolean8 hasBufferedToken;
Rune nr;
const Punctuation *puncts;
void *obj;
void (*Error)(Lex *, const char *, void *);
void (*Warn)(Lex *, const char *, void *);
Lex *nextLexer;
Tok buf;
char name[MAXTOKLEN];
};
/// Register callbacks for lexer warning and error triggers.
FORCE_INLINE void SetLexerErrorFunc(Lex *p,
void (*errf)(Lex *, const char *, void *),
void (*warnf)(Lex *, const char *, void *),
void *obj)
{
p->Error = errf;
p->Warn = warnf;
p->obj = obj;
}
/**
* \brief Set parsing session name and initial line number.
*
* \param [out] p A lexer, must not be `NULL`
* \param [in] name Name for this parsing session
* \param [in] line Initial line number, 0 is implicitly changed to 1
*/
void BeginLexerSession(Lex *p, const char *name, unsigned line);
/**
* \brief Setup lexer to parse text, sized.
*
* \param [out] p A lexer, must not be `NULL`
* \param [in] text Text to be parsed, must have at least `n` chars
* \param [in] n Number of chars in `text`
*/
void SetLexerTextn(Lex *p, const char *text, size_t n);
/**
* \brief Setup lexer to parse text.
*
* \param [out] p A lexer, must not be `NULL`
* \param [in] text `NUL` terminated text to be parsed
*/
FORCE_INLINE void SetLexerText(Lex *p, const char *text)
{
EXTERNC size_t strlen(const char *);
SetLexerTextn(p, text, strlen(text));
}
/**
* \brief Change lexer flags.
*
* \param [out] p A lexer, must not be `NULL`
* \param [in] flags New flags for the lexer
*/
FORCE_INLINE void SetLexerFlags(Lex *p, unsigned flags)
{
p->flags = flags;
}
/// Retrieve current lexer flags.
FORCE_INLINE unsigned GetLexerFlags(Lex *p)
{
return p->flags;
}
/// Trigger an error over a lexer.
CHECK_PRINTF(2, 3) void LexerError(Lex *p, const char *fmt, ...);
/**
* Trigger a warning over a lexer.
*/
CHECK_PRINTF(2, 3) void LexerWarning(Lex *p, const char *fmt, ...);
/// Test whether a lexer reached the end.
FORCE_INLINE Boolean IsLexerEndOfFile(Lex *p)
{
return (p->pos >= p->lim || *p->pos == '\0') && !p->hasBufferedToken;
}
/// Test whether a lexer encountered an error.
FORCE_INLINE Boolean HasLexerError(Lex *p)
{
return p->hasError;
}
/**
* \brief Read and return next token.
*
* \param [in,out] p A lexer, must not be `NULL`
* \param [out] dest Storage for the returned token, must not be `NULL`
*
* \return If a new token has been read, then `tok->text` is returned,
* `NULL` is returned if a parsing error has been encountered,
* or no more tokens are available.
*/
char *Lex_ReadToken(Lex *p, Tok *dest);
/**
* \brief Read and return next token in the same line.
*
* This is a variant of `Lex_ReadToken()` useful to implement
* a C Preprocessor, it avoids parsing spanning more than one line.
* `\` followed by a newline is recognized and treated as a regular
* space.
*/
char *Lex_ReadTokenOnLine(Lex *p, Tok *dest);
/**
* \brief Expects an integral token, reading and returning its value.
*
* \param [in,out] p A lexer, must not be `NULL`
* \param [in] optionalSign Allow an optional `+` or `-` sign before the
* token, if set to `FALSE` only unsigned
* integers are allowed.
*
* \return The token value, 0 on error, use `HasLexerError()` to distinguish
* between actual 0 and error value.
*/
long long Lex_ParseInt(Lex *p, Boolean optionalSign);
/**
* \brief Expects a boolean token, reading and returning its value.
*
* \param [in,out] p A lexer, must not be `NULL`
* \param [in] allowNumeric Convert numeric values to booleans, 0 for
* `FALSE`, any other numeric value for `TRUE`
*
* \return The boolean value, `FALSE` on error or end of file,
* use `HasLexerError()` or `IsLexerEndOfFile()` to distinguish
* between actual `FALSE` and error value.
*/
Boolean Lex_ParseBool(Lex *p, Boolean allowNumeric);
/**
* \brief Expects a floating point token, reading and returning its value.
*
* \param [in,out] p A lexer, must not be `NULL`
* \param [in] optionalSign Allow an optional `+` or `-` sign before the
* token, if set to `FALSE` only non-negative
* values are allowed.
*
* \return The float value, 0 on error or end of file,
* use `HasLexerError()` or `IsLexerEndOfFile()` to distinguish
* between actual 0 and error value.
*/
double Lex_ParseFloat(Lex *p, Boolean optionalSign);
/**
* \brief Read a one dimensional matrix (vector of length `n`) from `p` into `dest`.
*
* Matrix format is:
* ```
* (x y z w ...)
* ```
*
* \return `TRUE` on success, `FALSE` on error.
*/
Boolean Lex_ParseMatrix1(Lex *p, float *dest, size_t n);
/**
* \brief `Lex_ParseMatrix1()` variant for two dimensional matrixes.
*
* Matrix format is:
* ```
* ((x0 y0 z0 w0 ...) (x1 y1 z1 w1 ...) ...)
* ```
*/
Boolean Lex_ParseMatrix2(Lex *p, float *dest, size_t n, size_t m);
/// `Lex_ParseMatrix1()` variant for tridimensional matrixes.
Boolean Lex_ParseMatrix3(Lex *p, float *dest, size_t n, size_t m, size_t u);
/// Discard any buffered token and any in text token up to a new line.
void Lex_SkipLine(Lex *p);
/**
* \brief Skip every token until `tok` is encountered.
*
* \param [in,out] p A lexer, must not be `NULL`
* \param [in] tok Token to look for, must not be `NULL`
*
* \return `tok` on success, `NULL` on error or end of file.
*/
char *Lex_SkipUntil(Lex *p, const char *tok);
/**
* \brief Expect and skip section enclosed within braces.
*
* Braced sections are enclosed by punctuation tokens of id `P_BRACEOPEN` and
* `P_BRACECLOSE`.
*
* \param [in,out] p A lexer, must not be `NULL`
* \param [in] parseFirstBrace Whether the function should expect the next
* token to be the first brace of the section
* (`TRUE`) or it should assume the first brace
* has already been parsed (`FALSE`).
*
* \return `TRUE` if section was skipped successfully, `FALSE` on error
* (either unbalanced braces or unexpected token).
*/
Boolean Lex_SkipBracedSection(Lex *p, Boolean parseFirstBrace);
/**
* \brief Expect a token, matching and returning it, raises error on mismatch.
*
* \param [in,out] p A lexer, must not be `NULL`
* \param [in] tok Token to be expected, must not be `NULL`
*
* \return On success `tok` is returned, on error `NULL`.
*/
char *Lex_MatchToken(Lex *p, const char *tok);
/**
* \brief Expect any token, raises an error if none is found.
*
* \param [in,out] p A lexer, must not be `NULL`
* \param [out] dest Storage for returned token, must not be `NULL`
*
* \return On success `tok->text` is returned, on error `NULL`.
*/
char *Lex_MatchAnyToken(Lex *p, Tok *dest);
/**
* \brief Expect a token of a specific `type` and `subtype`, raise error on mismatch.
*
* \param [in,out] p A lexer, must not be `NULL`
* \param [out] dest Storage for returned token, must not be `NULL`
* \param [in] type Token type to be expected
* \param [in] subtype Subtype mask for the expected token
*
* \return On success `tok->text`, `NULL` otherwise.
*/
char *Lex_MatchTokenType(Lex *p, Tok *dest, int type, unsigned subtype);
/**
* Check whether next token matches `tok`.
*
* If token matches it is read from `p` and returned, as in `Lex_ReadToken()`,
* otherwise `p` is left unaltered (except for parsing errors).
*/
char *Lex_CheckToken(Lex *p, const char *tok);
/// Similar to `Lex_CheckToken()`, but matches by token `type` and `subtype`.
char *Lex_CheckTokenType(Lex *, Tok *dest, int type, unsigned subtype);
/**
* \brief Peek next token from `p` and test whether it matches with `tok`.
*
* In no case next token is consumed from `p`, lexer is left unaltered
* (except for parsing errors).
*/
char *Lex_PeekToken(Lex *p, const char *tok);
/// Similar to `Lex_PeekToken()`, but matches by token `type` and `subtype`.
char *Lex_PeekTokenType(Lex *p, Tok *dest, int type, unsigned subtype);
/**
* \brief Place a token back into the lexer.
*
* Only one token may be placed back into the lexer at a time,
* it will be returned back on the next call to `Lex_ReadToken()`.
*/
void Lex_UngetToken(Lex *p, const Tok *tok);
#endif

85
lonetix/include/df/mem.h Normal file
View File

@ -0,0 +1,85 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
/**
* \file mem.h
*
* Common allocator interface.
*
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
* \author Lorenzo Cogotti
*/
#ifndef DF_MEM_H_
#define DF_MEM_H_
#include "xpt.h"
/**
* \brief Memory allocator interface.
*
* This is a common interface `struct` used by functions or types
* that accomodate for custom memory allocation policies.
*
* \note The behavior described here for the `Alloc` and `Free` functions is
* a sensible contract that should be respected by most allocators.
* Still, special purpose allocators may tweak it to fit very
* specific situations, e.g. an allocator optimized for a specific use may
* ignore all calls to `Free`, or may interpret a `Free` of chunks never
* returned by `Alloc` as hints to grow its available memory pool,
* a sensitive allocator may choose to only return zeroed memory on
* new chunks and zero them out as soon as they're `Free`d.
* Such allocators should be restricted to special circumstances, while
* the behavior described here should provide the general rule.
*/
typedef struct {
/**
* \brief Allocate or reallocate a memory chunk.
*
* The `Alloc` function takes an optional `allocp`, which may represent
* the allocator state. Some allocators don't require any state or
* provide a global one, in which case `allocp` may be `NULL`.
* The returned chunk is required to be at least `size` bytes large.
* The `oldp` argument is `NULL` for new allocations, but may also be
* a pointer to an existing chunk previously returned by the allocator,
* which is the case for shrink or grow requests. The allocator may
* enlarge or shrink the chunk referenced by `oldp` to reduce
* fragmentation.
* On shrink and grow requests the data up to the minimum value between
* `size` and the old chunk size is preserved
*
* \param [in,out] allocp Allocator state pointer, `NULL` if allocator has no state
* \param [in] size Requested memory chunk size, in bytes
* \param [in,out] oldp Old memory chunk pointer, `NULL` if a new allocation is
* requested
*
* \return One of the following:
* * a pointer to a possibly uninitialized memory chunk on successful new
* allocation,
* * a pointer to the resized memory block, which may reside
* in a different location rather than `oldp`, in the event of
* a successful shrink or grow request,
* * `NULL` on allocation failure.
*/
void *(*Alloc)(void *allocp, size_t size, void *oldp);
/**
* \brief Free a memory chunk.
*
* The `Free` function takes an optional `allocp`, which may
* represent the allocator state, with the same semantics as `Alloc`,
* and a pointer previously returned by `Alloc`, and marks the chunk
* referenced by it as free for future allocations.
* Any allocator should silently ignore requests to free the `NULL`
* pointer.
*
* \param [in,out] allocp Allocator state pointer, `NULL` if allocator has no
* state
* \param [in] p Pointer to a memory chunk previously returned by `Alloc`,
* if `NULL` calling this function is a NOP.
*/
void (*Free)(void *allocp, void *p);
} MemOps;
/// Plain `MemOps` using regular `realloc()` and `free()`, use `NULL` for `allocp`.
extern const MemOps *const Mem_StdOps;
#endif

View File

@ -0,0 +1,154 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
/**
* \file mem_file.h
*
* Manage raw memory byte buffers as an I/O stream file-like resource.
*
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
* \author Lorenzo Cogotti
*
* Memory file resources are regular raw-bytes buffer that may be read,
* written and seeked like regular files, implementing the
* `StmOps` interface. Memory buffer may be managed into a variety of ways
* giving the API some flexibility, the policy is determined by a set of flags,
* specified upon initialization (and possibly changed on the fly).
* When writing to a memory file the buffer will tipically be
* `realloc()`ated as needed, the `granularity` field provides a hint
* to reduce excessive reallocations (buffer shall be `realloc()`ated to
* multiples of such value). Buffer reallocation may be opted out altoghether
* by toggling the `MEM_FILE_NOGROWBIT` flag, which turns any attempt
* of writing more bytes than currently available into a short-write.
* Memory buffers may also be initialized in read-only or write-only mode,
* enabling the API to reliably manage `const` qualified buffers or
* write-only memory zones.
*/
#ifndef DF_MEM_FILE_H_
#define DF_MEM_FILE_H_
#include "stm.h"
/// Default `MemFile` reallocation granularity.
#define MEM_FILE_GRAN (16u * 1024)
/// Allow write operations.
#define MEM_FILE_WRBIT BIT(0)
/// Allow read operations.
#define MEM_FILE_RDBIT BIT(1)
/**
* \brief `MemFile` whose `OWN` flag is set own the memory buffer,
* `free()`ing it upon close and `realloc()`ating it as necessary.
*/
#define MEM_FILE_OWNBIT BIT(2)
/**
* \brief `MemFile` with `NOGROW` flag won't reallocate
* their buffer, consequently short-writes are not treated as errors.
*/
#define MEM_FILE_NOGROWBIT BIT(3)
/**
* \brief A file-like memory buffer.
*
* Provides functionality to operate on a memory chunk in a stream-like fashion,
*/
typedef struct {
char *buf; ///< memory buffer `MemFile` operates on
size_t pos; ///< current position inside `buf`
size_t nbytes; ///< `buf` length, in bytes
size_t cap; ///< `buf` actual capacity, in bytes
size_t gran; ///< reallocation granularity in bytes, must be a power of 2
unsigned flags; ///< operating mode flags
} MemFile;
/// Stream operations on `MemFile`, including `Close()`.
extern const StmOps *const Stm_MemFileOps;
/// Non `Close()`-ing stream operations on `MemFile`.
extern const StmOps *const Stm_NcMemFileOps;
/**
* \brief Perform basic initialization of a `MemFile`, with an empty
* initial buffer.
*
* The resulting memory file resource shall be initialized according to
* function arguments and its buffer shall be `NULL`.
*
* \param [out] stm Pointer to a memory file, must not be `NULL`
* \param [in] gran Reallocation granularity in bytes, must be a power of 2
* \param [in] flags Operating mode flags for the newly initialized memory file
*/
void Stm_InitMemFile(MemFile *stm, size_t gran, unsigned flags);
/**
* \brief Initialize `MemFile` from an existing buffer.
*
* \param [out] stm Pointer to a memory file, must not be `NULL`
* \param [in] buf Read-only buffer to be used, must hold at least `nbytes` bytes
* \param [in] nbytes `buf` size in bytes
* \param [in] gran Memory file reallocation granularity, in bytes, must be a power of 2
* \param [in] flags Memory file resource operating mode flags
*/
void Stm_MemFileFromBuf(MemFile *stm, void *buf, size_t nbytes, size_t gran, unsigned flags);
/**
* \brief Initialize a `MemFile` for read over an existing memory buffer.
*
* Resulting memory file resource is implicitly initialized as read-only,
* its buffer won't grow, won't be reallocated, nor it shall be `free()`-d upon
* close.
*
* \param [out] stm Pointer to a memory file, must not be `NULL`
* \param [in] buf Read-only buffer to be used, must hold at least `nbytes` bytes
* \param [in] nbytes `buf` size in bytes
*
* \note No check is made to ensure the provided buffer is NUL-terminated,
* the resulting `MemFile` is initialized with the provided buffer
* as-is. If such characteristic is important, it is the caller's
* responsibility to ensure that.
*/
void Stm_RoMemFileFromBuf(MemFile *, const void *, size_t);
/**
* \brief Take ownership of the buffer managed by a `MemFile`.
*
* \return A pointer to the managed buffer, whose ownership is transferred
* to the caller, thus the caller shall be responsible for its
* deallocation (if necessary).
*
* \note It is not always necessary to call `free()` on the returned buffer,
* since memory files may operate even on static or stack-allocated
* buffers (see `Stm_MemFileFromBuf()` for example).
*/
FORCE_INLINE char *Stm_TakeMemFileBuf(MemFile *stm)
{
char *buf = stm->buf;
stm->flags &= ~MEM_FILE_OWNBIT;
stm->buf = NULL;
stm->nbytes = stm->cap = 0;
return buf;
}
/**
* \brief Read `nbytes` bytes from `stm` into `buf`.
*
* \return Number of bytes actually read from `stm`, -1 on error.
*/
Sint64 Stm_MemFileRead(MemFile *stm, void *buf, size_t nbytes);
/**
* \brief Write `nbytes` bytes from `buf` into `stm`.
*
* \return Number of bytes actually written to `stm`, -1 on error.
*/
Sint64 Stm_MemFileWrite(MemFile *stm, const void *buf, size_t nbytes);
/**
* \brief Set `stm` file pointer, offsetting it of `pos` bytes from `whence`
*
* \return New file cursor position, -1 on error.
*/
Sint64 Stm_MemFileSeek(MemFile *stm, Sint64 pos, SeekMode whence);
/// Close `stm`, `free()`-ing buffer if necessary.
void Stm_MemFileClose(MemFile *stm);
#endif

191
lonetix/include/df/numlib.h Normal file
View File

@ -0,0 +1,191 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
/**
* \file numlib.h
*
* Fast locale independent conversion from numerics (integer or floating point
* types) to ASCII and back.
*
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
* \author Lorenzo Cogotti
*/
#ifndef DF_NUMLIB_H_
#define DF_NUMLIB_H_
#include "xpt.h"
/**
* \brief Integral compile-time costant representing an upper-bound estimate
* of the number of digit-characters necessary to hold the **decimal**
* representation of `typ`.
*
* Can be used as such:
* ```c
* char buf[1 + DIGS(int) + 1]; // sign + digits + '\0'
*
* Itoa(INT_MAX, buf);
* ```
*
* Approximation is derived from the following formula, where `MAX_INT` is
* the maximum integral value representable by `typ`:
* ```
* N = ceil(log10(MAX_INT)) ->
* N = floor(log10(MAX_INT)) + 1 ->
* (as base-256 logarithm)
* N = floor( log256(MAX_INT) / log256(10) ) + 1 ->
*
* N <= floor( sizeof(typ) * 2.40824 ) + 1 ->
*
* N ~= 241 * sizeof(typ) / 100 + 1
* ```
*
* \param typ An **integer** type or expression
*
* \author [John Bollinger](https://stackoverflow.com/a/43789115)
*/
#define DIGS(typ) ((241 * sizeof(typ)) / 100 + 1)
// #define DIGS(typ) (sizeof(typ) * CHAR_BIT) gross upper-bound approx
/**
* \brief Integral compile-time constant representing an upper-bound estimate
* of the number of digit-characters necessary to hold the **hexadecimal**
* representation of `typ`.
*
* \param typ An **integer** type or expression
*/
#define XDIGS(typ) (sizeof(typ) * 2)
/**
* \brief Maximum number of characters returned by `Ftoa()`,
* **excluding terminating `\0` char**.
*
* Range of double (IEEE-754 `binary64`): `[1.7E-308 ... 1.7E308]`
* - 1 char for sign
* - 309 digits for integer part
* - 1 char for mantissa dot
* - 37 chars for mantissa
*/
#define DOUBLE_STRLEN (309 + 39)
STATIC_ASSERT(sizeof(double) <= 8, "DOUBLE_STRLEN might be inaccurate on this platform");
// should actually make sure we also have IEEE-754 floats...
/**
* \brief Unsigned integer to ASCII conversion.
*
* Destination buffer is assumed to be large enough to hold the
* result. A storage of `DIGS(x) + 1` chars is guaranteed
* to be sufficient. Resulting string is always `NUL` terminated.
*
* \param [in] x Value to be converted
* \param [out] dest Destination string, must not be `\0`
*
* \return Pointer to the trailing `\0` char inside `dest`, useful
* for further string concatenation.
*/
char *Utoa(unsigned long long x, char *dest);
/**
* \brief Signed integer to ASCII conversion.
*
* Destination buffer is assumed to be large enough to hold the result.
* A storage of `1 + DIGS(x) + 1` chars, accounting for the sign
* character, is guaranteed to be sufficient.
*
* \param [in] x Value to be converted
* \param [out] dest Destination string, must not be `NULL`
*
* \return Pointer to the trailing `\0` char in `dest`, useful for further
* string concatenation.
*/
char *Itoa(long long x, char *dest);
/**
* \brief Unsigned integer to hexadecimal lowercase ASCII string.
*
* Destination buffer is assumed to be large enough to hold the result.
* A storage of `XDIGS(x) + 1` chars is guaranteed to be
* sufficient. Resulting string is always `\0` terminated.
*
* \param [in] x Value to be converted
* \param [out] dest Destination string, must not be `NULL`
*
* \return Pointer to the trailing `\0` char in `dest`, useful for further
* string concatenation.
*
* \note No `0x` prefix is prepended to resulting string.
*/
char *Xtoa(unsigned long long x, char *dest);
/**
* \brief Floating point number to scientific notation string.
*
* Destination string is assumed to be large enough to store the conversion
* result, a buffer of size `DOUBLE_STRLEN + 1` is guaranteed to be large
* enough for it. Result is always `\0` terminated.
*
* \param [in] x Floating point number to be converted
* \param [out] dest Destination for result string, must not be `NULL`
*
* \return Pointer to the trailing `\0` char inside result, useful for
* further string concatenation.
*/
char *Ftoa(double x, char *dest);
/// Numeric conversion outcomes.
typedef enum {
NCVENOERR = 0, ///< Conversion successful
NCVEBADBASE, ///< The specified numeric base is out of range
NCVENOTHING, ///< No legal numeric data in input string
NCVEOVERFLOW, ///< Numeric input too large for target integer type
NCVEUNDERFLOW ///< Numeric input too small for target integer type
} NumConvRet;
/**
* \brief ASCII to integer conversion.
*
* If `base` is 0 then the actual numeric base is guessed from input string
* prefix according to an extended C convention:
*
* Numeric prefix | Base
* ---------------|---------------------
* __0__ | octal, base 8
* __0x__ | hexadecimal, base 16
* __0b__ | binary, base 2
* __otherwise__ | decimal, base 10
*
* \param [in] s Input string to be converted, must not be `NULL`
* \param [out] endp Storage where end pointer is returned, may be `NULL`
* \param [in] base Integer conversion base, a value between 0 and 36 inclusive
* \param [out] outcome Storage where conversion outcome is returned, may be `NULL`
*
* \return Conversion result, 0 on error (use `outcome` to detect error).
*
* @{
* \fn unsigned long long Atoull(const char *, char **, unsigned, NumConvRet *)
* \fn unsigned long Atoul(const char *, char **, unsigned, NumConvRet *)
* \fn unsigned Atou(const char *, char **, unsigned, NumConvRet *)
* \fn long long Atoll(const char *, char **, unsigned, NumConvRet *)
* \fn long Atol(const char *, char **, unsigned, NumConvRet *)
* \fn int Atoi(const char *, char **, unsigned, NumConvRet *)
* @}
*/
unsigned long long Atoull(const char *s, char **endp, unsigned base, NumConvRet *outcome);
unsigned long Atoul(const char *s, char **endp, unsigned base, NumConvRet *outcome);
unsigned Atou(const char *s, char **endp, unsigned base, NumConvRet *outcome);
long long Atoll(const char *s, char **endp, unsigned base, NumConvRet *outcome);
long Atol(const char *s, char **endp, unsigned base, NumConvRet *outcome);
int Atoi(const char *s, char **endp, unsigned base, NumConvRet *outcome);
/**
* \brief ASCII to floating point conversion.
*
* \param [in] s Input string to be converted, must not be `NULL`
* \param [out] endp Storage where end pointer is returned, must not be `NULL`
* \param [out] outcome Storage where conversion outcome is returned, may be `NULL`
*
* \return Conversion result, 0 on error (use `outcome` to detect error).
*/
double Atof(const char *s, char **endp, NumConvRet *outcome);
#endif

View File

@ -0,0 +1,45 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
/**
* \file srcloc.h
*
* Source location information type.
*
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
* \author Lorenzo Cogotti
*/
#ifndef DF_SRCLOC_H_
#define DF_SRCLOC_H_
#include "xpt.h"
/**
* Source location information, useful to pack toghether
* information from `__FILE__`, `__LINE__`, and `__func__` for error reporting
* purposes.
*/
typedef struct {
const char *filename; ///< Filename the error was raised from
unsigned long long line; ///< Line that triggered the error
const char *func; ///< Function that raised the error
unsigned call_depth; ///< Call depth, number of functions skipped for backtrace
} Srcloc;
/**
* Declare a `Srcloc` variable named `var` containing location
* info about next, current, or previous source line.
*
* \param var Identifier name to assign to the newly created variable
*
* @{
* @def SRCLOC_NEXT_LINE
* @def SRCLOC_THIS_LINE
* @def SRCLOC_PREV_LINE
* @}
*/
#define SRCLOC_NEXT_LINE(var) Srcloc var = { __FILE__, __LINE__ + 1, __func__, 0 }
#define SRCLOC_THIS_LINE(var) Srcloc var = { __FILE__, __LINE__, __func__, 0 }
#define SRCLOC_PREV_LINE(var) Srcloc var = { __FILE__, __LINE__ - 1, __func__, 0 }
#endif

140
lonetix/include/df/stm.h Normal file
View File

@ -0,0 +1,140 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
/**
* \file stm.h
*
* General stream I/O definitions and utilities.
*
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
* \author Lorenzo Cogotti
*/
#ifndef DF_STM_H_
#define DF_STM_H_
#include "sys/fsdef.h" // Fildes and SeekMode definitions
/**
* \brief I/O stream operations interface.
*
* Defines an interface to work with an abstract I/O stream.
*/
typedef struct {
/**
* \brief Read bytes from the stream.
*
* Function pointer may be `NULL` on write-only streams.
*
* \param streamp Stream handle
* \param buf Destination buffer
* \param nbytes_ Bytes count to be read from `streamp` to `buf`
*
* \return Number of bytes actually read from the stream,
* which may be less than the requested number of bytes.
* If no bytes are available (`EOF`) function returns 0.
* On error `-1` is returned.
*
* \see For reference `Sys_Fread()`
*/
Sint64 (*Read)(void *streamp, void *buf, size_t nbytes);
/**
* \brief Write bytes to the stream.
*
* Function pointer may be `NULL` on read-only streams.
*
* \param streamp Stream handle
* \param buf buffer to be written, holds at least `nbytes` bytes
* \param nbytes Bytes count to be written to `hn`
*
* \return Number of bytes actually written to `streamp`, which
* may be less than `nbytes`, -1 on error.
*
* \see For reference `Sys_Fwrite()`
*/
Sint64 (*Write)(void *streamp, const void *buf, size_t nbytes);
/**
* \brief Seek inside the stream.
*
* Function pointer may be `NULL` on non-seekable streams.
*
* \param streamp Stream handle
* \param off Seek offset in bytes
* \param whence Seek mode
*
* \return The final cursor position on success, -1 on error.
*
* \see For reference [Sys_Fseek()](@ref Sys_Fseek)
*/
Sint64 (*Seek)(void *streamp, Sint64 off, SeekMode whence);
/**
* \brief Retrieve current position inside stream.
*
* Function pointer may be `NULL` on non-seekable streams.
*
* \param streamp Stream handle
*
* \return Current cursor position inside `streamp`, -1 on error.
*
* \see For reference `Sys_Ftell()`
*/
Sint64 (*Tell)(void *streamp);
/**
* \brief Finalizes writes to stream.
*
* Function pointer may be `NULL` in read-only streams or when such
* operation is unavailable or meaningless.
*
* \param streamp Stream handle
*
* \return`OK` on success`NG` otherwise.
*/
Judgement (*Finish)(void *streamp);
/**
* \brief Closes the stream and free its resources.
*
* Function pointer may be `NULL` if such operation is not necessary.
*
* \param streamp Stream handle to be closed
*
* \see For reference `Sys_Fclose()`
*/
void (*Close)(void *streamp);
} StmOps;
/**
* \brief Implementation of `StmOps` over `Fildes`.
*
* May be used to enable any function accepting `StmOps` to
* work with `Fildes`.
* Complete ownership is provided by `Stm_FildesOps`, non-closing
* access is provided by `Stm_NcFildesOps`.
* `Finish()` function performs a full file sync to disk
* (as in: `Sys_Fsync()` with a `TRUE` `fullSync` flag).
*
* @{
* \var Stm_FildesOps
* \var Stm_NcFildesOps
* @}
*/
extern const StmOps *const Stm_FildesOps;
extern const StmOps *const Stm_NcFildesOps;
/**
* \brief Obtain a stream pointer from a file descriptor.
*
* \param [in] fd File descriptor
*
* \return Pointer suitable to be used as the `streamp` parameter for
* a `StmOps` interface.
*
* \see `Stm_FildesOps`, `Stm_NcFildesOps`
*
* \note Returned pointer may very well be `NULL`.
*/
FORCE_INLINE void *STM_FILDES(Fildes fd)
{
STATIC_ASSERT(sizeof(fd) <= sizeof(Sintptr), "STM_FILDES() ill formed on this platform");
return ((void *) ((Sintptr) fd));
}
#endif

198
lonetix/include/df/strlib.h Normal file
View File

@ -0,0 +1,198 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
/**
* \file strlib.h
*
* Additional ASCII string and char classification utility library.
*
* \copyright The DoubleFourteen Code Forge (c) All Rights Reserved
* \author Lorenzo Cogotti
*/
#ifndef DF_STRLIB_H_
#define DF_STRLIB_H_
#include "xpt.h"
/// Test whether `c` is a digit `char`.
FORCE_INLINE Boolean Df_isdigit(char c)
{
return c >= '0' && c <= '9';
}
/// Test whether `c` is an uppercase ASCII `char`.
FORCE_INLINE Boolean Df_isupper(char c)
{
return c >= 'A' && c <= 'Z';
}
/// Test whether `c` is a lowercase ASCII `char`.
FORCE_INLINE Boolean Df_islower(char c)
{
return c >= 'a' && c <= 'z';
}
/// Test whether `c` is an ASCII space `char`.
FORCE_INLINE Boolean Df_isspace(char c)
{
return c == ' ' || (c >= '\t' && c <= '\n');
}
/// Test whether `c` is an alphabetic ASCII `char`.
FORCE_INLINE Boolean Df_isalpha(char c)
{
return Df_islower(c) || Df_isupper(c);
}
/// Test whether `c` is an alphanumeric ASCII `char`.
FORCE_INLINE Boolean Df_isalnum(char c)
{
return Df_isdigit(c) || Df_isalpha(c);
}
/**
* \brief Convert `c` to uppercase ASCII `char`.
*
* \return Uppercase `c`, if `c` is lowecase, otherwise
* returns `c` itself.
*/
FORCE_INLINE char Df_toupper(char c)
{
// Toggle off lowercase bit if c is lowercase
return c & ~(Df_islower(c) << 5);
}
/**
* \brief Convert `c` to lowercase ASCII `char`.
*
* \return Lowercase `c`, if `c` is uppercase, otherwise returns `c` itself.
*/
FORCE_INLINE char Df_tolower(char c)
{
// Only toggle lowercase bit if c is uppercase
return c | (Df_isupper(c) << 5);
}
/// Test whether `c` is an hexadecimal digit `char`.
FORCE_INLINE Boolean Df_ishexdigit(char c)
{
char lc = Df_tolower(c);
return Df_isdigit(c) || (lc >= 'a' && lc <= 'f');
}
/**
* \brief Concatenate `dest` with `s`, writing at most `n` `char`s to `dest`
* (including `\0`).
*
* \return `strlen(dest) + strlen(s)` before concatenation, that is:
* the length of the concatenated string without truncation,
* truncation occurred if return value `>= n`.
*
* \note Function always terminates `dest` with `\0`.
*/
size_t Df_strncatz(char *dest, const char *s, size_t n);
/**
* \brief Copy at most `n` `char`s from `s` to `dest` (including `\0`).
*
* \return The length of `s`, truncation occurred if return value `>= n`.
*
* \note Function always terminates `dest` with `\0`.
*/
size_t Df_strncpyz(char *dest, const char *s, size_t n);
/// Compare ASCII strings `a` and `b` ignoring case.
int Df_stricmp(const char *a, const char *b);
/// Compare at most `n` chars from ASCII strings `a` and `b`, ignoring case.
int Df_strnicmp(const char *a, const char *b, size_t n);
/// Convert ASCII string to lowercase, returns `s` itself.
FORCE_INLINE char *Df_strlwr(char *s)
{
char *p = s;
char c;
while ((c = *p) != '\0')
*p++ = Df_tolower(c);
return s;
}
/// Convert ASCII string to uppercase, returns `s` itself.
FORCE_INLINE char *Df_strupr(char *s)
{
char *p = s;
char c;
while ((c = *p) != '\0')
*p++ = Df_toupper(c);
return s;
}
/// Trim trailing whitespaces in-place, returns `s` itself.
INLINE char *Df_strtrimr(char *s)
{
EXTERNC size_t strlen(const char *);
size_t n = strlen(s);
while (n > 0 && Df_isspace(s[n-1])) n--;
s[n] = '\0';
return s;
}
/// Trim leading whitespaces in-place, returns `s` itself.
INLINE char *Df_strtriml(char *s)
{
char *p1 = s, *p2 = s;
while (Df_isspace(*p1)) p1++;
while ((*p2++ = *p1++) != '\0');
return s;
}
/// Trim leading and trailing whitespaces in-place, returns `s` itself.
INLINE char *Df_strtrim(char *s)
{
Df_strtrimr(s);
return Df_strtriml(s);
}
/**
* \brief Pad string to left with `c` up to `n` ASCII chars.
*
* \return Resulting string length.
*/
INLINE size_t Df_strpadl(char *s, char c, size_t n)
{
EXTERNC size_t strlen(const char *);
EXTERNC void *memmove(void *, const void *, size_t);
EXTERNC void *memset(void *, int, size_t);
size_t len = strlen(s);
if (len < n) {
memmove(s + n - len, s, len + 1);
memset(s, c, n - len);
len = n;
}
return len;
}
/**
* \brief Pad string to right with `c` up to `n` ASCII chars.
*
* \return Resulting string length.
*/
INLINE size_t Df_strpadr(char *s, char c, size_t n)
{
EXTERNC size_t strlen(const char *);
size_t i = strlen(s);
while (i < n) s[i++] = c;
s[i] = '\0';
return i;
}
#endif

104
lonetix/include/df/sys/con.h Executable file
View File

@ -0,0 +1,104 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
/**
* \file sys/con.h
*
* System console interface.
*
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
* \author Lorenzo Cogotti
*
* Provides basic functionality to print to system console and
* test for supported capabilities.
*/
#ifndef DF_SYS_CON_H_
#define DF_SYS_CON_H_
#include "stm.h"
#include <stdarg.h>
/**
* \typedef ConHn
* \brief Platform console handle descriptor.
*
* \def STDIN
* \brief Platform Standard Input (`stdin`) handle.
* \def STDOUT
* \brief Platform Standard Output (`stdout`) handle.
* \def STDERR
* \brief Platform Standard Error (`stderr`) handle.
*
* \fn Fildes CON_FILDES(ConHn)
*
* \brief Convert a `ConHn` to a `Fildes`, making the console handle usable with
* regular platform file API.
*/
#ifdef _WIN32
typedef Uint32 /*DWORD*/ ConHn;
#define STDIN ((ConHn) -10) // STD_INPUT_HANDLE
#define STDOUT ((ConHn) -11) // STD_OUTPUT_HANDLE
#define STDERR ((ConHn) -12) // STD_ERROR_HANDLE
Fildes CON_FILDES(ConHn hn);
#else
typedef int ConHn;
#define STDIN ((ConHn) 0)
#define STDOUT ((ConHn) 1)
#define STDERR ((ConHn) 2)
FORCE_INLINE Fildes CON_FILDES(ConHn hn)
{
return (Fildes) hn; // trivial on Unix
}
#endif
/**
* \brief Convert a `ConHn` to a `streamp` pointer for `StmOps`.
*
* \see `Stm_ConOps`
*/
FORCE_INLINE void *STM_CONHN(ConHn hn)
{
STATIC_ASSERT(sizeof(Sintptr) >= sizeof(void *), "ConHn ill formed on this platform");
return (void *) ((Sintptr) hn);
}
/**
* \brief `StmOps` interface operating on console output.
*
* There is no `Close()` function for consoles.
*/
extern const StmOps *const Stm_ConOps;
/**
* Print string to console.
*
* \note No newline is appended.
*/
void Sys_Print(ConHn hn, const char *s);
/// Formatted print to console handle.
CHECK_PRINTF(2, 3) void Sys_Printf(ConHn hn, const char *fmt, ...);
/// `Sys_Printf()` variant using `va_list`.
CHECK_PRINTF(2, 0) void Sys_VPrintf(ConHn hn, const char *fmt, va_list va);
/**
* \brief Read at most `nbytes` characters from `hn` to `buf`, input is *not* `\0` terminated.
*
* \return Count of `char`s written to `buf`.
*/
size_t Sys_Read(ConHn hn, char *buf, size_t nbytes);
/// Non-Blocking variant of `Sys_Read()`.
size_t Sys_NbRead(ConHn hn, char *buf, size_t nbytes); // NON-BLOCKING
/// Test whether `hn` supports VT100 commands.
Boolean Sys_IsVt100Console(ConHn hn);
#endif

59
lonetix/include/df/sys/dbg.h Executable file
View File

@ -0,0 +1,59 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
/**
* \file sys/dbg.h
*
* Debugging utilities to retrieve stack trace and symbol names.
*
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
* \author Lorenzo Cogotti
*/
#ifndef DF_SYS_DBG_H_
#define DF_SYS_DBG_H_
#include "sys/con.h"
/// Helper union to cast `void *` to function pointer - be sad if you use it.
typedef union {
void *sym; ///< Function as a `void *`
void (*func)(void); ///< Function pointer.
} Funsym;
STATIC_ASSERT(sizeof(void *) == sizeof(void (*)(void)), "Ill-formed Funsym for target platform");
/// Test whether the current process is being executed under a debugger.
Boolean Sys_IsDebuggerPresent(void);
/**
* \brief Get a symbol's name, if available.
*
* \param [in] sym Symbol whose name is needed.
*
* \return A pointer to a statically allocated thread-local buffer containing
* a `\0` terminated symbol name on success, `NULL` if no name could be
* retrieved.
*/
char *Sys_GetSymbolName(void *sym);
/**
* \brief Dump at most `n` backtrace entries to `dest`.
*
* Returned backtrace does not include `Sys_GetBacktrace()`
* itself, and has the caller as the topmost symbol.
*
* \return Number of entries actually returned to `dest` on success,
* 0 when no backtrace information is available.
*/
size_t Sys_GetBacktrace(void **dest, size_t n);
/**
* \brief Get the caller's caller.
*
* Useful for quick debugging:
* ```c
* printf("Called by: %s\n", Sys_GetSymbolName(Sys_GetCaller()));
* ```
*/
void *Sys_GetCaller(void);
/// Dump backtrace to console (typically `STDERR`).
void Sys_DumpBacktrace(ConHn hn, void **trace, size_t n);
#endif

132
lonetix/include/df/sys/endian.h Executable file
View File

@ -0,0 +1,132 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
/**
* \file sys/endian.h
*
* Architecture specific byteswap utilities.
*
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
* \author Lorenzo Cogotti
*/
#ifndef DF_SYS_ENDIAN_H_
#define DF_SYS_ENDIAN_H_
#include "xpt.h"
#ifdef _MSC_VER
#include <stdlib.h> // _byteswap_*()
#endif
/// Helper `union` to access bit representation of `float`.
typedef union {
float f32;
Uint32 bits;
} Floatbits;
/// Helper `union` to access bit representation of `double`.
typedef union {
double f64;
Uint64 bits;
} Doublebits;
STATIC_ASSERT(sizeof(Floatbits) == sizeof(Uint32), "float vs Uint32 size mismatch");
STATIC_ASSERT(sizeof(Doublebits) == sizeof(Uint64), "double vs Uint64 size mismatch");
/// Swap bytes inside 16-bits word.
FORCE_INLINE Uint16 bswap16(Uint16 x)
{
#ifdef _MSC_VER
return _byteswap_ushort(x);
#elif defined(__GNUC__)
return __builtin_bswap16(x);
#else
return BSWAP16(x);
#endif
}
/// Swap bytes inside 32-bits dword.
FORCE_INLINE Uint32 bswap32(Uint32 x)
{
#ifdef _MSC_VER
return _byteswap_ulong(x);
#elif defined(__GNUC__)
return __builtin_bswap32(x);
#else
return BSWAP32(x);
#endif
}
/// Swap bytes inside 64-bits quadword.
FORCE_INLINE Uint64 bswap64(Uint64 x)
{
#ifdef _MSC_VER
return _byteswap_uint64(x);
#elif defined(__GNUC__)
return __builtin_bswap64(x);
#else
return BSWAP64(x);
#endif
}
/// `bswap16()` if target isn't little-endian.
FORCE_INLINE Uint16 leswap16(Uint16 x)
{
#if EDN_NATIVE == EDN_LE
return x;
#else
return bswap16(x);
#endif
}
/// `bswap16()` if target isn't big-endian.
FORCE_INLINE Uint16 beswap16(Uint16 x)
{
#if EDN_NATIVE == EDN_BE
return x;
#else
return bswap16(x);
#endif
}
/// `bswap32()` if target isn't little-endian.
FORCE_INLINE Uint32 leswap32(Uint32 x)
{
#if EDN_NATIVE == EDN_LE
return x;
#else
return bswap32(x);
#endif
}
/// `bswap32()` if target isn't big-endian.
FORCE_INLINE Uint32 beswap32(Uint32 x)
{
#if EDN_NATIVE == EDN_BE
return x;
#else
return bswap32(x);
#endif
}
/// `bswap64()` if target isn't little-endian.
FORCE_INLINE Uint64 leswap64(Uint64 x)
{
#if EDN_NATIVE == EDN_LE
return x;
#else
return bswap64(x);
#endif
}
/// `bswap64()` if target isn't big-endian.
FORCE_INLINE Uint64 beswap64(Uint64 x)
{
#if EDN_NATIVE == EDN_BE
return x;
#else
return bswap64(x);
#endif
}
#endif

253
lonetix/include/df/sys/fs.h Executable file
View File

@ -0,0 +1,253 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
/**
* \file sys/fs.h
*
* Portable low-level filesystem layer
*
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
* \author Lorenzo Cogotti
*
* Achieves portable low-level, close to operating system, filesystem
* functionality. Functionality includes:
* - file I/O
* - file usage hints
* - file creation and removal
* - directory listing, creation and removal
* - path utilities.
*
* No library file buffering is attempted, nor any kind of text-based I/O.
* Paths are UTF-8.
*/
#ifndef DF_SYS_FS_H_
#define DF_SYS_FS_H_
#include "sys/fsdef.h" // platform-specific defs, including `Fildes`
/// File access mode for `Sys_Fopen()`.
typedef enum {
FM_READ, ///< Read-only access
FM_WRITE, ///< Write-only access
FM_APPEND, ///< File-append access
FM_EXCL, ///< Exclusive creation access
FM_TEMP ///< Temporary scratch file creation
} FopenMode;
/// Access pattern is sequential.
#define FH_SEQ BIT(0)
/// Access pattern is random.
#define FH_RAND BIT(1)
/// Avoid filesystem cache buffers with this file.
#define FH_UNBUF BIT(2)
/// File data is accessed once and never reused.
#define FH_NOREUSE BIT(3)
/**
* \brief Open a descriptor handle to a regular or special file.
*
* Every successfully opened file descriptor must be closed using
* `Sys_Fclose()` when no longer necessary.
*
* \param [in] filename Path to file, must not be `NULL`
* \param [in] mode File access mode
* \param [in] hints File access hints (`FH_*` bit mask)
*
* \return Opened file descriptor on success, `FILDES_BAD` on failure.
*/
Fildes Sys_Fopen(const char *filename, FopenMode mode, unsigned hints);
/**
* \brief Move file's cursor to the specified offset.
*
* \param [in] fd Opened file descriptor
* \param [in] offset Offset to move the cursor to, according to `whence`, in bytes
* \param [in] whence Seek mode
*
* \return New cursor position on success, -1 on failure or non-seekable file.
*/
Sint64 Sys_Fseek(Fildes fd, Sint64 offset, SeekMode whence);
/// Retrieve current file cursor position, returns -1 on failure or non-seekable file.
Sint64 Sys_Ftell(Fildes fd);
/// Get current file size, in bytes, -1 on error.
Sint64 Sys_FileSize(Fildes fd);
/**
* \brief Write raw bytes to file.
*
* \param [in] fd Opened, writable, file descriptor
* \param [in] buf Containing at least `nbytes` bytes of data
* \param [in] nbytes Bytes count in `buf` to be written to `fd`
*
* \return Number of bytes actually written on `fd`, possibly less
* than `nbytes` on short-write. -1 on failure.
*/
Sint64 Sys_Fwrite(Fildes fd, const void *buf, size_t nbytes);
/**
* \brief Read raw bytes from file.
*
* \param [in] fd Opened, readable, file descriptor
* \param [out] buf Destination buffer for the read operation
* \param [in] nbytes Bytes count to read from `fd` inside `buf`
*
* \return Number of bytes actually read from `fd`, possibly less than `nbytes`,
* 0 is returned on `EOF` condition. -1 on failure.
*/
Sint64 Sys_Fread(Fildes fd, void *buf, size_t nbytes);
/**
* \brief Truncate or grow file size to its current cursor position.
*
* \return `OK` on success, and file is altered as follows,
* - if file size has been truncated excess data is lost;
* - if file has grown in size (file cursor was beyond the original size),
* new content is unspecified.
* On failure returns `NG` and file is unaltered.
*/
Judgement Sys_SetEof(Fildes fd);
/**
* \brief Synchronize file data to disk, flushing buffers.
*
* Some systems require `fd` to be writable, whether `Sys_Fsync()` supports
* a read-only descriptor is system specific.
* Some systems allow optimized syncs that only guarantee read operations
* consistency afterwards, this optimization is used on such systems when
* `fullSync` is `FALSE`. otherwise `fullSync` is ignored (as if always `TRUE`).
* A call to `Sys_Fsync()` with `fullSync` to `TRUE` causes the system
* to sync disk data with `fd` in its entirety.
*
* \param [in] fd Opened file descriptor
* \param [in] fullSync Whether a full sync should occur
*
* \return `OK` on success, `NG` on failure or on systems where
* there is no mean to force file data sync with disk.
*/
Judgement Sys_Fsync(Fildes fd, Boolean fullSync);
/// Close opened file descriptor.
void Sys_Fclose(Fildes fd);
/**
* \brief List directory contents.
*
* `pat` | Meaning
* ---------|------------------------------------------
* __NULL__ | Equivalent to `""`
* "" | No filtering
* / | Only return subdirectories
* .* | Only return files whose extension matches
*
* \param [in] path Path to directory, must not be `NULL`
* \param [out] nfiles Location to store returned file count, may be `NULL` if unimportant
* \param [in] pat Optional pattern to filter directory files, may be `NULL`
*
* \return `malloc()`ed string list containing matching files,
* must be `free()`d by the caller when no longer necessary.
* A single `free()` on the returned list is sufficient to
* release it entirely.
*/
char **Sys_ListFiles(const char *path, unsigned *nfiles, const char *pat);
/**
* \brief Create directory.
*
* \param [in] path Directory creation path, must not be `NULL`
*
* \return `OK` on success, and directory is created.
* Creating already existing directories is a success.
* `NG` on failure.
*/
Judgement Sys_Mkdir(const char *path);
/**
* \brief Rename a file.
*
* Different systems may impose different restrictions on
* rename operations, in particular when the operation crosses
* different devices in the filesystem.
* The only portable and safe way to rename a file (or, in this
* specific scenario, *move* it) is copying it over `newPath`,
* and remove `path` on success, but the basic `rename` operation is
* usually safe, unexpensive and atomic under the same device.
* `Sys_Rename()` works on directories as well.
*
* \param [in] path Path to the file to be renamed, must not be `NULL`
* \param [in] newPath New name for the file, destination directory must exist
*
* \return `OK` on success, `NG` otherwise.
*/
Judgement Sys_Rename(const char *path, const char *newPath);
/**
* \brief Remove a file or empty directory.
*
* \param [in] path Path to file or directory to be removed, must not be `NULL`
*
* \return `OK` on success, `NG` otherwise.
*/
Judgement Sys_Remove(const char *path);
// Path utilities
/// Retrieve the absolute file extension (leftmost not leading dot in basename).
char *Sys_GetAbsoluteFileExtension(const char *path);
/// Retrieve the file extension (rightmost not leading dot in basename).
char *Sys_GetFileExtension(const char *path);
/**
* \brief Set `path` file extension to `ext`.
*
* \param [in,out] path UTF-8 path, must not be `NULL`
* \param [in] ext File extension, including dot, must not be `NULL`
*
* \return Pointer to the extension inside `path`.
*
* \note Assumes `path` is is large enough to hold the result.
*/
char *Sys_SetFileExtension(char *path, const char *ext);
/**
* \brief Removes extension from `path` if it matches `ext`.
*
* \param [in,out] path UTF-8 path, must not be `NULL`
* \param [in] ext File extension, including dot, leave to `""` or `NULL`
* to remove any extension
*
* \return Length of the resulting path, in chars.
*/
size_t Sys_StripFileExtension(char *path, const char *ext);
/**
* \brief If file in `path` has no extension yet, set it to `ext`.
*
* \param [in,out] path UTF-8 path, must not be `NULL`
* \param [in] ext Default extension to be set, including dot, must not be `NULL`
*
* \return Pointer to the extension inside `path`
*
* \note Assums `path` is large enough to hold the result.
*/
char *Sys_DefaultFileExtension(char *path, const char *ext);
/**
* Strip initial portion of `path` if it matches `basePath`.
*
* \param [in,out] path UTF-8 path, must not be `NULL`
* \param [in] basePath Initial path to be removed, use `""` or `NULL` to strip the entire path and leave only file (Unix `basename()`)
*
* \return Length of the resulting path, in chars.
*/
size_t Sys_StripPath(char *path, const char *basePath);
/// Return `path` depth (number of path components).
size_t Sys_PathDepth(const char *path);
/**
* \brief Strip leading slashes and change any path separator to `/`.
*
* \return Resulting path length, in chars.
*/
size_t Sys_ConvertPath(char *path);
/**
* \brief Change any path separator to a single `PATH_SEP`.
*
* \return Resulting path length, in chars.
*/
size_t Sys_ReplaceSeps(char *path);
/// Case-sensitive UTF-8 path comparison, regardless of separators.
int Sys_PathCompare(const char *a, const char *b);
// XXX int Sys_PathCompareNoCase(const char *a, const char *b);
// XXX int Sys_PathCompareNoCaseAscii(const char *a, const char *b);
#endif

58
lonetix/include/df/sys/fsdef.h Executable file
View File

@ -0,0 +1,58 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
/**
* \file sys/fsdef.h
*
* Platform-specific filesystem types and definitions.
*
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
* \author Lorenzo Cogotti
*/
#ifndef DF_SYS_FSDEF_H_
#define DF_SYS_FSDEF_H_
#include "xpt.h"
/**
* \typedef Fildes
* \brief Platform specific file handle (`HANDLE` on Windows, `int` elsewhere).
*
* \def FILDES_BAD
* \brief Bad file descriptor.
*
* \def PATH_SEP
* \brief Path separator character (`\` on Windows, `/` elsewhere).
*
* \def EOLN
* \brief Text file newline sequence (`\r\n` on Windows, `\n` elsewhere).
*/
#ifdef _WIN32
typedef void *Fildes;
#define FILDES_BAD 0
#define PATH_SEP '\\'
#define EOLN "\r\n"
#else
typedef int Fildes;
#define FILDES_BAD -1
#define PATH_SEP '/'
#define EOLN "\n"
#endif
/// I/O stream seek modes.
typedef enum {
SK_SET, ///< Seek from beginning of stream
SK_CUR, ///< Seek from current position
SK_END ///< Seek from stream end
} SeekMode;
#endif

View File

@ -0,0 +1,136 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
/**
* \file sys/interlocked.h
*
* Lock-free atomic operations on integers and pointers.
*
* \copyright The DoubleFourteen Code Forge (c) All Rights Reserved
* \author Lorenzo Cogotti
*/
#ifndef DF_SYS_INTERLOCKED_H_
#define DF_SYS_INTERLOCKED_H_
#include "xpt.h"
/**
* \fn void Smp_AtomicStore(long *p, long v)
* \fn void Smp_AtomicStoreRx(long *p, long v)
* \fn void Smp_AtomicStoreRel(long *p, long v)
* \fn void Smp_AtomicStorePtr(void **p, void *v)
* \fn void Smp_AtomicStorePtrRx(void **p, void *v)
* \fn void Smp_AtomicStorePtrRel(void **p, void *v)
* \fn void Smp_AtomicStore8(Sint8 *p, Sint8 v)
* \fn void Smp_AtomicStore8Rx(Sint8 *p, Sint8 v)
* \fn void Smp_AtomicStore8Rel(Sint8 *p, Sint8 v)
* \fn void Smp_AtomicStore16(Sint16 *p, Sint16 v)
* \fn void Smp_AtomicStore16Rx(Sint16 *p, Sint16 v)
* \fn void Smp_AtomicStore16Rel(Sint16 *p, Sint16 v)
* \fn void Smp_AtomicStore32(Sint32 *p, Sint32 v)
* \fn void Smp_AtomicStore32Rx(Sint32 *p, Sint32 v)
* \fn void Smp_AtomicStore32Rel(Sint32 *p, Sint32 v)
* \fn void Smp_AtomicStore64(Sint64 *p, Sint64 v)
* \fn void Smp_AtomicStore64Rx(Sint64 *p, Sint64 v)
* \fn void Smp_AtomicStore64Rel(Sint64 *p, Sint64 v)
*
* Atomic store operation to a variable or pointer.
*
* \note Only a subset of the fixed-size variants may be available on
* specific architectures.
*/
#ifdef _MSC_VER
#include "interlocked_intrin_msvc.h"
#include "interlocked_ops_msvc.h"
#else
#if __GCC_ATOMIC_LONG_LOCK_FREE != 2
#error "interlocked.h requires lock-free atomics on long!"
#endif
#if __GCC_ATOMIC_POINTER_LOCK_FREE != 2
#error "interlocked.h requires lock-free atomics on void *!"
#endif
/******************************************************************************
* ATOMICS ON FIXED SIZE INTEGERS *
******************************************************************************/
#if __GCC_ATOMIC_CHAR_LOCK_FREE == 2
#define INTERLOCKED_INT8
#define INTERLOCKED_TYPE Sint8
#define INTERLOCKED_SUFFIX 8
#include "interlocked_ops_gcc.h"
#undef INTERLOCKED_TYPE
#undef INTERLOCKED_SUFFIX
#endif /* INTERLOCKED_INT8 */
#if (__SIZEOF_SHORT__ == 2 && __GCC_ATOMIC_SHORT_LOCK_FREE == 2) || \
(__SIZEOF_INT__ == 2 && __GCC_ATOMIC_INT_LOCK_FREE == 2)
#define INTERLOCKED_INT16
#define INTERLOCKED_TYPE Sint16
#define INTERLOCKED_SUFFIX 16
#include "interlocked_ops_gcc.h"
#undef INTERLOCKED_TYPE
#undef INTERLOCKED_SUFFIX
#endif /* INTERLOCKED_INT16 */
#if (__SIZEOF_INT__ == 4 && __GCC_ATOMIC_INT_LOCK_FREE == 2) || \
(__SIZEOF_LONG__ == 4)
#define INTERLOCKED_INT32
#define INTERLOCKED_TYPE Sint32
#define INTERLOCKED_SUFFIX 32
#include "interlocked_ops_gcc.h"
#undef INTERLOCKED_TYPE
#undef INTERLOCKED_SUFFIX
#endif /* INTERLOCKED_INT32 */
#if (__SIZEOF_LONG_LONG__ == 8 && __GCC_ATOMIC_LONG_LONG_LOCK_FREE == 2) || \
(__SIZEOF_LONG__ == 8)
#define INTERLOCKED_INT64
#define INTERLOCKED_TYPE Sint64
#define INTERLOCKED_SUFFIX 64
#include "interlocked_ops_gcc.h"
#undef INTERLOCKED_TYPE
#undef INTERLOCKED_SUFFIX
#endif /* INTERLOCKED_INT64 */
/******************************************************************************
* ATOMICS ON LONG INTEGERS *
******************************************************************************/
#define INTERLOCKED_TYPE long
#define INTERLOCKED_SUFFIX
#include "interlocked_ops_gcc.h"
#undef INTERLOCKED_TYPE
#undef INTERLOCKED_SUFFIX
/******************************************************************************
* ATOMICS ON POINTERS *
******************************************************************************/
#define INTERLOCKED_TYPE void *
#define INTERLOCKED_SUFFIX Ptr
#define INTERLOCKED_NO_ARIT
#include "interlocked_ops_gcc.h"
#undef INTERLOCKED_TYPE
#undef INTERLOCKED_SUFFIX
#undef INTERLOCKED_NO_ARIT
#endif
#endif

View File

@ -0,0 +1,63 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
/**
* \file sys/interlocked_intrin_msvc.h
*
* MSVC-specific intrinsics for interlocked operations.
*
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
* \author Lorenzo Cogotti
*/
#ifndef DF_SYS_INTERLOCKED_H_
#error "use interlocked.h, do not include interlocked_intrin_msvc.h directly"
#endif
#pragma intrinsic(_InterlockedCompareExchange)
#pragma intrinsic(_InterlockedExchangeAdd)
#pragma intrinsic(_InterlockedExchange)
#pragma intrinsic(_InterlockedCompareExchangePointer)
#pragma intrinsic(_InterlockedExchangePointer)
#pragma intrinsic(_InterlockedCompareExchange8)
#pragma intrinsic(_InterlockedExchangeAdd8)
#pragma intrinsic(_InterlockedExchange8)
#pragma intrinsic(_InterlockedCompareExchange16)
#pragma intrinsic(_InterlockedExchangeAdd16)
#pragma intrinsic(_InterlockedExchange16)
#pragma intrinsic(_InterlockedAnd16)
#pragma intrinsic(_InterlockedOr16)
#pragma intrinsic(_InterlockedXor16)
#if (defined(_M_IX86) && _M_IX86 >= 500) || defined(_M_AMD64) || defined(_M_IA64) || defined(_M_ARM)
#pragma intrinsic(_InterlockedCompareExchange64)
#pragma intrinsic(_InterlockedExchangeAdd64)
#pragma intrinsic(_InterlockedExchange64)
#endif
#ifdef _M_ARM
#pragma intrinsic(_InterlockedCompareExchange_nf)
#pragma intrinsic(_InterlockedCompareExchange_acq)
#pragma intrinsic(_InterlockedCompareExchange_rel)
#pragma intrinsic(_InterlockedCompareExchangePointer_nf)
#pragma intrinsic(_InterlockedCompareExchangePointer_acq)
#pragma intrinsic(_InterlockedCompareExchangePointer_rel)
#pragma intrinsic(_InterlockedCompareExchange8_nf)
#pragma intrinsic(_InterlockedCompareExchange8_acq)
#pragma intrinsic(_InterlockedCompareExchange8_rel)
#pragma intrinsic(_InterlockedCompareExchange16_nf)
#pragma intrinsic(_InterlockedCompareExchange16_acq)
#pragma intrinsic(_InterlockedCompareExchange16_rel)
#pragma intrinsic(_InterlockedCompareExchange64_nf)
#pragma intrinsic(_InterlockedCompareExchange64_acq)
#pragma intrinsic(_InterlockedCompareExchange64_rel)
#endif /* _M_ARM */
#include <intrin.h>
#error "Sorry, not implemented yet"

View File

@ -0,0 +1,191 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
/**
* \file sys/interlocked_ops_gcc.h
*
* Generates interlocked functions from GCC intrinsics, based on some macros.
*
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
* \author Lorenzo Cogotti
*
* This file should be `#include`d from `interlocked.h`, on GNUC compilers.
* It generated interlocked primitives based on `INTERLOCKED_TYPE`,
* `INTERLOCKED_SUFFIX` macros.
* `#define`ing `INTERLOCKED_NO_ARIT` disables arithmetic functions
* generation.
* File may be `#include`d multiple times.
*/
#ifndef DF_SYS_INTERLOCKED_H_
#error "Use interlocked.h, do not include interlocked_gcc_ops.h directly"
#endif
#ifndef INTERLOCKED_TYPE
#error "Please define INTERLOCKED_TYPE for atomic operation target type"
#endif
#ifndef INTERLOCKED_SUFFIX
#error "Please define INTERLOCKED_SUFFIX for atomic operation suffix"
#endif
#define _PASTE(a, b) a ## b
#define _XPASTE(a, b) _PASTE(a, b)
#define _FNAME(wk, name, memory_order) \
_XPASTE(_XPASTE(Smp_ ## wk ## Atomic ## name, INTERLOCKED_SUFFIX), memory_order)
#define _FOP(op, memory_order, ...) \
__atomic_ ## op (__VA_ARGS__, __ATOMIC_ ## memory_order)
#define _FCAS(wkflag, succ_memory_order, fail_memory_order, ...) \
__atomic_compare_exchange_n(__VA_ARGS__, wkflag, __ATOMIC_ ## succ_memory_order, __ATOMIC_ ## fail_memory_order)
FORCE_INLINE INTERLOCKED_TYPE _FNAME(,Load,) (INTERLOCKED_TYPE *_p)
{
return _FOP(load_n, SEQ_CST, _p);
}
FORCE_INLINE INTERLOCKED_TYPE _FNAME(,Load,Rx) (INTERLOCKED_TYPE *_p)
{
return _FOP(load_n, RELAXED, _p);
}
FORCE_INLINE INTERLOCKED_TYPE _FNAME(,Load,Acq) (INTERLOCKED_TYPE *_p)
{
return _FOP(load_n, ACQUIRE, _p);
}
FORCE_INLINE void _FNAME(,Store,) (INTERLOCKED_TYPE *_p, INTERLOCKED_TYPE _v)
{
_FOP(store_n, SEQ_CST, _p, _v);
}
FORCE_INLINE void _FNAME(,Store,Rx) (INTERLOCKED_TYPE *_p, INTERLOCKED_TYPE _v)
{
_FOP(store_n, RELAXED, _p, _v);
}
FORCE_INLINE void _FNAME(,Store,Rel) (INTERLOCKED_TYPE *_p, INTERLOCKED_TYPE _v)
{
_FOP(store_n, RELEASE, _p, _v);
}
#ifndef INTERLOCKED_NO_ARIT
FORCE_INLINE INTERLOCKED_TYPE _FNAME(,Add,) (INTERLOCKED_TYPE *_p, INTERLOCKED_TYPE _v)
{
return _FOP(add_fetch, ACQ_REL, _p, _v);
}
FORCE_INLINE INTERLOCKED_TYPE _FNAME(,Add,Rx) (INTERLOCKED_TYPE *_p, INTERLOCKED_TYPE _v)
{
return _FOP(add_fetch, RELAXED, _p, _v);
}
FORCE_INLINE INTERLOCKED_TYPE _FNAME(,Add,Acq) (INTERLOCKED_TYPE *_p, INTERLOCKED_TYPE _v)
{
return _FOP(add_fetch, ACQUIRE, _p, _v);
}
FORCE_INLINE INTERLOCKED_TYPE _FNAME(,Add,Rel) (INTERLOCKED_TYPE *_p, INTERLOCKED_TYPE _v)
{
return _FOP(add_fetch, RELEASE, _p, _v);
}
FORCE_INLINE INTERLOCKED_TYPE _FNAME(,Sub,) (INTERLOCKED_TYPE *_p, INTERLOCKED_TYPE _v)
{
return _FOP(sub_fetch, ACQ_REL, _p, _v);
}
FORCE_INLINE INTERLOCKED_TYPE _FNAME(,Sub,Rx) (INTERLOCKED_TYPE *_p, INTERLOCKED_TYPE _v)
{
return _FOP(sub_fetch, RELAXED, _p, _v);
}
FORCE_INLINE INTERLOCKED_TYPE _FNAME(,Sub,Acq) (INTERLOCKED_TYPE *_p, INTERLOCKED_TYPE _v)
{
return _FOP(sub_fetch, ACQUIRE, _p, _v);
}
FORCE_INLINE INTERLOCKED_TYPE _FNAME(,Sub,Rel) (INTERLOCKED_TYPE *_p, INTERLOCKED_TYPE _v)
{
return _FOP(sub_fetch, RELEASE, _p, _v);
}
FORCE_INLINE INTERLOCKED_TYPE _FNAME(,Incr,) (INTERLOCKED_TYPE *_p)
{
return _FOP(fetch_add, ACQ_REL, _p, 1);
}
FORCE_INLINE INTERLOCKED_TYPE _FNAME(,Incr,Rx) (INTERLOCKED_TYPE *_p)
{
return _FOP(fetch_add, RELAXED, _p, 1);
}
FORCE_INLINE INTERLOCKED_TYPE _FNAME(,Incr,Acq) (INTERLOCKED_TYPE *_p)
{
return _FOP(fetch_add, ACQUIRE, _p, 1);
}
FORCE_INLINE INTERLOCKED_TYPE _FNAME(,Incr,Rel) (INTERLOCKED_TYPE *_p)
{
return _FOP(fetch_add, RELEASE, _p, 1);
}
FORCE_INLINE INTERLOCKED_TYPE _FNAME(,Decr,) (INTERLOCKED_TYPE *_p)
{
return _FOP(fetch_sub, ACQ_REL, _p, 1);
}
FORCE_INLINE INTERLOCKED_TYPE _FNAME(,Decr,Rx) (INTERLOCKED_TYPE *_p)
{
return _FOP(fetch_sub, RELAXED, _p, 1);
}
FORCE_INLINE INTERLOCKED_TYPE _FNAME(,Decr,Acq) (INTERLOCKED_TYPE *_p)
{
return _FOP(fetch_sub, ACQUIRE, _p, 1);
}
FORCE_INLINE INTERLOCKED_TYPE _FNAME(,Decr,Rel) (INTERLOCKED_TYPE *_p)
{
return _FOP(fetch_sub, RELEASE, _p, 1);
}
#endif /* INTERLOCKED_NO_ARIT */
FORCE_INLINE INTERLOCKED_TYPE _FNAME(,Xchng,) (INTERLOCKED_TYPE *_p, INTERLOCKED_TYPE _v)
{
return _FOP(exchange_n, ACQ_REL, _p, _v);
}
FORCE_INLINE INTERLOCKED_TYPE _FNAME(,Xchng,Rx) (INTERLOCKED_TYPE *_p, INTERLOCKED_TYPE _v)
{
return _FOP(exchange_n, RELAXED, _p, _v);
}
FORCE_INLINE INTERLOCKED_TYPE _FNAME(,Xchng,Acq) (INTERLOCKED_TYPE *_p, INTERLOCKED_TYPE _v)
{
return _FOP(exchange_n, ACQUIRE, _p, _v);
}
FORCE_INLINE INTERLOCKED_TYPE _FNAME(,Xchng,Rel) (INTERLOCKED_TYPE *_p, INTERLOCKED_TYPE _v)
{
return _FOP(exchange_n, RELEASE, _p, _v);
}
FORCE_INLINE Boolean _FNAME(,Cas,) (INTERLOCKED_TYPE *_p, INTERLOCKED_TYPE _e, INTERLOCKED_TYPE _v)
{
return _FCAS(FALSE, ACQ_REL, ACQUIRE, _p, &_e, _v);
}
FORCE_INLINE Boolean _FNAME(,Cas,Rx) (INTERLOCKED_TYPE *_p, INTERLOCKED_TYPE _e, INTERLOCKED_TYPE _v)
{
return _FCAS(FALSE, RELAXED, RELAXED, _p, &_e, _v);
}
FORCE_INLINE Boolean _FNAME(,Cas,Acq) (INTERLOCKED_TYPE *_p, INTERLOCKED_TYPE _e, INTERLOCKED_TYPE _v)
{
return _FCAS(FALSE, ACQUIRE, ACQUIRE, _p, &_e, _v);
}
FORCE_INLINE Boolean _FNAME(,Cas,Rel) (INTERLOCKED_TYPE *_p, INTERLOCKED_TYPE _e, INTERLOCKED_TYPE _v)
{
return _FCAS(FALSE, RELEASE, RELAXED, _p, &_e, _v);
}
FORCE_INLINE Boolean _FNAME(Wk,Cas,) (INTERLOCKED_TYPE *_p, INTERLOCKED_TYPE _e, INTERLOCKED_TYPE _v)
{
return _FCAS(TRUE, ACQ_REL, ACQUIRE, _p, &_e, _v);
}
FORCE_INLINE Boolean _FNAME(Wk,Cas,Rx) (INTERLOCKED_TYPE *_p, INTERLOCKED_TYPE _e, INTERLOCKED_TYPE _v)
{
return _FCAS(TRUE, RELAXED, RELAXED, _p, &_e, _v);
}
FORCE_INLINE Boolean _FNAME(Wk,Cas,Acq) (INTERLOCKED_TYPE *_p, INTERLOCKED_TYPE _e, INTERLOCKED_TYPE _v)
{
return _FCAS(TRUE, ACQUIRE, ACQUIRE, _p, &_e, _v);
}
FORCE_INLINE Boolean _FNAME(Wk,Xchng,Rel) (INTERLOCKED_TYPE *_p, INTERLOCKED_TYPE _e, INTERLOCKED_TYPE _v)
{
return _FCAS(TRUE, RELEASE, RELAXED, _p, &_e, _v);
}
#undef _PASTE
#undef _XPASTE
#undef _FNAME
#undef _FOP
#undef _FCAS

View File

178
lonetix/include/df/sys/ip.h Executable file
View File

@ -0,0 +1,178 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
/**
* \file sys/ip.h
*
* Internet Protocol (IP) definitions and types.
*
* \copyright The DoubleFourteen Code Forge (C) All Right Reserved
* \author Lorenzo Cogotti
*/
#ifndef DF_SYS_IP_H_
#define DF_SYS_IP_H_
#include "xpt.h"
/// Known Internet Protocol address family types enumeration.
typedef enum {
IP4, ///< Internet Protocol version 4
IP6 ///< Internet Protocol version 6
} IpType;
/// Internet Protocol version 4 (IPv4) address, big endian order.
typedef union {
Uint8 bytes[4]; ///< Address bytes representation
Uint16 words[2]; ///< Address as a pair of 16-bits words
Uint32 dword; ///< Address as a single 32-bits dword (big endian)
} Ipv4adr;
/// Size of an IPv4 address in bytes.
#define IPV4_SIZE 4
/// Bits inside an IPv4 address.
#define IPV4_WIDTH (IPV4_SIZE * 8)
STATIC_ASSERT(sizeof(Ipv4adr) == IPV4_SIZE, "Bad Ipv4adr size");
/// Size of a string to make an IPv4 address, **excluding** trailing `\0`.
#define IPV4_STRLEN 16
/// IPv4 wildcard address static initializer: `0.0.0.0`.
#define IPV4_ANY_INIT { { 0x00, 0x00, 0x00, 0x00 } }
/// IPv4 loopback address static initializer: `127.0.0.1`.
#define IPV4_LOOPBACK_INIT { { 0x7f, 0x00, 0x00, 0x01 } }
/// IPv4 broadcast address static initializer: `255.255.255.255`.
#define IPV4_BROADCAST_INIT { { 0xff, 0xff, 0xff, 0xff } }
/**
* \brief Convert `Ipv4adr` to its string representation.
*
* The destination buffer **is assumed to be large enough to store
* the resulting string**, a buffer of `IPV4_STRLEN + 1`
* chars is safe to use as the `dest` argument.
*
* \param [in] adr Address to be converted, must not be `NULL`
* \param [out] dest Destination storage for string representation, must not be `NULL`
*
* \return Pointer to the trailing `\0` `char` inside `dest`, useful
* for further string concatenation.
*/
char *Ipv4_AdrToString(const Ipv4adr *adr, char *dest);
/**
* \brief Convert an address from its string representation to `Ipv4adr`.
*
* \return `OK` if conversion was successful, `NG` if address string
* does not represent a valid IP.
*/
Judgement Ipv4_StringToAdr(const char *address, Ipv4adr *dest);
/// Compare IPv4 addresses for equality.
FORCE_INLINE Boolean Ipv4_Equal(const Ipv4adr *a, const Ipv4adr *b)
{
return a->dword == b->dword;
}
/// Internet Protocol version 6 address.
typedef union {
Uint8 bytes[16]; ///< Address as raw bytes
Uint16 words[8]; ///< Address as short words sequence
Uint32 dwords[4]; ///< Address as dwords sequence
} Ipv6adr;
/// Size of an IPv6 address in bytes.
#define IPV6_SIZE 16
/// Bits inside an IPv6 address.
#define IPV6_WIDTH (IPV6_SIZE * 8)
STATIC_ASSERT(sizeof(Ipv6adr) == IPV6_SIZE, "Bad Ipv6adr size");
/// Size of a string to make an IPv6 address, **excluding** trailing `\0`.
#define IPV6_STRLEN 46
/// Static initializer for an UNSPECIFIED Ipv6 address.
#define IPV6_UNSPEC_INIT { { \
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 \
} }
/// Static initializer for a LOOPBACK Ipv6 address.
#define IPV6_LOOPBACK_INIT { { \
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 \
} }
/// Test whether an IPv6 address represents a mapped IPv4 address.
#define IS_IPV6_V4MAPPED(v6) \
((v6)->dwords[0] == 0 && \
(v6)->dwords[1] == 0 && \
(v6)->dwords[2] == BE32(0xffff))
/// Test whether an IPv6 address is multicast.
#define IS_IPV6_MULTICAST(v6) ((v6)->bytes[0] == 0xff)
/**
* \brief Convert an IPv6 address to its string representation, destination
* storage should be `[IPV6_STRLEN + 1]`.
*
* \return Pointer to the trailing `\0` inside `dest`.
*/
char *Ipv6_AdrToString(const Ipv6adr *adr, char *dest);
/**
* \brief Convert IPv6 address string to `Ipv6adr`.
*
* \return `OK` on success, `NG` if `address` does not represent a valid IPv6 address.
*/
Judgement Ipv6_StringToAdr(const char *address, Ipv6adr *dest);
/// Compare two IPv6 addresses for equality.
FORCE_INLINE Boolean Ipv6_Equal(const Ipv6adr *a, const Ipv6adr *b)
{
return a->dwords[0] == b->dwords[0] && a->dwords[1] == b->dwords[1] &&
a->dwords[2] == b->dwords[2] && a->dwords[3] == b->dwords[3];
}
/// Generic IP address.
typedef struct {
IpType family; ///< Currently stored address family
union {
Uint8 bytes[IPV6_SIZE]; ///< Address as raw bytes, for convenient initialization
Ipv4adr v4; ///< IPv4 address, if `family == IP4`
Ipv6adr v6; ///< IPv6 address, if `family == IP6`
Uint32 dword; ///< As IPv4 single dword, for macro compat
Uint16 words[8]; ///< As IPv4/IPv6 word sequence, for macro compat
Uint32 dwords[4]; ///< As IPv6 dwords sequence, for macro compat
};
} Ipadr;
/**
* \brief Convert a generic IP address to its string representation.
*
* \note A destination string of length `IPV6_STRLEN + 1`
* is capable of storing an address string for any address family.
*/
char *Ip_AdrToString(const Ipadr *adr, char *dest);
/**
* \brief Convert IP string representation to `Ipadr`.
*
* \return `OK` on success, `NG` if `address` is not a valid IP address.
*/
Judgement Ip_StringToAdr(const char *address, Ipadr *dest);
/// Compare generic addresses for equality.
FORCE_INLINE Boolean Ip_Equal(const Ipadr *a, const Ipadr *b)
{
if (a->family != b->family)
return FALSE;
switch (a->family) {
case IP4: return Ipv4_Equal(&a->v4, &b->v4);
case IP6: return Ipv6_Equal(&a->v6, &b->v6);
default: UNREACHABLE; return FALSE;
}
}
#endif

154
lonetix/include/df/sys/sys.h Executable file
View File

@ -0,0 +1,154 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
/**
* \file sys/sys.h
*
* Miscellaneous system functionality.
*
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
* \author Lorenzo Cogotti
*/
#ifndef DF_SYS_ERR_H_
#define DF_SYS_ERR_H_
#include "srcloc.h"
#include <stdarg.h>
#ifdef _MSC_VER
// for Sys_WipeMemory()
#include <intrin.h>
#pragma intrinsic(_ReadWriteBarrier)
#endif
/**
* \typedef SysRet
* \brief Platform-specific error code for system operations.
*/
typedef int SysRet; // errno type
/**
* \brief Error status structure.
*
* Stores latest operation result and current error handling callback.
*/
typedef struct {
/// Latest operation result status.
SysRet code;
/**
* \brief Error handler callback.
*
* \param [in] code System error code
* \param [in] reason Human readable concise message providing context to error, never `NULL`
* \param [in] loc Source location where error occurred, `NULL` if unavailable
* \param [in] obj User defined additional data forwarded to callback unaltered
*/
void (*func)(SysRet code, const char *reason, Srcloc *loc, void *obj);
/// Additional user data to be forwarded to `func`.
void *obj;
} SysErrStat;
/**
* \brief Abort error handler callback.
*
* This error handler terminates execution abnormally,
* attempting to log an error message as accurate as possible using system
* specific facilities.
*
* If this handler is registered, execution shall terminate as soon as an
* exceptional error condition is encountered in the system layer.
*/
#define SYS_ERR_ABORT ((void (*)(SysRet, const char *, Srcloc *, void *)) -1)
/**
* \brief Ignoring error handler callback.
*
* If this handler is registered any system error is ignored.
*/
#define SYS_ERR_IGN ((void (*)(SysRet, const char *, Srcloc *, void *)) 0)
/**
* \brief Terminating error handler callback.
*
* Terminates execution providing sensible error message, it differs
* from `SYS_ERR_ABORT` in that no exceptional termination semantics is
* implied, but rather an unsuccessful event that caused immediate exit
* (no core dump or stack traces are left behind).
*/
#define SYS_ERR_QUIT ((void (*)(SysRet, const char *, Srcloc *, void *)) 1)
/**
* \brief Install a system error handler callback.
*
* Initially installed handler is `SYS_ERR_IGN`,
* thus any error is effectively ignored unless a new handler is installed.
*
* Once installed, the error callback is called immediately for every
* exceptional error condition encountered inside system layer.
*
* Error handlers are thread-local, thus different threads may implement
* appropriate error handling policy without interfering with each other.
*
* \param [in] func Error handler function
* \param [in] obj Custom user-provided object passed unaltered upon callback
*
* \see `SysErrStat` for callback documentation
*/
void Sys_SetErrFunc(void (*func)(SysRet, const char *, Srcloc *, void *), void *obj);
/**
* Retrieve the system error status.
*
* \param [out] stat Storage for returned error status, may be `NULL`
*
* \return Last operation result status.
*/
SysRet Sys_GetErrStat(SysErrStat *stat);
/**
* Trigger an out of memory error.
*
* @note Depending on the current error policy, execution may
* very well continue after call returns.
*/
#define Sys_OutOfMemory() _Sys_OutOfMemory(__FILE__, __func__, __LINE__, 0)
// NOTE: implementation detail, should always be called through `Sys_OutOfMemory()`.
NOINLINE void _Sys_OutOfMemory(const char *,
const char *,
unsigned long long,
unsigned);
/**
* \brief Zero-out `nbytes` bytes inside `data`, preventing compiler optimization.
*
* A compiler might optimize-out `memset()` or similar operations if `data`
* is never read again.
* This function ensures such operation is never optimized away, which
* may be useful to zero-out sensitive data.
*/
FORCE_INLINE void Sys_WipeMemory(volatile void *data, size_t nbytes)
{
#if defined(_MSC_VER) || defined(__GNUC__)
EXTERNC void *memset(void *, int, size_t);
memset((void *) data, 0, nbytes);
// Compiler fence
#ifdef _MSC_VER
_ReadWriteBarrier();
#else
__asm__ __volatile__ ("" : : : "memory");
#endif
#else
// Slow but portable
volatile Uint8 *p = (volatile Uint8 *) data;
while (nbytes--)
*p++ = 0x00;
#endif
}
/// Sleep *at least* for the specified amount of **milliseconds**.
void Sys_SleepMillis(Uint32 millis);
#endif

157
lonetix/include/df/sys/vt100.h Executable file
View File

@ -0,0 +1,157 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
/**
* \file sys/vt100.h
*
* VT100 compliant control code sequences.
*
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
* \author Lorenzo Cogotti
*/
#ifndef DF_SYS_VT100_H_
#define DF_SYS_VT100_H_
#ifndef STR
#define STR(x) #x
#endif
#ifndef XSTR
#define XSTR(x) STR(x)
#endif
/// VT100 escape character constant, prefixed to any command
#define VTESC "\x1b"
/// Operating System Command marker, immediately following ESC
#define VTOSC "]"
/// CSI marker, immediately following ESC
#define VTCSI "["
/// Enabling command suffix
#define VTENB "h"
/// Disabling command suffix
#define VTDIS "l"
/// Designate Character Set - DEC Line mode drawing
#define VTDCSLIN VTESC "(0"
/// Designate Character Set - US ASCII (default) mode
#define VTDCSCHR VTESC "(B"
/// Soft Terminal Reset (reset terminal to default state)
#define VTSRST VTESC VTCSI "!p"
/// Cursor blink command mnemonic
#define ATT160 "?12"
/// Text Cursor Enable Mode command mnemonic
#define DECTCEM "?25"
/// Start cursor blinking
#define VTBLKENB VTESC VTCSI ATT160 VTENB
/// Stop cursor blinking
#define VTBLKDIS VTESC VTCSI ATT160 VTDIS
/// Show cursor inside console
#define VTCURSHW VTESC VTCSI DECTCEM VTENB
/// Hide console cursor
#define VTCURHID VTESC VTCSI DECTCEM VTDIS
/// Console Screen Buffer command mnemonic
#define DECSCRB "?1049"
/// Switch to Alternate Screen buffer
#define VTALTSCR VTESC VTCSI DECSCRB VTENB
/// Switch to Main Screen buffer
#define VTMAINSCR VTESC VTCSI DECSCRB VTDIS
/// Set Graphic Rendition command mnemonic
#define SGR "m"
/// Set Graphics Rendition to `N` (N is any of the SGR constants)
#define VTSGR(n) VTESC VTCSI XSTR(n) SGR
/// Obtain the corresponding background color code from a foreground color
#define VT_TOBG(c) ((c) + 10)
/// Obtain the corresponding bold/emphasized variant from a foreground or background color
#define VT_TOBLD(c) ((c) + 60)
/// Reset graphics rendition to its default mode (both foreground and background)
#define VTDFLT 0
/// Enable bold text/emphasis
#define VTBLD 1
/// Disable bold text/emphasis
#define VTNOBLD 22
/// Enable text underline
#define VTUND 4
/// Disable text underline
#define VTNOUND 24
/// Invert foreground and background color
#define VTINV 7
/// Restore foreground and background to their normal value
#define VTNOINV 27
/// Black color code (foreground)
#define VTBLK 30
/// Red color code (foreground)
#define VTRED 31
/// Green color code (foreground)
#define VTGRN 32
/// Yellow color code (foreground)
#define VTYEL 33
/// Blue color code (foreground)
#define VTBLUE 34
/// Magenta color code (foreground)
#define VTMAGN 35
/// Cyan color code (foreground)
#define VTCYAN 36
/// White color code (foreground)
#define VTWHIT 37
/// Code to restore foreground color to its default value
#define VTFGDFLT 39
#define VTBGBLK 40
#define VTBGRED 41
#define VTBGGRN 42
#define VTBGYEL 43
#define VTBGBLUE 44
#define VTBGMAGN 45
#define VTBGCYAN 46
#define VTBGWHIT 47
#define VTBGDFLT 49
#define VTBLK_BLD 90
#define VTRED_BLD 91
#define VTGRN_BLD 92
#define VTYEL_BLD 93
#define VTBLUE_BLD 94
#define VTMAGN_BLD 95
#define VTCYAN_BLD 96
#define VTWHIT_BLD 97
#define VTBGBLK_BLD 100
#define VTBGRED_BLD 101
#define VTBGGRN_BLD 102
#define VTBGYEL_BLD 103
#define VTBGBLUE_BLD 104
#define VTBGMAGN_BLD 105
#define VTBGCYAN_BLD 106
#define VTBGWHIT_BLD 107
/// Erase in Display command mnemonic
#define ED "J"
/// Erase in Line command mnemonic
#define EL "K"
/// Erase in Display with command parameter `n`
#define VTED(n) VTESC VTCSI XSTR(n) ED
/// Erase in Line with command parameter `n`
#define VTEL(n) VTESC VTCSI XSTR(n) EL
/// Erase from cursor (inclusive) to end of display/line
#define VTCUR 0
/// Erase from the beginning of the line/display to end
#define VTSET 1
/// Erase everything in line/display
#define VTALL 2
#endif

92
lonetix/include/df/utf/utf.h Executable file
View File

@ -0,0 +1,92 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
/**
* \file utf/utf.h
*
* UTF-8 decoding and encoding functionality.
*
* \author Russ Cox
* \author Rob Pike
* \author Ken Thompson
* \author Lorenzo Cogotti
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved.
*
* This API is derived by work authored by Russ Cox - namely the Unix port of the Plan 9
* UTF-8 library, originally written by Rob Pike and Ken Thompson.
*
* Original license terms follow:
* ```
* Copyright © 2021 Plan 9 Foundation
*
* 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 substantial 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.
* ```
* The original libutf library is available at: https://9fans.github.io/plan9port/unix/libutf.tgz
*/
#ifndef DF_UTF_H_
#define DF_UTF_H_
#include "utf/utfdef.h"
/**
* \brief Convert the first UTF-8 rune inside `\0` terminated string `str` to a `Rune` in `dest`.
*
* \return Number of bytes read from `str` for the returned `Rune`.
*
* \note Returned bytes are usually equivalent to `runelen()` over the returned `Rune`,
* but values may differ in case of a decoding error. In that case `¢hartorune()` returns `RUNE_ERR`,
* and returns 1. This allows the caller to skip one byte and move on with the decoding.
*/
size_t chartorune(Rune *dest, const char *str);
/// Inverse of `chartorune()`.
size_t runetochar(char *dest, Rune r);
/// Calculate the number of bytes necessary to encode `r`.
size_t runelen(Rune r);
/// Calculate the number of bytes necessary to encode the first `n` runes referenced by `r`.
size_t runenlen(const Rune *r, size_t n);
/// Test whether the first `n` bytes referenced by `src` form at least one `Rune`.
Boolean fullrune(const char *src, size_t n);
/// Convert `r` to lowercase.
Rune tolowerrune(Rune r);
/// Convert 'r` to uppercase.
Rune toupperrune(Rune r);
/// Convert `r` to titlecase.
Rune totitlerune(Rune r);
/// Test whether `r` is a lowercase UTF-8 rune.
Boolean islowerrune(Rune r);
/// Test whether `r` is an uppercase UTF-8 rune.
Boolean isupperrune(Rune r);
/// Test whether `r` represents an alphabetic UTF-8 rune.
Boolean isalpharune(Rune r);
/// Test wheter `r` is a title-case UTF-8 rune.
Boolean istitlerune(Rune r);
/// Test whether `r` represents a space UTF-8 rune.
Boolean isspacerune(Rune r);
/// Return the number of runes inside the `\0` terminated UTF-8 string `s`.
size_t utflen(const char *s);
/// Find the first occurrence of `r` inside the `\0' terminated UTF-8 string `s`, `NULL` if not found.
char *utfrune(const char *s, Rune r);
/// Find the last occurrence of `r` inside the `\0` terminated UTF-8 string `s`, `NULL` if not found.
char *utfrrune(const char *s, Rune r);
/// Find the first occurrence of the UTF-8 `\0` terminated UTF-8 string `needle` inside `haystack`, `NULL` if not found.
char *utfutf(const char *haystack, const char *needle);
#endif

27
lonetix/include/df/utf/utfdef.h Executable file
View File

@ -0,0 +1,27 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
/**
* \file utf/utfdef.h
*
* UTF-8 types and macro constants definitions.
*
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
* \author Lorenzo Cogotti
*/
#ifndef DF_UTFDEF_H_
#define DF_UTFDEF_H_
#include "xpt.h"
/// 32-bits unsigned type capable of holding any UTF-8 character.
typedef Uint32 Rune;
#define MAXUTF 4u ///< Maximum bytes per `Rune`
#define RUNE_SYNC 0x80 ///< Cannot represent part of a UTF sequence (<)
#define RUNE_SELF 0x80 ///< `Rune` and UTF sequences are the same (<)
#define RUNE_ERR 0xfffdu ///< Decoding error in UTF
#define MAXRUNE 0x10ffffu ///< Maximum `Rune` value
#define BOM 0xefbbbfu ///< UTF-8 BOM marker
#endif

491
lonetix/include/df/xpt.h Normal file
View File

@ -0,0 +1,491 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
/**
* \file xpt.h
*
* Cross platform types and definitions.
*
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
* \author Lorenzo Cogotti
*
* Defines fixed-size integer types and other general utility types and
* other macros that are ought to be available and equivalent across any
* supported platform.
*
* This header keeps its dependencies at a bare minimum, and **MUST NOT**
* excessively pollute the namespace.
*
* Including this header makes symbols from `stddef.h` and `stdint.h` visible.
*/
#ifndef DF_XPT_H_
#define DF_XPT_H_
#include <stddef.h>
#include <stdint.h>
// Extern function declarations for inline functions on header
/**
* \def EXTERNC
*
* Expands to `extern` or `extern "C"` depending on whether or not the compiler
* supports C++.
*
* This is useful to declare `extern` C functions inside inline functions
* code within .h files, in order to avoid a full fledged `#include`.
*/
#ifdef __cplusplus
#define EXTERNC extern "C"
#else
#define EXTERNC extern
#endif
// Boolean and fixed size types
typedef int Boolean; ///< A boolean value, legal values are `TRUE` or `FALSE`
typedef int ToggleState; ///< Switch toggle state, legal values are `ON` or `OFF`
typedef int Judgement; ///< Operation result outcome, legal values are `OK` or `NG`
/// Boolean values (`true`, `false`).
enum BooleanLogic {
FALSE = 0, ///< `false` boolean value, guaranteed to be 0.
TRUE = 1 ///< `true` boolean value, guaranteed to be 1.
};
/// Switch toggle states (`on`, `off`).
enum BooleanSwitch {
OFF = 0, ///< `off` value, guaranteed to be 0.
ON = 1 ///< `on` value, guaranteed to be 1.
};
/// Operation success states (success, failure).
enum BooleanJudgement {
OK = 0, ///< Success state, guaranteed to be 0 (no errors).
NG = -1 ///< Failure state, negative value, equals to -1 (no-good).
};
typedef char Boolean8; ///< Packed boolean type, may hold the same values as `Boolean`
typedef int8_t Sint8; ///< Fixed-size signed 8-bits integer type
typedef uint8_t Uint8; ///< Fixed-size unsigned 8-bits integer type
typedef int16_t Sint16; ///< Fixed-size signed 16-bits integer type
typedef uint16_t Uint16; ///< Fixed-size unsigned 16-bits integer type
typedef int32_t Sint32; ///< Fixed-size signed 32-bits integer type
typedef uint32_t Uint32; ///< Fixed-size unsigned 32-bits integer type
typedef int64_t Sint64; ///< Fixed-size signed 64-bits integer type
typedef uint64_t Uint64; ///< Fixed-size unsigned 64-bits integer type
typedef intptr_t Sintptr; ///< Fixed-size signed type large enough to store a pointer
typedef uintptr_t Uintptr; ///< Fixed-size unsigned type large enough to store a pointer
// Fixed size compile time constants
#define S8_C(c) INT8_C(c) ///< Expands constant `c` to a signed 8 bit integer literal
#define U8_C(c) UINT8_C(c) ///< Expands constant `c` to an unsigned 8 bit integer literal
#define S16_C(c) INT16_C(c) ///< Expands constant `c` to a signed 16 bit integer literal
#define U16_C(c) UINT16_C(c) ///< Expands constant `c` to an unsigned 16 bit integer literal
#define S32_C(c) INT32_C(c) ///< Expands constant `c` to a signed 32 bit integer literal
#define U32_C(c) UINT32_C(c) ///< Expands constant `c` to an unsigned 32 bit integer literal
#define S64_C(c) INT64_C(c) ///< Expands constant `c` to a signed 64 bit integer literal
#define U64_C(c) UINT64_C(c) ///< Expands constant `c` to an unsigned 64 bit integer literal
// Compiler support for thread-locals, over/under-alignment,
// inline and no-return functions
// NOTE: we don't support TinyCC because there's no TLS support there
#ifdef __TINYC__
#error "Sorry, no TLS support available on Tiny C Compiler"
#endif
/**
* \def THREAD_LOCAL
*
* \brief Declares a static variable as thread local.
*
* \def ALIGNED(a, what)
*
* \brief Force type, variable, or member alignment.
*
* \param a Required alignment, must be a positive power of two and a compile-time integer literal.
* \param what What should be aligned, a variable, type or member
*
* \def NORETURN
*
* \brief Declare a function shall never return to its caller (like `exit()`).
*
* \warning Behavior is undefined if function does return.
*
* \def UNREACHABLE
*
* \brief Mark code as unreachable.
*
* \warning Behavior is undefined if code is actually reached during execution.
*
* \def FORCE_INLINE
*
* \brief Require function inlining, regardless of compiler policy.
*
* \note Function may be inlined even in debug builds.
*
* \def INLINE
*
* \brief Suggest a function should be inlined, compiler may still choose not to.
*
* \def NOINLINE
*
* \brief Force the compiler **not** to inline a function,
* regardless of its own policy.
*/
#ifdef _MSC_VER
#define THREAD_LOCAL __declspec(thread)
#else
#define THREAD_LOCAL __thread
#endif
#ifdef _MSC_VER
#define ALIGNED(a, what) __declspec(align(a)) what
#else
#define ALIGNED(a, what) what __attribute__((__aligned__(a)))
#endif
#ifdef _MSC_VER
#define NORETURN __declspec(noreturn)
#else
#define NORETURN __attribute__((__noreturn__))
#endif
#ifdef _MSC_VER
#define UNREACHABLE __assume(0)
#elif defined(__GNUC__)
#define UNREACHABLE __builtin_unreachable()
#else
#define UNREACHABLE ((void) 0) /*NOTREACHED*/
#endif
#ifdef _MSC_VER
#define FORCE_INLINE __forceinline
#define INLINE __inline
#define NOINLINE __declspec(noinline)
#elif defined(__GNUC__)
#define FORCE_INLINE static __inline__ __attribute__((__always_inline__, __unused__))
#define INLINE static __inline__ __attribute__((__unused__))
#define NOINLINE __attribute__((__noinline__))
#else
// this is a crude fallback and actually loses quite a bit of the intended semantics...
#if 0
#error "No FORCE_INLINE and NOINLINE for this platform"
#endif
#define FORCE_INLINE static inline
#define INLINE static inline
#define NOINLINE
#endif
// Compiler checks
/**
* \def CHECK_PRINTF
*
* \brief Compiler check on `printf()`-like function arguments.
*
* If compiler supports it, check at compile-time that `printf()` and `vprintf()`
* like formatted message arguments are valid.
*
* \param f `const char *` message template argument index (1 is first argument)
* \param a Variadic arguments start (`...` argument index), use 0 if function takes a `va_list` (1 is first argument)
*
* \def STATIC_ASSERT
*
* \brief Compile time assertion.
*
* If compiler supports it, test a condition at compile time, aborting
* compilation on failure.
*
* \param what Expression to be evaluated at compile time
* \param msg Failure message
*/
#ifdef __GNUC__
#define CHECK_PRINTF(f, a) __attribute__((__format__(__printf__, f, a)))
#else
#define CHECK_PRINTF(f, a)
#endif
#ifndef __cplusplus
#define STATIC_ASSERT(what, msg) _Static_assert(what, msg)
#else
#define STATIC_ASSERT(what, msg) static_assert(what, msg)
#endif
// NOTE: if we ever need packing just use the MSVC style #pragma pack(push, N)
// + #pragma pack(pop), it's pretty much universal.
// Platform suitable memory alignment type
/**
* \def ALIGNMENT
*
* \brief Platform memory alignment required for primitive types.
*
* \note This alignment is only safe for primitive and trivial struct types,
* but may be insufficient for SIMD types or overaligned types.
*/
#ifdef _MSC_VER
#ifdef _WIN32
#define ALIGNMENT 4
#else
#define ALIGNMENT 8
#endif
#elif defined(__GNUC__)
#define ALIGNMENT __BIGGEST_ALIGNMENT__
#else
// conservative guess
#define ALIGNMENT 16
#endif
/**
* \brief Perform or test alignment of size `x` or pointer `p` to `align` bytes.
*
* \param x Unsigned integral size in bytes
* \param p A pointer
* \param align Unsigned integral power of two specifying alignment
*
* \warning `align` must be a power of 2, macro arguments are evaluated more
* than once.
*
* @{
* \def ALIGN
* \def ALIGN_DOWN
* \def ALIGN_PTR
* \def ALIGN_PTR_DOWN
*
* \def IS_ALIGNED
* \def IS_PTR_ALIGNED
* @}
*/
#define ALIGN(x, align) \
(((x) + ((align) - 1)) & ~((align) - 1))
#define ALIGN_DOWN(x, align) \
ALIGN((x) - ((align) - 1), align)
#define ALIGN_PTR(p, align) \
((void *) ALIGN((uintptr_t) (p), align))
#define ALIGN_PTR_DOWN(p, align) \
((void *) ALIGN_DOWN((uintptr_t) (p), align))
#define IS_ALIGNED(x, align) (((x) & ((align) - 1)) == 0)
#define IS_PTR_ALIGNED(p, align) IS_ALIGNED((Uintptr) p, align)
/**
* \def alloca
*
* \brief Dynamic memory allocation on stack.
*
* \param size Size to be allocated, in bytes, unsigned integral value
*
* \warning Allocating large chunks of memory on stack is a sure one-way ticket
* to a stack overflow, there is no way to indicate memory allocation
* failures for `alloca()`.
*/
#ifdef _MSC_VER
#define alloca(size) _alloca(size)
#else
#define alloca(size) __builtin_alloca(size)
#endif
/**
* \def ARRAY_SIZE
*
* \brief Array element count.
*
* \param array Array to be sized
*/
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
#endif
/**
* \def BIT
*
* \brief Return unsigned integer constant with a single bit set.
*
* \param idx Bit index to be set (0 is LSB)
*/
#ifndef BIT
#define BIT(idx) (1uLL << (idx))
#endif
/**
* \def USED
*
* Suppress unused variable warnings for `x`.
*
* \param x Identifier to mark as used
*/
#ifndef USED
#define USED(x) ((void) (x))
#endif
/**
* \def EDN_LE
*
* \brief Constant identifying little-endian byte ordering (LSB first).
*
* \def EDN_BE
*
* \brief Constant identifying big-endian byte ordering (MSB first).
*
* \def EDN_NATIVE
*
* \brief Constant identifying native byte ordering,
* equals either `EDN_BE` or `EDN_LE`.
*/
#ifdef _WIN32
#define EDN_LE 0
#define EDN_BE 1
#define EDN_NATIVE EDN_LE
#else
#define EDN_LE __ORDER_LITTLE_ENDIAN__
#define EDN_BE __ORDER_BIG_ENDIAN__
#define EDN_NATIVE __BYTE_ORDER__
#endif
#if EDN_NATIVE != EDN_LE && EDN_NATIVE != EDN_BE
#error "Unsupported platform endianness"
#endif
/**
* \brief Swaps bytes inside 16, 32 and 64 bits unsigned integers.
*
* Truncates `x` to the required amount of bytes, if necessary,
* and reverses each byte around. The value is then returned.
* If `x` is a compile-time constant the result is also a constant.
*
* \param x Unsigned integer value to be byte-swapped
*
* \warning `x` is evaluated multiple times.
*
* @{
* \def BSWAP16
* \def BSWAP32
* \def BSWAP64
* @}
*/
#ifndef BSWAP16
#define BSWAP16(x) ((Uint16) ( \
(((x) & 0xff00u) >> 8) | (((x) & 0x00ffu) << 8) \
))
#endif
#ifndef BSWAP32
#define BSWAP32(x) ((Uint32) ( \
(((x) & 0xff000000u) >> 24) | (((x) & 0x00ff0000u) >> 8) | \
(((x) & 0x0000ff00u) << 8) | (((x) & 0x000000ffu) << 24) \
))
#endif
#ifndef BSWAP64
#define BSWAP64(x) ((Uint64) ( \
(((x) & 0xff00000000000000uLL) >> 56) | \
(((x) & 0x00ff000000000000uLL) >> 40) | \
(((x) & 0x0000ff0000000000uLL) >> 24) | \
(((x) & 0x000000ff00000000uLL) >> 8) | \
(((x) & 0x00000000ff000000uLL) << 8) | \
(((x) & 0x0000000000ff0000uLL) << 24) | \
(((x) & 0x000000000000ff00uLL) << 40) | \
(((x) & 0x00000000000000ffuLL) << 56) \
))
#endif
/**
* \brief Declare a fixed-size big or little endian constant.
*
* Following macros may be used to declare fixed size
* constant of a specific endianness at compile time.
* Original value is swapped if destination endianness
* is not match the host endianness. These macros are usable for
* variables as well, but functions declared in `sys/endian.h` should
* be preferred whenever possible (i.e. use these macros on variables
* only inside headers to reduce `#include`s).
*
* \param x Constant or variable, which is truncated to the required size.
*
* \return `x` as a constant or variable byte-swapped as needed.
*
* \warning `x` is evaluated multiple times.
*
* @{
* \def BE16
* \def BE32
* \def BE64
* \def LE16
* \def LE32
* \def LE64
* @}
*/
#if EDN_NATIVE == EDN_LE
#define BE16(x) BSWAP16(x)
#define BE32(x) BSWAP32(x)
#define BE64(x) BSWAP64(x)
#define LE16(x) ((Uint16) (x))
#define LE32(x) ((Uint32) (x))
#define LE64(x) ((Uint64) (x))
#else
#define BE16(x) ((Uint16) (x))
#define BE32(x) ((Uint32) (x))
#define BE64(x) ((Uint64) (x))
#define LE16(x) BSWAP16(x)
#define LE32(x) BSWAP32(x)
#define LE64(x) BSWAP64(x)
#endif
/**
* \brief Minimum, maximum and clamped to range values.
*
* \param x, y Generic comparable values
* \param a, b Clamping range, inclusive, `a` must be less than or equal to `b`
*
* \warning Arguments are evaluated more than once.
*
* @{
* \def MIN
* \def MAX
* \def CLAMP
* @}
*/
#ifndef MIN
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
#endif
#ifndef MAX
#define MAX(x, y) (((x) < (y)) ? (y) : (x))
#endif
#ifndef CLAMP
#define CLAMP(x, a, b) MAX(a, MIN(x, b))
#endif
/**
* \def ABS
*
* \brief Absolute value of `x` using ternary `?:` operator.
*
* \param x An expression comparable with 0
*
* \warning `x` is evaluated more than once.
*/
#ifndef ABS
#define ABS(x) (((x) >= 0) ? (x) : -(x))
#endif
/**
* \def FLEX_ARRAY
*
* \brief Clarity macro to convey that a trailing array member inside
* a `struct` is an array of arbitrary size.
*/
#ifdef __cplusplus
#define FLEX_ARRAY 1
#elif defined(_MSC_VER) || defined(__GNUC__)
#define FLEX_ARRAY
#else
#define FLEX_ARRAY 1
#endif
#endif