mirror of
https://codeberg.org/1414codeforge/ubgpsuite.git
synced 2025-06-05 21:29:11 +02:00
[*] Initial commit
This commit is contained in:
87
lonetix/include/df/argv.h
Normal file
87
lonetix/include/df/argv.h
Normal 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
87
lonetix/include/df/bgp/asn.h
Executable 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
1198
lonetix/include/df/bgp/bgp.h
Executable file
File diff suppressed because it is too large
Load Diff
62
lonetix/include/df/bgp/bytebuf.h
Executable file
62
lonetix/include/df/bgp/bytebuf.h
Executable 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
90
lonetix/include/df/bgp/dump.h
Executable 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
888
lonetix/include/df/bgp/mrt.h
Executable 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
|
44
lonetix/include/df/bgp/patricia.h
Executable file
44
lonetix/include/df/bgp/patricia.h
Executable 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
222
lonetix/include/df/bgp/prefix.h
Executable 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
385
lonetix/include/df/bgp/vm.h
Executable 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
250
lonetix/include/df/bgp/vmintrin.h
Executable 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
162
lonetix/include/df/bufio.h
Normal 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
347
lonetix/include/df/chkint.h
Normal 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
134
lonetix/include/df/cpr/bzip2.h
Executable file
@ -0,0 +1,134 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
/**
|
||||
* \file cpr/bzip2.h
|
||||
*
|
||||
* Burrows–Wheeler 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
121
lonetix/include/df/cpr/flate.h
Executable 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
118
lonetix/include/df/cpr/xz.h
Executable 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
475
lonetix/include/df/lexer.h
Normal 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
85
lonetix/include/df/mem.h
Normal 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
|
154
lonetix/include/df/mem_file.h
Normal file
154
lonetix/include/df/mem_file.h
Normal 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
191
lonetix/include/df/numlib.h
Normal 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
|
45
lonetix/include/df/srcloc.h
Normal file
45
lonetix/include/df/srcloc.h
Normal 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
140
lonetix/include/df/stm.h
Normal 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
198
lonetix/include/df/strlib.h
Normal 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
104
lonetix/include/df/sys/con.h
Executable 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
59
lonetix/include/df/sys/dbg.h
Executable 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
132
lonetix/include/df/sys/endian.h
Executable 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
253
lonetix/include/df/sys/fs.h
Executable 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
58
lonetix/include/df/sys/fsdef.h
Executable 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
|
136
lonetix/include/df/sys/interlocked.h
Executable file
136
lonetix/include/df/sys/interlocked.h
Executable 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
|
63
lonetix/include/df/sys/interlocked_intrin_msvc.h
Executable file
63
lonetix/include/df/sys/interlocked_intrin_msvc.h
Executable 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"
|
||||
|
191
lonetix/include/df/sys/interlocked_ops_gcc.h
Executable file
191
lonetix/include/df/sys/interlocked_ops_gcc.h
Executable 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
|
0
lonetix/include/df/sys/interlocked_ops_msvc.h
Executable file
0
lonetix/include/df/sys/interlocked_ops_msvc.h
Executable file
178
lonetix/include/df/sys/ip.h
Executable file
178
lonetix/include/df/sys/ip.h
Executable 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
154
lonetix/include/df/sys/sys.h
Executable 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
157
lonetix/include/df/sys/vt100.h
Executable 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
92
lonetix/include/df/utf/utf.h
Executable 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
27
lonetix/include/df/utf/utfdef.h
Executable 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
491
lonetix/include/df/xpt.h
Normal 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
|
||||
|
Reference in New Issue
Block a user