Crelude

A personal C (C11) prelude / standard library.

For enhanced C syntax highlighting whilst using this library, consider using my .vim/after/syntax/c.vim.

Build and Install

make
PREFIX=/usr/local make install

or similar.

Generates tests executable and libcrelude.so. Tested on GNU+Linux and macOS, on both x86_64 and aarch64 (ARM) architectures.

TODO

  • Completely implement the standard libc printf formatters.
  • Cut down on GNU extensions.

title: Classes


Classes

  • struct Args
  • struct GenericArray
    Array with pointer to void.
  • struct GenericMap
    Hash-table that maps void * to void *.
  • struct GenericSlice
    Slice with pointer to void.
  • struct MemArray
    Array with pointer type to smallest addressable units of memory.
  • struct MemSlice
    Slice with pointer type to smallest addressable units of memory.
  • struct RunicBuilder
    Mutable runic string which is built/pushed-to over time.
  • struct StringBuilder
    Mutable string which is built/pushed-to over time.
  • struct _Arg
  • struct _ArgID
  • struct _ArgParser
  • struct _ArgTemplate
  • struct _Natural
  • struct _atomic_t
    Useful for resource counting etc.
  • struct runic
    Imutable warpper for UCS-4/UTF-32 encoded runic string (runes are mutable).
  • struct string
    Immutable wrapper for UTF-8 encoded string (bytes are mutable).
  • struct symbol
    Symbols are interned strings.

Updated on 23 August 2022 at 00:54:19 UTC


title: symbol summary: Symbols are interned strings.


symbol

Symbols are interned strings. #include <common.h>

Public Attributes

Public Attributes Documentation

variable hash

u64 hash;

variable value

string value;

Updated on 23 August 2022 at 00:54:19 UTC


title: StringBuilder summary: Mutable string which is built/pushed-to over time.


StringBuilder

Mutable string which is built/pushed-to over time. #include <common.h>

Public Attributes

Public Attributes Documentation

variable value

byte * value;

variable len

usize len;

variable cap

usize cap;

Updated on 23 August 2022 at 00:54:19 UTC


title: string summary: Immutable wrapper for UTF-8 encoded string (bytes are mutable).


string

Immutable wrapper for UTF-8 encoded string (bytes are mutable). #include <common.h>

Public Attributes

Public Attributes Documentation

variable value

byte * value;

variable len

usize len;

Updated on 23 August 2022 at 00:54:19 UTC


title: RunicBuilder summary: Mutable runic string which is built/pushed-to over time.


RunicBuilder

Mutable runic string which is built/pushed-to over time. #include <common.h>

Public Attributes

Public Attributes Documentation

variable value

rune * value;

variable len

usize len;

variable cap

usize cap;

Updated on 23 August 2022 at 00:54:19 UTC


title: runic summary: Imutable warpper for UCS-4/UTF-32 encoded runic string (runes are mutable).


runic

Imutable warpper for UCS-4/UTF-32 encoded runic string (runes are mutable). #include <common.h>

Public Attributes

Public Attributes Documentation

variable value

rune * value;

variable len

usize len;

Updated on 23 August 2022 at 00:54:19 UTC


title: MemSlice summary: Slice with pointer type to smallest addressable units of memory.


MemSlice

Slice with pointer type to smallest addressable units of memory. #include <common.h>

Public Attributes

Public Attributes Documentation

variable value

umin * value;

variable len

usize len;

Updated on 23 August 2022 at 00:54:19 UTC


title: MemArray summary: Array with pointer type to smallest addressable units of memory.


MemArray

Array with pointer type to smallest addressable units of memory. #include <common.h>

Public Attributes

Public Attributes Documentation

variable value

umin * value;

variable len

usize len;

variable cap

usize cap;

Updated on 23 August 2022 at 00:54:19 UTC


title: GenericSlice summary: Slice with pointer to void.


GenericSlice

Slice with pointer to void. #include <common.h>

Public Attributes

Public Attributes Documentation

variable value

u0 * value;

variable len

usize len;

Updated on 23 August 2022 at 00:54:19 UTC


title: GenericMap summary: Hash-table that maps void * to void *.


GenericMap

Hash-table that maps void * to void *. #include <common.h>

Public Attributes

Public Attributes Documentation

variable len

usize len;

variable hash

u64 hash;

variable value

u0 * value;

variable key

struct GenericMap::@18::@19::@20 key;

variable next

u0 * next;

variable value

struct GenericMap::@18::@19 * value;

variable cap

usize cap;

variable buckets

struct GenericMap::@18 buckets;

variable value_size

usize value_size;

variable key_size

usize key_size;

variable node_size

usize node_size;

variable hash_offset

usize hash_offset;

variable key_offset

usize key_offset;

variable value_offset

usize value_offset;

variable next_offset

usize next_offset;

variable key_type

HashKeyType key_type;

variable hasher

u64(* hasher;

Updated on 23 August 2022 at 00:54:19 UTC


title: GenericArray summary: Array with pointer to void.


GenericArray

Array with pointer to void. #include <common.h>

Public Attributes

Public Attributes Documentation

variable value

u0 * value;

variable len

usize len;

variable cap

usize cap;

Updated on 23 August 2022 at 00:54:19 UTC


title: Args


Args

#include <argparse.h>

Public Attributes

Public Attributes Documentation

variable len

usize len;

variable hash

u64 hash;

variable value

ArgID value;

variable key

struct Args::@1::@2::@3 key;

variable value

Arg value;

variable next

u0 * next;

variable value

struct Args::@1::@2 * value;

variable cap

usize cap;

variable buckets

struct Args::@1 buckets;

variable value_size

usize value_size;

variable key_size

usize key_size;

variable node_size

usize node_size;

variable hash_offset

usize hash_offset;

variable key_offset

usize key_offset;

variable value_offset

usize value_offset;

variable next_offset

usize next_offset;

variable key_type

HashKeyType key_type;

variable hasher

u64(* hasher;

Updated on 23 August 2022 at 00:54:19 UTC


title: _Natural


_Natural

Public Attributes

Name
u64value

Public Attributes Documentation

variable value

u64 value;

Updated on 23 August 2022 at 00:54:19 UTC


title: _atomic_t summary: Useful for resource counting etc.


_atomic_t

Useful for resource counting etc. #include <common.h>

Public Attributes

Name
intvalue

Public Attributes Documentation

variable value

int value;

Updated on 23 August 2022 at 00:54:19 UTC


title: _ArgTemplate


_ArgTemplate

#include <argparse.h>

Public Attributes

Public Attributes Documentation

variable id

ArgID id;

variable short_form

string short_form;

variable long_form

string long_form;

variable takes_value

bool takes_value;

variable help

string help;

Updated on 23 August 2022 at 00:54:19 UTC


title: _ArgParser


_ArgParser

#include <argparse.h>

Public Attributes

Name
u32magic
ArgTemplate *value
usizelen
usizecap
struct _ArgParser::@5templates
u64hash
bytevalue
struct _ArgParser::@6::@8::@9::@10key
usizevalue
u0 *next
struct _ArgParser::@6::@8::@9 *value
struct _ArgParser::@6::@8buckets
usizevalue_size
usizekey_size
usizenode_size
usizehash_offset
usizekey_offset
usizevalue_offset
usizenext_offset
HashKeyTypekey_type
u64(*hasher
struct _ArgParser::@6index_of_short_form
stringvalue
struct _ArgParser::@7::@12::@13::@14key
struct _ArgParser::@7::@12::@13 *value
struct _ArgParser::@7::@12buckets
struct _ArgParser::@7index_of_long_form
boolawaiting_value
ArgIDawaiting_value_id
usizestring_count
stringerror_message

Public Attributes Documentation

variable magic

u32 magic;

variable value

ArgTemplate * value;

variable len

usize len;

variable cap

usize cap;

variable templates

struct _ArgParser::@5 templates;

variable hash

u64 hash;

variable value

byte value;

variable key

struct _ArgParser::@6::@8::@9::@10 key;

variable value

usize value;

variable next

u0 * next;

variable value

struct _ArgParser::@6::@8::@9 * value;

variable buckets

struct _ArgParser::@6::@8 buckets;

variable value_size

usize value_size;

variable key_size

usize key_size;

variable node_size

usize node_size;

variable hash_offset

usize hash_offset;

variable key_offset

usize key_offset;

variable value_offset

usize value_offset;

variable next_offset

usize next_offset;

variable key_type

HashKeyType key_type;

variable hasher

u64(* hasher;

variable index_of_short_form

struct _ArgParser::@6 index_of_short_form;

variable value

string value;

variable key

struct _ArgParser::@7::@12::@13::@14 key;

variable value

struct _ArgParser::@7::@12::@13 * value;

variable buckets

struct _ArgParser::@7::@12 buckets;

variable index_of_long_form

struct _ArgParser::@7 index_of_long_form;

variable awaiting_value

bool awaiting_value;

variable awaiting_value_id

ArgID awaiting_value_id;

variable string_count

usize string_count;

variable error_message

string error_message;

Updated on 23 August 2022 at 00:54:19 UTC


title: _ArgID


_ArgID

#include <argparse.h>

Public Attributes

Name
u16value

Public Attributes Documentation

variable value

u16 value;

Updated on 23 August 2022 at 00:54:19 UTC


title: _Arg


_Arg

#include <argparse.h>

Public Attributes

Name
stringvalue
boolis_on

Public Attributes Documentation

variable value

string value;

variable is_on

bool is_on;

Updated on 23 August 2022 at 00:54:19 UTC


title: Files


Files


Updated on 23 August 2022 at 00:54:19 UTC


title: src


src

Directories

Files


Updated on 23 August 2022 at 00:54:19 UTC


title: src/crelude


src/crelude

Files


Updated on 23 August 2022 at 00:54:19 UTC


title: src/crelude/argparse.c


src/crelude/argparse.c

Source code

#include "argparse.h"
#include "common.h"
#include "io.h"

#ifndef IMPLEMENTATION

u0 arginit(ArgParser *ctx)
{
    ctx->magic = ARGP_MAGIC_INIT_CONST;
    ctx->templates = ANEW(ctx->templates, 15);
    ctx->index_of_short_form = MNEW(ctx->index_of_short_form, 15);
    ctx->index_of_long_form  = MNEW(ctx->index_of_long_form, 15);
    ctx->awaiting_value = false;
    ctx->string_count = 0;
    ctx->error_message = EMPTY(string);
}

ArgID argreg(ArgParser *ctx, char *short_form, char *long_form, bool takes_value, char *help)
{
    if (nil == ctx || ctx->magic != ARGP_MAGIC_INIT_CONST)
        panic("you did not init (`arginit`) the ArgParse structure.");

    usize index = ctx->templates.len;
    ArgID id = { (u16)index };
    string short_form_str = nil == short_form ? EMPTY(string) : from_cstring(short_form);
    string  long_form_str = nil ==  long_form ? EMPTY(string) : from_cstring(long_form);
    ArgTemplate template = {
        .id = id,
        .short_form = short_form_str,
        .long_form = long_form_str,
        .takes_value = takes_value,
        .help = from_cstring(help),
    };

    PUSH(ctx->templates, template);
    unless (nil == short_form) {
        if ('-' == short_form[0]) short_form += 1;
        ASSOCIATE(ctx->index_of_short_form, short_form[0], index);
    }
    unless (nil == long_form) {
        if (0 == strncmp(long_form, "--", 2)) long_form += 2;
        ASSOCIATE(ctx->index_of_long_form, from_cstring(long_form), index);
    }

    return id;
}

ierr argparse_c(ArgParser *ctx, u0 *map_id_to_arg, char *arg)
{
    return argparse(ctx, map_id_to_arg, from_cstring(arg));
}

ierr argparse(ArgParser *ctx, u0 *map_id_to_arg, string arg)
{
    ArgTemplate template;
    Arg argument;
    ArgID arg_id;
    usize *id = nil;
    char *equal_sign = nil;
    bool disabled = '+' == GET(arg, 0);

    if (ctx->awaiting_value) {
        // set value of argument awaiting a value.
        argument = (Arg){ .value = arg, .is_on = true };
        associate(map_id_to_arg, &ctx->awaiting_value_id, &argument);
        ctx->awaiting_value = false;
    } else if (0 == string_ncmp(arg, STR("--"), 2)) {
        // parsing long-form argument.
        equal_sign = strchr(UNWRAP(arg), '=');
        if (nil != equal_sign)
            arg = SLICE(string, arg, 0, equal_sign - UNWRAP(arg));
        arg = SLICE(string, arg, 2, -1);  // skip leading '--'

        id = LOOKUP(ctx->index_of_long_form, arg);
        if (nil == id) {
            ctx->error_message = sprint("no such argument `--%S' exists.", arg);
            return ARGPARSE_UNKNOWN;
        }
        arg_id = (ArgID){ *id };
        // get template for argument to determine behaviour.
        template = GET(ctx->templates, *id);
        if (template.takes_value) {
            // takes an argument.
            if (nil != equal_sign) {
                // value comes after equal sign.
                argument = (Arg){ .value = from_cstring(equal_sign + 1), .is_on = true };
                associate(map_id_to_arg, &arg_id, &argument);
            } else {
                // value comes in the next argument.
                ctx->awaiting_value = true;
                ctx->awaiting_value_id = arg_id;
            }
        } else {
            // is a toggle argument.
            argument = (Arg){ .value = EMPTY(string), .is_on = true };
            associate(map_id_to_arg, &arg_id, &argument);
        }
    } else if ('-' == GET(arg, 0) || '+' == GET(arg, 0)) {
        // parsing short-form argument(s).
        arg = SLICE(string, arg, 1, -1);  // skip the dash (-)
        foreach (opt, arg) {
            id = LOOKUP(ctx->index_of_short_form, opt);
            if (nil == id) {
                ctx->error_message = sprint("no such argument `-%c' exists.", opt);
                return ARGPARSE_UNKNOWN;
            }
            arg_id = (ArgID){ *id };
            // get template for argument to determine behaviour.
            template = GET(ctx->templates, *id);
            if (template.takes_value) {
                // takes an argument.
                if (arg.len != 1 && !it.first) {
                    // option with argument mixed in with other options: not allowed (ambiguous).
                    ctx->error_message = sprint("argument `-%c' takes a value, and must stand alone.", opt);
                    return ARGPARSE_INVALID;
                } else if (arg.len != 1) {
                    // the argument is glued onto the end (e.g. `-n13` <-> `-n 13`)
                    argument = (Arg){ .value = SLICE(string, arg, 1, -1), .is_on = true };
                    associate(map_id_to_arg, &arg_id, &argument);
                    break;  // the argument has been consumed, no further options.
                } else {
                    // argument value comes in the next argument.
                    ctx->awaiting_value = true;
                    ctx->awaiting_value_id = arg_id;
                }
            } else {
                // is a toggle argument.
                argument = (Arg){ .value = EMPTY(string), .is_on = !disabled };
                associate(map_id_to_arg, &arg_id, &argument);
            }
        }
    } else {
        ctx->error_message = sprint("unexpected string `%S' when parsing arguments.", arg);
        return ARGPARSE_UNEXPECTED;
    }

    ++ctx->string_count;
    return ARGPARSE_OK;
}

ierr argparseall_c(ArgParser *ctx, u0 *map_id_to_arg, usize argc, char **argv)
{
    ierr err = OK;

    for (usize i = 0; i < argc; ++i)
        if (OK != (err = argparse_c(ctx, map_id_to_arg, argv[i]))) break;

    return err;
}

#ifdef ENTRY_FUNCTION
ierr argparseall(ArgParser *ctx, u0 *map_id_to_arg, Arguments args)
{
    ierr err = OK;

    foreach (arg, args)
        unless (OK == (err = argparse(ctx, map_id_to_arg, arg))) break;

    return err;
}
#endif

#endif

Updated on 23 August 2022 at 00:54:19 UTC


title: src/crelude/argparse.h


src/crelude/argparse.h

More...

Classes

Name
struct_Arg
struct_ArgID
structArgs
struct_ArgTemplate
struct_ArgParser

Types

Name
enum@0 { ARGPARSE_OK = EXIT_SUCCESS, ARGPARSE_INVALID, ARGPARSE_UNEXPECTED, ARGPARSE_EXPECTED_ARGUMENT, ARGPARSE_UNKNOWN}
typedef struct _ArgArg
typedef struct _ArgIDArgID
typedef struct _ArgTemplateArgTemplate
typedef struct _ArgParserArgParser

Functions

Name
ArgIDargreg(ArgParser * ctx, char * short_form, char * long_form, bool takes_value, char * help)
u0arginit(ArgParser * ctx)
Initialise argument parser.
ierrargparse_c(ArgParser * ctx, u0 * map_id_to_arg, char * arg)
Parse arguments one value of argv at the time.
ierrargparse(ArgParser * ctx, u0 * map_id_to_arg, string arg)
Parse arguments one string value from argv at the time.
ierrargparseall_c(ArgParser * ctx, u0 * map_id_to_arg, usize argc, char ** argv)
Parse arguments from argc and argv.

Attributes

Name
u64hash
ArgIDvalue
struct @4key
u0 *next

Defines

Detailed Description

Parsing command line arguments.

Types Documentation

enum @0

EnumeratorValueDescription
ARGPARSE_OKEXIT_SUCCESS
ARGPARSE_INVALID
ARGPARSE_UNEXPECTED
ARGPARSE_EXPECTED_ARGUMENT
ARGPARSE_UNKNOWN

typedef Arg

typedef struct _Arg Arg;

typedef ArgID

typedef struct _ArgID ArgID;

typedef ArgTemplate

typedef struct _ArgTemplate ArgTemplate;

typedef ArgParser

typedef struct _ArgParser ArgParser;

Functions Documentation

function argreg

ArgID argreg(
    ArgParser * ctx,
    char * short_form,
    char * long_form,
    bool takes_value,
    char * help
)

function arginit

u0 arginit(
    ArgParser * ctx
)

Initialise argument parser.

function argparse_c

ierr argparse_c(
    ArgParser * ctx,
    u0 * map_id_to_arg,
    char * arg
)

Parse arguments one value of argv at the time.

function argparse

ierr argparse(
    ArgParser * ctx,
    u0 * map_id_to_arg,
    string arg
)

Parse arguments one string value from argv at the time.

function argparseall_c

ierr argparseall_c(
    ArgParser * ctx,
    u0 * map_id_to_arg,
    usize argc,
    char ** argv
)

Parse arguments from argc and argv.

Attributes Documentation

variable hash

u64 hash;

variable value

ArgID value;

variable key

struct @4 key;

variable next

u0 * next;

Macros Documentation

define ARGP_MAGIC_INIT_CONST

#define ARGP_MAGIC_INIT_CONST 0x61726770

Source code


#pragma once
#include "common.h"

#define ARGP_MAGIC_INIT_CONST 0x61726770
                             /* a r g p */

enum {
    ARGPARSE_OK = OK,
    ARGPARSE_INVALID,  //< invalid argument structure.
    ARGPARSE_UNEXPECTED,  //< unexpected argument while parsing.
    ARGPARSE_EXPECTED_ARGUMENT,  //< expected value to be given to parameter.
    ARGPARSE_UNKNOWN,  // < unknown argument/option given.
};

record(Arg) {
    string value;  //< slice into ARGV string, nil string if no argument.
    bool is_on;  //< true if argument toggled on, false if not (-a/+a).
};

newtype(ArgID, u16);
newmap(Args, ArgID, Arg);

record(ArgTemplate) {
    ArgID id;
    string short_form; //< may be nil.
    string long_form;  //< may be nil.
    bool takes_value;
    string help; //< may be nil, but morally shouldn't be.
};

record(ArgParser) {
    u32 magic;
    arrayof(ArgTemplate) templates;
    mapof(byte,   usize) index_of_short_form;
    mapof(string, usize) index_of_long_form;
    bool awaiting_value;
    ArgID awaiting_value_id;
    usize string_count;  //< current index into ARGV.
    string error_message;  //< error if encountered.
};

ArgID argreg(ArgParser *ctx, char *short_form, char *long_form, bool takes_value, char *help);

u0 arginit(ArgParser *ctx);

ierr argparse_c(ArgParser *ctx, u0 *map_id_to_arg, char *arg);

ierr argparse(ArgParser *ctx, u0 *map_id_to_arg, string arg);

ierr argparseall_c(ArgParser *ctx, u0 *map_id_to_arg, usize argc, char **argv);

#ifdef ENTRY_FUNCTION
ierr argparseall(ArgParser *ctx, u0 *map_id_to_arg, Arguments args);
#endif

Updated on 23 August 2022 at 00:54:19 UTC


title: src/crelude/base64.c


src/crelude/base64.c

Source code

#include "base64.h"
#include "log.h"

#ifndef IMPLEMENTATION

usize base64_encoded_size(usize original)
{
    usize size = original;
    unless (original % 3 == 0)
        size += 3 - (original % 3);
    size /= 3;
    size *= 4;

    return size;
}

usize base64_decoded_size(MemSlice encoded)
{
    usize size = encoded.len / 4 * 3;

    for (usize i = encoded.len; i-- > 0;)
        if (NTH(encoded, i) == '=') --size;
        else break;

    return size;
}

MemSlice base64_encode(MemSlice data)
{
    if (data.len == 0 || PTR(data) == nil)
        return EMPTY(MemSlice);

    usize len = base64_encoded_size(data.len);
    MemSlice out = SMAKE(umin, len + 1);
    NTH(out, len) = NUL;  //< NUL-terminate the bytes.
    --out.len;  //< Don't *count* NUL.

    for (usize i = 0, j = 0; i < data.len; i += 3, j += 4) {
        usize v = NTH(data, i);
        bool not_two_from_end = i + 2 < data.len;
        bool not_one_from_end = i + 1 < data.len;

        v = v << 8 | NTH(data, i + 1) * not_one_from_end;
        v = v << 8 | NTH(data, i + 2) * not_two_from_end;

        NTH(out, j)     = NTH(BASE64_DIGITS, (v >> 18) & 0x3F);
        NTH(out, j + 1) = NTH(BASE64_DIGITS, (v >> 12) & 0x3F);

        NTH(out, j + 2) = not_one_from_end
            ? NTH(BASE64_DIGITS, (v >> 6) & 0x3F) : '=';
        NTH(out, j + 3) = not_two_from_end
            ? NTH(BASE64_DIGITS,        v & 0x3F) : '=';
    }

    return out;
}

MemSlice base64_decode(MemSlice data)
{
    if (data.len == 0 || PTR(data) == nil)
        return EMPTY(MemSlice);

    if (LAST(data) == NUL)
        --data.len;

    foreach (digit, data)
        unless (is_base64_digit(digit)) {
            LOG(ERROR, "invalid base64 digit: %hhX (%c)", digit, digit);
            return EMPTY(MemSlice);
        }

    usize len = base64_decoded_size(data);
    MemSlice out = SMAKE(umin, len + 1);
    NTH(out, len) = NUL;  //< NUL-terminate the bytes.
    --out.len;  //< Don't *count* NUL.

    for (usize i = 0, j = 0; i < data.len; i += 4, j += 3) {
        // `v` is 4 bytes.  We decode sets of 4 base-64 digits into 3 bytes.
        u32 v = NTH(BASE64_INVERSES, NTH(data, i) - '+');
        v = (v << 6) | NTH(BASE64_INVERSES, NTH(data, i + 1) - '+');

        bool not_2_from_padding = NTH(data, i + 2) != '=';
        bool not_3_from_padding = NTH(data, i + 3) != '=';

        v = v << 6 | NTH(BASE64_INVERSES, NTH(data, i + 2) - '+')
                   * not_2_from_padding;
        v = v << 6 | NTH(BASE64_INVERSES, NTH(data, i + 3) - '+')
                   * not_3_from_padding;

        NTH(out, j) = (v >> 16) & 0xFF;
        if (not_2_from_padding)
            NTH(out, j + 1) = (v >> 8) & 0xFF;
        if (not_3_from_padding)
            NTH(out, j + 2) = v & 0xFF;
    }

    return out;
}

#endif

Updated on 23 August 2022 at 00:54:19 UTC


title: src/crelude/base64.h


src/crelude/base64.h

More...

Functions

Name
i8 *base64_decode_table(void )
boolis_base64_digit(byte d)
usizebase64_encoded_size(usize )
Calculate encoded size given original size.
usizebase64_decoded_size(MemSlice )
Calculate decoded size given the endcoded data.
MemSlicebase64_encode(MemSlice )
MemSlicebase64_decode(MemSlice )

Attributes

Name
byte *value
usizelen
const struct @16BASE64_DIGITS
Encoding characters / digits, each representing a value between 0..63.
const struct @17BASE64_INVERSES
Inverse/decoding table. (-1) represents absence from the table.

Detailed Description

Base-64 encoding and decoding.

Functions Documentation

function base64_decode_table

static i8 * base64_decode_table(
    void 
)

Generates the above inverses table (length 80). This could be a constexpr or something in a better language.

function is_base64_digit

static inline bool is_base64_digit(
    byte d
)

function base64_encoded_size

usize base64_encoded_size(
    usize 
)

Calculate encoded size given original size.

function base64_decoded_size

usize base64_decoded_size(
    MemSlice 
)

Calculate decoded size given the endcoded data.

function base64_encode

MemSlice base64_encode(
    MemSlice 
)

Return: A slice holding a heap-allocated free-able pointer to the encoded data.

Encode bytes into base-64.

function base64_decode

MemSlice base64_decode(
    MemSlice 
)

Return: A heap allocated slice holding the raw decoded data.

Decode from base-64 to raw bytes. If input is not a valid base-64 string, an empty nil-slice will be returned.

Attributes Documentation

variable value

byte * value;

variable len

usize len;

variable BASE64_DIGITS

static const struct @16 BASE64_DIGITS = 
{   .[len](/crelude/Files/base64_8h.md#variable-len) = sizeof(( [byte](/crelude/Files/common_8h.md#typedef-byte) []) { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" } )/sizeof( [byte](/crelude/Files/common_8h.md#typedef-byte) ),   .[value](/crelude/Files/common_8h.md#variable-value) = ( [byte](/crelude/Files/common_8h.md#typedef-byte) []) { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" }   };

Encoding characters / digits, each representing a value between 0..63.

variable BASE64_INVERSES

static const struct @17 BASE64_INVERSES = 









{   .[len](/crelude/Files/base64_8h.md#variable-len) = sizeof(( [i8](/crelude/Files/common_8h.md#typedef-i8) []) {      62, -1, -1, -1, 63, 52, 53, 54, 55, 56,      57, 58, 59, 60, 61, -1, -1, -1, -1, -1,      -1, -1,  0,  1,  2,  3,  4,  5,  6,  7,       8,  9, 10, 11, 12, 13, 14, 15, 16, 17,      18, 19, 20, 21, 22, 23, 24, 25, -1, -1,      -1, -1, -1, -1, 26, 27, 28, 29, 30, 31,      32, 33, 34, 35, 36, 37, 38, 39, 40, 41,      42, 43, 44, 45, 46, 47, 48, 49, 50, 51   } )/sizeof( [i8](/crelude/Files/common_8h.md#typedef-i8) ),   .[value](/crelude/Files/common_8h.md#variable-value) = ( [i8](/crelude/Files/common_8h.md#typedef-i8) []) {      62, -1, -1, -1, 63, 52, 53, 54, 55, 56,      57, 58, 59, 60, 61, -1, -1, -1, -1, -1,      -1, -1,  0,  1,  2,  3,  4,  5,  6,  7,       8,  9, 10, 11, 12, 13, 14, 15, 16, 17,      18, 19, 20, 21, 22, 23, 24, 25, -1, -1,      -1, -1, -1, -1, 26, 27, 28, 29, 30, 31,      32, 33, 34, 35, 36, 37, 38, 39, 40, 41,      42, 43, 44, 45, 46, 47, 48, 49, 50, 51   }   };

Inverse/decoding table. (-1) represents absence from the table.

Source code


#pragma once
#include "common.h"

static const sliceof(byte) BASE64_DIGITS = INIT(byte,
    { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" });

static const sliceof(i8) BASE64_INVERSES = INIT(i8, {
//  ------------------10------------------
    62, -1, -1, -1, 63, 52, 53, 54, 55, 56, // |
    57, 58, 59, 60, 61, -1, -1, -1, -1, -1, // |
    -1, -1,  0,  1,  2,  3,  4,  5,  6,  7, // |
     8,  9, 10, 11, 12, 13, 14, 15, 16, 17, // 8
    18, 19, 20, 21, 22, 23, 24, 25, -1, -1, // |
    -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, // |
    32, 33, 34, 35, 36, 37, 38, 39, 40, 41, // |
    42, 43, 44, 45, 46, 47, 48, 49, 50, 51  // |
});

static i8 *base64_decode_table(void) __attribute__((unused));
static i8 *base64_decode_table()
{
    static i8 table[80];
    memset(table, -1, sizeof(table));

    for (usize i = 0; i < BASE64_DIGITS.len - 1; ++i)
        table[NTH(BASE64_DIGITS, i) - '+'] = i;

    return &*table;
}

static inline bool is_base64_digit(byte d)
{
    return (d >= '0' && d <= '9')
        || (d >= 'A' && d <= 'Z')
        || (d >= 'a' && d <= 'z')
        ||  d == '+' || d == '/'
        ||  d == '=';
}

usize base64_encoded_size(usize);
usize base64_decoded_size(MemSlice);

MemSlice base64_encode(MemSlice);

MemSlice base64_decode(MemSlice);

Updated on 23 August 2022 at 00:54:19 UTC


title: src/crelude/common.c


src/crelude/common.c

Source code

#include "common.h"
#include "io.h"
#include "utf.h"

#include <assert.h>

#ifndef IMPLEMENTATION

inline u0 *or(const u0 *nullable, const u0 *nonnull)
{
    if (nullable == nil)
        return (u0 *)nonnull;
    return (u0 *)nullable;
}

bool is_zero(imax n)
{ return n == 0; }

bool is_zerof(f64 n)
{ return n == 0.0; }

bool is_zeroed(u0 *ptr, usize width)
{
    umin *xs = (umin *)ptr;
    until (width-- == 0) unless (*xs++ == 0) return false;
    return true;
}

u0 zero(u0 *blk, usize width)
{
    if (blk == nil || width == 0)
        return UNIT;
    umin *bytes = blk;
    until (width-- == 0)
        *bytes++ = 0;
    return UNIT;
}

u0 *emalloc(usize len, usize size)
{
    usize bytes = len * size;
    u0 *m = MALLOC(bytes);
    if (m == nil)
        PANIC("Could not allocate %zu bytes.", bytes);
    zero(m, bytes);
    return m;
}

/* in-place */
u0 reverse(u0 *self, usize width)
{
    umin chunk[width];  //< C99 variable-length array.
    MemArray *arr = self;
    umin *ptr = arr->value;
    usize len = arr->len * width;

    for (usize i = 0, j = len - 1; i < j; ++i, --j) {
        memcpy(  chunk, ptr + i, width);
        memcpy(ptr + i, ptr + j, width);
        memcpy(ptr + j,   chunk, width);
    }

    return UNIT;
}

MemSlice reverse_endianness(MemSlice bytes)
{
    MemSlice copy;
    copy = COPY(bytes);

    for (usize i = 0, j = copy.len - 1; i < j; ++i, --j) {
        umin temp = NTH(copy, i);
        NTH(copy, i) = NTH(copy, j);
        NTH(copy, j) = temp;
    }

    return copy;
}

bool is_little_endian()
{
    uword w = 0x01;
    return *(umin *)&w == 1;
}

u128 big_endian(umin *start, usize bytes)
{
    assert(bytes <= sizeof(u128));
    // Write to offset in word (big endian), then reverse
    // depending on endianess. i.e., [0x32][0xF1] becomes:
    //   [0x00][0x00][0x32][0xF1]  (big endian), or
    //   [0xF1][0x32][0x00][0x00]  (little endian)
    umin mem[sizeof(u128)];
    memset(mem, 0, sizeof(u128));
    memcpy(mem + sizeof(u128) - bytes, start, bytes);

    MemSlice reversed = VIEW(MemSlice, mem, 0, sizeof(u128));
    if (is_little_endian())  // reverse `mem` if little endian.
        reverse(&reversed, 1);

    return *(u128 *)mem;
}

u0 memswap(umin *a, umin *b, usize bytes)
{
    usize words = bytes / WORD_SIZE;
    usize trail = words * WORD_SIZE;  //< Where left-over bytes start.

    // Swap word sized blocks first.
    for (usize i = 0; i < words; i += WORD_SIZE) {
        uword tmp;
        memcpy( &tmp, a + i, WORD_SIZE);
        memcpy(a + i, b + i, WORD_SIZE);
        memcpy(b + i,  &tmp, WORD_SIZE);
    }
    // Swap remaining bytes, byte-by-byte.
    for (usize i = trail; i < bytes; ++i) {
        umin tmp = a[i];
        a[i] = b[i];
        b[i] = tmp;
    }
}

static u0 swap_blocks(MemSlice block, usize pivot)
{  // Tail-recursion should be optimised here.
    if (pivot == 0 || pivot == block.len)
        return;  // We are already done.

    MemSlice A = SLICE(MemSlice, block, 0, pivot);
    MemSlice B = SLICE(MemSlice, block, pivot, -1);
    MemSlice A1, A2;
    MemSlice B1, B2;

    if (A.len < B.len) {
        B1 = SLICE(MemSlice, B, 0, -A.len - 1);
        B2 = SLICE(MemSlice, B, B1.len, -1);
        assert(A.len == B2.len);

        memswap(A.value, B2.value, A.len);
        B = SLICE(MemSlice, block, 0, B.len);
        swap_blocks(B, pivot);
    } else if (A.len > B.len) {
        A1 = SLICE(MemSlice, A, 0, B.len);
        A2 = SLICE(MemSlice, A, A1.len, -1);
        assert(B.len == A1.len);
        assert(pivot == A1.len + A2.len);

        memswap(B.value, A1.value, B.len);
        A = SLICE(MemSlice, block, B.len, -1);
        swap_blocks(A, pivot - B.len);
    } else {
        // Same length, and thus trivial.
        memswap(A.value, B.value, A.len);
    }
}

u0 swap(u0 *self, usize pivot, usize width)
{
    // Deal with everything in terms of bytes.
    MemSlice blk = *(MemSlice *)self;
    blk.len *= width;
    usize pos = pivot * width;
    swap_blocks(blk, pos);
}

usize resize(u0 *self, usize cap, usize width)
{
    MemArray *arr = self;
    if (arr->cap == cap)
        return arr->cap - arr->len;
    // Lowering the capacity; best done by REALLOC.
    if (cap < arr->cap) {
        arr->value = REALLOC(arr->value, cap * width);
    } else {  // Growing the capacity; must zero bytes.
        umin *new = emalloc(cap, width);
        memcpy(new, arr->value, arr->cap * width);
        FREE(arr->value);
        arr->value = new;
    }

    arr->cap = cap;
    return arr->cap - arr->len;
}

usize grow(u0 *self, usize count, usize width)
{
    MemArray *arr = self;
    usize old_cap = arr->cap;
    usize new_cap = arr->cap;

    if (arr->len + count > arr->cap) {  // reallocate array.
        new_cap = (usize)(arr->cap * ARRAY_REALLOC_FACTOR) + count;
        resize(arr, new_cap, width);
    }

    arr->len += count;
    return new_cap - old_cap;
}

u0 *get(u0 *self, usize index, usize width)
{
    MemArray *arr = self;
    if (index > arr->len) return nil;

    return arr->value + index * width;
}

u0 *set(u0 *self, usize index, const u0 *elem, usize width)
{
    MemArray *arr = self;
    if (index > arr->len || elem == nil) return nil;

    memcpy(arr->value + index * width, elem, width);
    return arr->value + index * width;
}

usize push(u0 *restrict self, const u0 *restrict element, usize width)
{
    if (element == nil) return 0;

    MemArray *arr = (MemArray *restrict)self;
    umin *elem = (umin *restrict)element;

    usize growth = grow(arr, 1, width);
    memcpy(arr->value + width * (arr->len - 1), elem, width);
    return growth;
}

usize insert(u0 *restrict self, usize index,
             const u0 *restrict element, usize width)
{
    if (element == nil) return 0;

    MemArray *arr = (MemArray *restrict)self;
    umin *elem = (umin *restrict)element;
    usize growth = grow(arr, 1, width);

    umin *gap = arr->value + width * index;
    // Offset elements down by one, from insertion index,
    // leaving us with a gap for insertion.
    memmove(gap + width, gap, width * (arr->len - 1 - index));
    // Insert element into the empty space.
    memcpy(gap, elem, width);

    return growth;
}

usize extend(u0 *restrict self, const u0 *restrict slice, usize width)
{
    if (slice == nil) return 0;

    MemArray *arr = (MemArray *restrict)self;
    MemSlice *sub = (MemSlice *restrict)slice;

    if (sub->len == 0) return 0;

    usize end = arr->len;  //< old array length.
    usize growth = grow(arr, sub->len, width);

    memcpy(arr->value + width * end, sub->value, width * sub->len);
    return growth;
}

usize splice(u0 *restrict self, usize index, const u0 *restrict slice, usize width)
{
    if (slice == nil) return 0;

    MemArray *arr = (MemArray *restrict)self;
    MemSlice *sub = (MemSlice *restrict)slice;

    if (sub->len == 0) return 0;

    usize end = arr->len;
    usize growth = grow(arr, sub->len, width);
    umin *gap = arr->value + width * index;
    // Offset elements down by the slice length, from insertion index,
    // leaving us with a `width * sub.len` byte gap for insertion.
    memmove(gap + width * sub->len, gap, width * (end - index));
    // Insert slice into empty space.
    memcpy(gap, sub->value, sub->len * width);

    return growth;
}

GenericSlice cut(u0 *self, usize from, isize upto, usize width)
{
    GenericArray *arr_ptr = self;
    MemArray arr = *(MemArray *)self;

    usize final = upto < 0 ? arr.len + upto : (usize)upto;
    assert(final < arr.len);

    if (from > final) {
        usize tmp = from;
        from = final;
        final = tmp;
    }
    usize pivot = final - from + 1;

    // Deal with bytes instead.
    arr.len *= width;
    arr.cap *= width;
    from *= width;
    final *= width;
    pivot *= width;

    MemSlice tail = SLICE(MemSlice, arr, from, arr.len);
    swap_blocks(tail, pivot);
    tail = SLICE(MemSlice, tail, tail.len - pivot, -1);

    u0 *tail_ptr = &tail;
    GenericSlice trailing = *(GenericSlice *)tail_ptr;
    trailing.len /= width;
    arr_ptr->len -= pivot / width;  // Capacity the same.
    return trailing;  // Return removed slice which is left over at end.
}

u0 *pop(u0 *self, usize width)
{
    MemArray *arr = self;
    assert(arr->len > 0);
    return (u0 *)(arr->value + --arr->len * width);
}

u0 *shift(u0 *self, usize width)
{
    MemSlice *arr = self;
    assert(arr->len > 0);

    swap(self, 1, width);
    return (u0 *)(arr->value + --arr->len * width);
}

usize null(u0 *self, usize width)
{
    GenericArray arr = *(GenericArray *)self;
    usize bytes = arr.cap * width;
    zero(PTR(arr), bytes);
    return bytes;
}

string from_cstring(const byte *cstring)
{
    return VIEW(string, (byte *)cstring, 0, strlen(cstring));
}

bool string_eq(string self, const string other)
{
    unless (self.len == other.len)
        return false;
    else if (self.value == other.value)
        return true;

    foreach (c, self)
        if (c != NTH(other, it.index))
            return false;
    return true;
}

i16 string_ncmp(const string self, const string other, usize n)
{ return strncmp(UNWRAP(self), UNWRAP(other), n); }

i16 string_cmp(const string self, const string other)
{
    byte *ptr0 = self.value,
         *ptr1 = other.value;

    if (ptr0 == ptr1) {
        if (self.len == other.len) return 0;
        return self.len > other.len
            ? ptr0[other.len] - 0
            : 0 - ptr1[self.len];
    }

    byte c0, c1;
    usize len = min(self.len, other.len);
    for (usize i = 0; i < len; ++i)
        if ((c0 = ptr0[i]) != (c1 = ptr1[i]))
            return c0 - c1;

    if (self.len == other.len) return 0;
    if (self.len == len)
        return 0 - ptr1[len];
    return ptr0[len] - 0;
}

u64 hash_bytes(MemSlice mem)
{
    u64 hash = 5381;
    foreach (c, mem)
        hash += c + (hash << 5);

    return hash;
}

u64 hash_string(string str)
{ return hash_bytes(TO_BYTES(str)); }

static u64 node_hash(const u0 *self, const u0 *node)
{
    const GenericMap *map = self;
    return *(u64 *)((umin *)node + map->hash_offset);
}
static u0 *node_key(const u0 *self, const u0 *node)
{
    const GenericMap *map = self;
    return (umin *)node + map->key_offset;
}
static u0 *node_value(const u0 *self, const u0 *node)
{
    const GenericMap *map = self;
    return (umin *)node + map->value_offset;
}
static u0 *node_next(const u0 *self, const u0 *node)
{
    const GenericMap *map = self;
    return (umin *)node + map->next_offset;
}

static usize bucket_index(const u0 *self, u64 hash)
{
    const GenericMap *map = self;
    return (usize)hash % map->buckets.cap;
}

// TODO: right now we check proper equality, maybe just use the hash,
//       and don't worry about hash-collisions?  `u64` is quite large after all.
static bool key_eq(const u0 *self, const u0 *key0, const u0 *key1)
{
    if (key0 == key1) return true;
    const GenericMap *map = self;

    switch (map->key_type) {
    case HKT_STRING:
    case HKT_MEM_SLICE:;
        const string *s0 = key0, *s1 = key1;
        return string_eq(*s0, *s1);
    case HKT_RUNIC:;
        const runic *r0 = key0, *r1 = key1;
        MemSlice _m0 = TO_BYTES(*r0), _m1 = TO_BYTES(*r1);
        MemSlice *m0 = &_m0, *m1 = &_m1;
        return string_eq(*(string *)m0, *(string *)m1);
    case HKT_CSTRING:
        return 0 == strcmp(*(byte **)key0, *(byte **)key1);
    case HKT_SMALL_INTEGER:
    case HKT_RAW_BYTES:
        return 0 == memcmp(key0, key1, map->key_size);
    }

    return PANIC("Improper hash-map key_type."), false;
}

usize init_hashnode(u0 *node, const u0 *_map, u64 hash, const u0 *key, const u0 *value)
{
    GenericMap *map = (u0 *)_map;
    umin *ptr = node;

    memcpy(ptr + map-> hash_offset, &hash, sizeof(u64));
    memcpy(ptr + map->  key_offset,   key, map->key_size);
    memcpy(ptr + map->value_offset, value, map->value_size);
    memset(ptr + map-> next_offset,     0, sizeof(u0 *));  //< NULL-pointing.

    return map->node_size;
}

u0 associate(u0 *self, const u0 *key, const u0 *value)
{
    GenericMap *map = self;
    const usize NODE_SIZE = map->node_size;

    u64 hash = map->hasher(key, map->key_size);
    usize index = bucket_index(map, hash);

    u0 *head = (umin *)PTR(map->buckets) + index * NODE_SIZE;
    u0 *node = head;  /*< type of `hashnode(K, V)`. */
    u0 *last = nil;
    // Walk up node-chain trying to find if key is already present.
    until (node == nil || node_hash(map, node) == 0) {  // zero-hash = unpopulated.
        if (key_eq(map, node_key(map, node), key)) {
            // Found node with equal key, so rewrite value.
            memcpy(node_value(map, node), value, map->value_size);
            return UNIT;
        }
        last = node;
        node = *(u0 **)node_next(map, node);
    }
    // Otherwise, insert the new key-value pair into the chain.
    assert(node == nil || node_hash(map, node) == 0);
    ++map->len;

    u0 *new = emalloc(1, NODE_SIZE);
    init_hashnode(new, map, hash, key, value);

    if (last == nil) {  // i.e. chain hasn't started.
        assert(node == head);
        memcpy(head, new, NODE_SIZE);
        grow(&map->buckets, 1, NODE_SIZE);
        FREE(new);
    } else {  // otherwise, make the node part of the linked list.
        u0 **last_next = node_next(map, last);
        assert(*last_next == nil);
        *last_next = new;
    }

    const f64 LOAD_FACTOR = (f64)map->len / map->buckets.cap;
    if (LOAD_FACTOR < HASHMAP_LOAD_THRESHOLD)
        return UNIT;
    // ^ if load factor (entries per potential bucket) gets over ~85%,
    // we should ~double it in capacity, preventing the linked lists
    // from getting to long, and lookup time too slow.

    GenericArray snodes = AMAKE(umin[NODE_SIZE], map->len);
    for (usize i = 0; i < map->buckets.cap; ++i) {
        u0 *snode = (umin *)PTR(map->buckets) + i * NODE_SIZE;
        if (node_hash(map, snode) == 0) continue;
        bool first = true;
        until (snode == nil) {
            u0 **next_field = node_next(map, snode);
            u0 *next = *next_field;
            *next_field = nil;
            push(&snodes, snode, NODE_SIZE);
            snode = next;
            if (first) { first = false; continue; }
            FREE(snode);  // we have a copy.
        }
    }
    // copied `snodes` (base/bucket nodes), now blank out the buckets array,
    // and rewrite it with new and correct indices.
    resize(&map->buckets,
           CEIL(usize, HASHMAP_GROWTH_FACTOR
             * (LOAD_FACTOR / HASHMAP_LOAD_THRESHOLD)
             * map->buckets.cap),
           NODE_SIZE);
    null(&map->buckets, NODE_SIZE);
    // repopulate.
    umin *bucket = (u0 *)PTR(map->buckets);
    for (usize i = 0; i < snodes.len; ++i) {
        u0 *snode = get(&snodes, i, NODE_SIZE);
        u64 shash = node_hash(map, snode);
        usize sindex = bucket_index(map, shash);
        u0 *bnode = bucket + sindex * NODE_SIZE;
        // copy directly into bucket array.
        if (node_hash(map, bnode) == 0) {
            memcpy(bnode, snode, NODE_SIZE);
            continue;
        }
        // otherwise, append to chain.
        until (*(u0 **)node_next(map, bnode) == nil)
            bnode = *(u0 **)node_next(map, bnode);
        assert(bnode != nil);
        // put `snode` on heap, and link up pointer to it.
        u0 *new_snode = emalloc(1, NODE_SIZE);
        memcpy(new_snode, snode, NODE_SIZE);
        u0 **next_field = node_next(map, bnode);
        *next_field = new_snode;
    }
    FREE_INSIDE(snodes);

    return UNIT;
}

u0 *lookup(u0 *self, const u0 *key)
{
    GenericMap *map = self;
    u64 hash = map->hasher(key, map->key_size);
    usize index = bucket_index(map, hash);

    u0 *head = (umin *)PTR(map->buckets) + index * map->node_size;
    // search hashnode-chain.
    until (head == nil || node_hash(map, head) == 0) {
        if (key_eq(map, node_key(map, head), key))
            return node_value(map, head);
        head = *(u0 **)node_next(map, head);
    }

    return nil;
}

bool drop(u0 *self, const u0 *key)
{
    GenericMap *map = self;
    u64 hash = map->hasher(key, map->key_size);
    usize index = bucket_index(map, hash);

    u0 *head = (umin *)PTR(map->buckets) + index * map->node_size;
    u0 *node = head;
    u0 *last = nil;
    // search hasnode-chain.
    until (node == nil || node_hash(map, node) == 0) {
        if (key_eq(map, node_key(map, node), key))
            break;
        last = node;
        node = *(u0 **)node_next(map, node);
    }

    if (node == nil || node_hash(map, node) == 0)
        // i.e. loop did not break, and
        // so the key does not exist.
        return false;

    if (node == head) {  // delete from bucket array directly.
        assert(last == nil);
        u0 *next = *(u0 **)node_next(map, node);
        if (next == nil) {
            zero(node, map->node_size);
            --map->buckets.len;
        } else {
            // otherwise, we need to copy the node into the bucket array,
            // overwriting the node that we are deleting.
            memcpy(node, next, map->node_size);
            FREE(next);  //< it is now in the base of the bucket array.
        }
    } else {
        // otherwise, re-order the pointers and free the node.
        assert(last != nil);
        u0 **last_next_field = node_next(map, last);
        u0 **node_next_field = node_next(map, node);
        *last_next_field = *node_next_field;
        FREE(node);
    }

    --map->len;
    return true;
}

GenericSlice get_keys(u0 *self)
{
    GenericMap *map = self;
    GenericSlice ks = {
        .len = map->len,
        .value = emalloc(map->len, map->key_size)
    };

    usize j = 0;
    for (usize i = 0; i < map->buckets.cap; ++i) {
        u0 *node = (umin *)PTR(map->buckets) + i * map->node_size;
        if (node_hash(map, node) == 0) continue;

        until (node == nil) {
            set(&ks, j++, node_key(map, node), map->key_size);
            node = *(u0 **)node_next(map, node);
        }
    }
    assert(j == map->len);
    return ks;
}

bool has_key(u0 *self, u0 *key)
{
    GenericMap *map = self;
    for (usize i = 0; i < map->buckets.cap; ++i) {
        u0 *node = (umin *)PTR(map->buckets) + i * map->node_size;
        if (node_hash(map, node) == 0) continue;

        until (node == nil) {
            if (key_eq(map, node_key(map, node), key))
                return true;
            node = *(u0 **)node_next(map, node);
        }
    }
    return false;
}

u0 empty_map(u0 *self)
{
    GenericMap *map = self;
    for (usize i = 0; i < map->buckets.cap; ++i) {
        u0 *node = (umin *)PTR(map->buckets) + i * map->node_size;
        if (node_hash(map, node) == 0) continue;

        node = *(u0 **)node_next(map, node);
        until (node == nil) {
            u0 *next = *(u0 **)node_next(map, node);
            FREE(node);
            node = next;
        }
    }
    zero(PTR(map->buckets), map->node_size * map->buckets.cap);
    return UNIT;
}

bool is_empty_map(u0 *self)
{
    GenericMap *map = self;
    if (PTR(map->buckets) == nil) return true;

    for (usize i = 0; i < map->buckets.cap; ++i) {
        u0 *node = (umin *)PTR(map->buckets) + i * map->node_size;
        if (node_hash(map, node) != 0) return false;
    }

    return true;
}

u0 free_map(u0 *self)
{
    GenericMap *map = self;
    empty_map(map);
    FREE_INSIDE(map->buckets);
    map->buckets.value = nil;
    return UNIT;
}

u0 dump_hashmap(u0 *self, byte *key_fmt, byte *value_fmt)
{
    GenericMap *map = self;
    const usize NODE_SIZE = map->node_size;
    umin *buckets = (u0 *)PTR(map->buckets);
    eprintln("entries:     %zu", map->len);
    eprintln("buckets.cap: %zu", map->buckets.cap);
    eprintln("buckets.len: %zu", map->buckets.len);

    for (usize i = 0; i < map->buckets.cap; ++i) {
        u0 *node = buckets + i * NODE_SIZE;
        u64 bhash = node_hash(map, node);

        eprint("| %02zu |", i);
        if (bhash == 0) {
            eprintln(" -x-");
            continue;
        }

        until (node == nil) {
            struct { umin _[16]; } key = { 0 }, value = { 0 };
            memcpy(&key, node_key(map, node), map->key_size);
            memcpy(&value, node_value(map, node), map->value_size);
            u64 hash = node_hash(map, node);
            string formatter = sprint(" -> [%s (%06llX): %s]",
                                      key_fmt, hash, value_fmt);
            eprint(PTR(formatter), key, value);
            node = *(u0 **)node_next(map, node);
        }
        eprint("\n");
    }
}

#endif

Updated on 23 August 2022 at 00:54:19 UTC


title: src/crelude/common.h


src/crelude/common.h

More...

Classes

Name
struct_atomic_t
Useful for resource counting etc.
structGenericArray
Array with pointer to void.
structMemArray
Array with pointer type to smallest addressable units of memory.
structGenericSlice
Slice with pointer to void.
structMemSlice
Slice with pointer type to smallest addressable units of memory.
structGenericMap
Hash-table that maps void * to void *.
structstring
Immutable wrapper for UTF-8 encoded string (bytes are mutable).
structrunic
Imutable warpper for UCS-4/UTF-32 encoded runic string (runes are mutable).
structStringBuilder
Mutable string which is built/pushed-to over time.
structRunicBuilder
Mutable runic string which is built/pushed-to over time.
structsymbol
Symbols are interned strings.

Types

Name
enumHashKeyType { HKT_STRING = 1 << 0, HKT_RUNIC = 1 << 1, HKT_CSTRING = 1 << 2, HKT_MEM_SLICE = 1 << 3, HKT_RAW_BYTES = 1 << 4, HKT_SMALL_INTEGER = 1 << 5}
typedef enum HashKeyTypeHashKeyType
typedef struct _atomic_tatomic_t
Useful for resource counting etc.
typedef voidu0
typedef signed intierr
typedef unsigned intuerr
typedef unsigned longuword
long is always the same size as a machine word.
typedef signed longiword
typedef unsigned intufast
typedef signed intifast
typedef ptrdiff_tisize
typedef size_tusize
Use for storing array indices or object sizes.
typedef intptr_tiptr
typedef uintptr_tuptr
Large enough to store a pointer, like (void *).
typedef intmax_timax
typedef uintmax_tumax
typedef unsigned charumin
Such that sizeof(umin) == 1.
typedef signed charimin
Such that sizeof(imin) == 1.
typedef __int8_ti8
typedef __uint8_tu8
typedef __uint8_tbyte
Don't use char when you want byte.
typedef __int16_ti16
typedef __uint16_tu16
typedef __int32_ti32
typedef __uint32_tu32
typedef u32rune
typedef __int64_ti64
typedef __uint64_tu64

Functions

Name
_Static_assert(sizeof(umin) ==1 &&sizeof(imin)==1, "umin and [imin](/crelude/Files/common_8h.md#typedef-imin) must have size one (1)." )
u0panic(const byte * , ... )
u0 *or(const u0 * nullable, const u0 * nonnull)
boolis_zero(imax )
boolis_zerof(f64 )
boolis_zeroed(u0 * , usize )
u0zero(u0 * blk, usize width)
u0 *emalloc(usize , usize )
Malloc with zeros, and panics when out of memory.
u0reverse(u0 * self, usize width)
MemSlicereverse_endianness(MemSlice bytes)
boolis_little_endian(void )
u128big_endian(umin * start, usize bytes)
Read big-endian integer.
u0swap(u0 * self, usize pivot, usize width)
u0memswap(umin * a, umin * b, usize bytes)
usizeresize(u0 * self, usize cap, usize width)
usizegrow(u0 * self, usize count, usize width)
u0 *get(u0 * self, usize index, usize width)
u0 *set(u0 * self, usize index, const u0 * elem, usize width)
usizepush(u0 * self, const u0 * element, usize width)
u0 *pop(u0 * self, usize width)
u0 *shift(u0 * self, usize width)
Works like pop but removes from the front.
usizeinsert(u0 * self, usize index, const u0 * element, usize width)
usizeextend(u0 * self, const u0 * slice, usize width)
usizesplice(u0 * self, usize index, const u0 * slice, usize width)
GenericSlicecut(u0 * self, usize from, isize upto, usize width)
usizenull(u0 * self, usize width)
stringfrom_cstring(const byte * )
NUL-terminated string to library string.
boolstring_eq(const string, const string)
Compare two strings for equality.
i16string_cmp(const string, const string)
Compare two strings for alphabetic rank.
i16string_ncmp(const string, const string, usize n)
Compare two strings for alphabetic rank upto a given number of bytes.
u64hash_string(const string)
Hash a string.
u64hash_bytes(const MemSlice)
Hash a byte slice.
u0associate(u0 * self, const u0 * key, const u0 * value)
Map / associate a key with a value, i.e. insert into the hash-map/table.
u0 *lookup(u0 * self, const u0 * key)
Look-up / get value from hash-map/table given the key.
booldrop(u0 * self, const u0 * key)
GenericSliceget_keys(u0 * self)
boolhas_key(u0 * self, u0 * key)
Checks if entry / key-value pair is present in hash-table/map given a key.
u0empty_map(u0 * self)
boolis_empty_map(u0 * self)
Checks if the hash-table / map is empty or freed.
u0free_map(u0 * self)
usizeinit_hashnode(u0 * , const u0 * , u64 , const u0 * , const u0 * )
Internal use 99% of the time.
u0dump_hashmap(u0 * self, byte * key_formatter, byte * value_formatter)
Hashmap debugging function.
attribute((unused) ) const
returnhash_bytes(extension({ __auto_type _self=(runes);MemSlice _bytes={ .len=_self.len sizeof(_self.value),.value=(umin *) _self.value };_bytes;}) )
returnhash_string(from_cstring(*(byte **) key) )

Attributes

Defines

Name
_GNU_SOURCE
Use GNU specific source.
FREE
MALLOC
REALLOC
VA_NUM_ARGS_IMPL(_1, _2, _3, _4, _5, N, ...)
VA_NUM_ARGS(...)
_TSTR(x)
TSTR(x)
TODO(...)
crelude_V_MAJOR
crelude_V_MINOR
crelude_V_PATCH
crelude_VERSION
ARRAY_REALLOC_FACTOR
loop
whilst
unless(cond)
never
always
until(cond)
unqualify(D, T)
record(NAME)
enumerable(NAME)
overlap(NAME)
newtype(NT, T)
arrayof(T)
newarray(NT, T)
sliceof(T)
newslice(NT, T)
hashof(T)
newhashable(NT, T)
HASHMAP_LOAD_THRESHOLD
HASHMAP_GROWTH_FACTOR
newmap(NT, K, V)
mapof(K, V)
hashnode(K, V)
MMAKE(K, V, CAP)
MNEW(VARIABLE, CAP)
Create new map / initialise map from map variable.
NTH(LIST, N)
GET(LIST, N)
SET(LIST, N, V)
UNUSED1(z)
UNUSED2(y, z)
UNUSED3(x, y, z)
UNUSED4(b, x, y, z)
UNUSED5(a, b, x, y, z)
UNUSED_IMPL_(nargs)
UNUSED_IMPL(nargs)
UNUSED(...)
NO_ERROR
OK
OKAY
FAIL
NOOP
nil
FLOOR(T, N)
CEIL(T, N)
UNIT
WORD_SIZE
Size of a machine word in bytes.
UCHAR8
UCHAR8
_FLOAT_BIT
_DOUBLE_BIT
_LDOUBLE_BIT
COPY(SELF)
TO_BYTES(SELF)
Convert array/slice to slice of bytes.
FROM_BYTES(T, SELF)
Convert from a byte array/slice into an array/slice of another type.
REVERSE(SELF)
In-place reverse.
SWAP(SELF, PIVOT)
PUSH(SELF, ELEM)
POP(SELF)
SHIFT(SELF)
INSERT(SELF, INDEX, ELEM)
EXTEND(SELF, SLIC)
SPLICE(SELF, INDEX, SLIC)
CUT(SELF, FROM, UPTO)
REMOVE(SELF, INDEX)
ASSOCIATE(SELF, KEY, VAL)
LOOKUP(SELF, KEY)
DROP(SELF, KEY)
KEYS(MAP)
HAS_KEY(MAP, KEY)
APPEND(SELF, ELEM)
PREPEND(SELF, ELEM)
UNSHIFT(SELF, ELEM)
PREFIX(SELF, SLIC)
HEAD(SELF, END)
TAIL(SELF, BEG)
FIRST(SELF)
LAST(SELF)
QSORT(SELF, CMPR)
Minimal helper/wrapper around in-place qsort for arrays/slices.
SORT(SELF, SUBJ, OTHR, ...)
CMP(A, B)
ANSI(CODE)
BOLD
FAINT
DIM
ITALIC
UNDER
BLINK
RAPID
INVERT
HIDDEN
STRIKE
BOLD_OFF
FAINT_OFF
ITALIC_OFF
UNDER_OFF
BLINK_OFF
RAPID_OFF
INVERT_OFF
HIDDEN_OFF
STRIKE_OFF
RESET
min(A, B)
max(A, B)
deref(T, PTR, ALT)
WRAP(TYPE, VALUE)
Wrap value in wrapper struct.
UNWRAP(STRUCTURE)
Unwraps pointer/value in sizing wrapper struct.
PTR(ARR)
Explicitly only extract pointer from array/slice.
FREE_INSIDE(S)
Call to free of inside of slice/array/newtype, etc.
INIT(TYPE, ...)
Initialise sizing wrapper with literal.
LIST(TYPE, ...)
Can be used to make slices from literal arrays.
STRING(...)
Initialise sizing wrapper with of string literal.
STR(...)
SEMPTY(TYPE)
Empty slice of certain type.
AEMPTY(TYPE)
Empty array of certain type.
EMPTY(TYPE)
Empty / zero struct.
IS_EMPTY(ARR)
Is array empty?
AMAKE(TYPE, CAP)
Heap allocates a variable sized array.
ANEW(VARIABLE, CAP)
Create new array / initialise array from array variable.
SMAKE(TYPE, LEN)
Heap allocates a constant sized slice type.
SNEW(VARIABLE, LEN)
Create new slice / initialise slice from slice variable.
SLICE(TYPE, OBJ, START, END)
Take a slice/substring/view of sized type.
VIEW(TYPE, PTR, START, END)
Works like SLICE, but on a pointer instead of an array.
SYMBOLIC(STR)
SYMBOL_LITERAL(STR_LIT)
ACOLLECT(T, count, pointer)
C array to dynamic array wrapper.
SCOLLECT(T, count, pointer)
C array to slice wrapper.
SMAP(T, func, list)
AMAP(T, func, list)
FOR_EACH(ELEM, ELEMS)
foreach

Detailed Description

Note: Read this: unicode by Jeff Bezanson, about modern unicode in C.

Defines basic macros and datatypes which are in common through-out the whole project.

Types Documentation

enum HashKeyType

EnumeratorValueDescription
HKT_STRING1 << 0
HKT_RUNIC1 << 1
HKT_CSTRING1 << 2
HKT_MEM_SLICE1 << 3
HKT_RAW_BYTES1 << 4
HKT_SMALL_INTEGER1 << 5

typedef HashKeyType

typedef enum HashKeyType HashKeyType;

typedef atomic_t

typedef struct _atomic_t atomic_t;

Useful for resource counting etc.

typedef u0

typedef void u0;

The type that occupies no space. Thanks to Terry for this one.

typedef ierr

typedef signed int ierr;

Explicitly mark functions that return error-codes as returning ierr instead of just int.

typedef uerr

typedef unsigned int uerr;

Not something very common.

typedef uword

typedef unsigned long uword;

long is always the same size as a machine word.

Unsigned machine word integer.

typedef iword

typedef signed long iword;

Signed machine word integer.

typedef ufast

typedef unsigned int ufast;

int in most cases is going to have the natural size suggested by the target architecture, optimal for most things.

typedef ifast

typedef signed int ifast;

typedef isize

typedef ptrdiff_t isize;

typedef usize

typedef size_t usize;

Use for storing array indices or object sizes.

typedef iptr

typedef intptr_t iptr;

typedef uptr

typedef uintptr_t uptr;

Large enough to store a pointer, like (void *).

typedef imax

typedef intmax_t imax;

typedef umax

typedef uintmax_t umax;

typedef umin

typedef unsigned char umin;

Such that sizeof(umin) == 1.

typedef imin

typedef signed char imin;

Such that sizeof(imin) == 1.

typedef i8

typedef __int8_t i8;

typedef u8

typedef __uint8_t u8;

typedef byte

typedef __uint8_t byte;

Don't use char when you want byte.

typedef i16

typedef __int16_t i16;

typedef u16

typedef __uint16_t u16;

typedef i32

typedef __int32_t i32;

typedef u32

typedef __uint32_t u32;

typedef rune

typedef u32 rune;

Unicode codepoint (USC-4) (32 bits), don't use char[4], and definitely do not use wchar_t.

typedef i64

typedef __int64_t i64;

typedef u64

typedef __uint64_t u64;

Functions Documentation

function _Static_assert

_Static_assert(
    sizeof(umin)  ==1 &&sizeof(imin)==1,
    "`umin` and `imin` must have size one (1)." 
)

function panic

u0 panic(
    const byte * ,
    ... 
)

function or

u0 * or(
    const u0 * nullable,
    const u0 * nonnull
)

function is_zero

bool is_zero(
    imax 
)

function is_zerof

bool is_zerof(
    f64 
)

function is_zeroed

bool is_zeroed(
    u0 * ,
    usize 
)

function zero

u0 zero(
    u0 * blk,
    usize width
)

Parameters:

  • blk Pointer to start of block.
  • width How many bytes to zero. e.g., for an array width = lenght * sizeof(elem).

Zero a block of memory.

function emalloc

u0 * emalloc(
    usize ,
    usize 
)

Malloc with zeros, and panics when out of memory.

function reverse

u0 reverse(
    u0 * self,
    usize width
)

Parameters:

  • self A pointer to an array or slice, cast to u0 *.
  • width The width/sizeof of an element in the array.

Reverse an array or slice in-place.

function reverse_endianness

MemSlice reverse_endianness(
    MemSlice bytes
)

Note: Heap allocates, remember to free.

Reverse a slice of bytes. Not in-place.

function is_little_endian

bool is_little_endian(
    void 
)

Return: true if little endian, false if big endian.

Check if system/CPU is using little endian.

function big_endian

u128 big_endian(
    umin * start,
    usize bytes
)

Read big-endian integer.

function swap

u0 swap(
    u0 * self,
    usize pivot,
    usize width
)

Parameters:

  • self Pointer to the slice, cast to (u0 *).
  • pivot The index of the slice that divides the blocks to swap.
  • width The sizeof(T) where T is the type of element in the slice.

Given a slice, swap the two blocks within the slice formed by selecting a pivot point (in-place).

[----A----|---B---] -> [---B---|----A----]
          ^ pivot

function memswap

u0 memswap(
    umin * a,
    umin * b,
    usize bytes
)

Parameters:

  • a Block of memory a, to be swapped for b.
  • b Block of memory b, to be swapped for a.
  • bytes The common size of block a and b in bytes.

Swaps two equally sized blocks of memory, overwriting each other.

function resize

usize resize(
    u0 * self,
    usize cap,
    usize width
)

Return: How much of the array is empty (i.e. cap - len).

Resizes the array, i.e. changes the capacity to a given value. Akin to realloc.

function grow

usize grow(
    u0 * self,
    usize count,
    usize width
)

Parameters:

  • self Void-pointer to array structure.
  • count Number of spaces to grow by.
  • width Size of the individual elements in the array, in bytes.

Return: How much capacity increased.

Grows the array length by count, reallocates if necessary. Does nothing else.

function get

u0 * get(
    u0 * self,
    usize index,
    usize width
)

Parameters:

  • self Pointer to slice or array.
  • index Index of element you wish to retrieve.
  • width Size of single array element in bytes.

Return: Pointer to element, or NULL if not present / out of bounds.

Get pointer to element at index in slice/array.

function set

u0 * set(
    u0 * self,
    usize index,
    const u0 * elem,
    usize width
)

Parameters:

  • self Pointer to slice or array.
  • index Index of element you wish to set.
  • width Size of single array element in bytes.

Return: Pointer to element just set, or NULL if out of bounds.

Set element at index in slice/array.

function push

usize push(
    u0 * self,
    const u0 * element,
    usize width
)

Parameters:

  • self Pointer to the dynamic array, cast to (u0 *).
  • element Pointer to element to be pushed, cast to (u0 *).
  • width The sizeof(T) where T is the type of the element that is being pushed.

Return: How much capacity increased.

Push element to array.

function pop

u0 * pop(
    u0 * self,
    usize width
)

Return: Pointer to popped element.

Pops/removes element from top of the stack (dynamic array).

function shift

u0 * shift(
    u0 * self,
    usize width
)

Works like pop but removes from the front.

function insert

usize insert(
    u0 * self,
    usize index,
    const u0 * element,
    usize width
)

Return: How much capacity increased.

Exactly like push, except position of element is arbitrary, with index specified in second argument.

function extend

usize extend(
    u0 * self,
    const u0 * slice,
    usize width
)

Parameters:

  • self A pointer to a dynamic array, of any type.
  • slice A pointer to a slice, and a slice only (not an array, dynamic array, etc.).
  • width The sizeof(T) where T is the type of the individual elements that are being appended to the array.

Return: How much capacity increased.

Works like push, but extends the array by multiple elements.

function splice

usize splice(
    u0 * self,
    usize index,
    const u0 * slice,
    usize width
)

Parameters:

  • self A pointer to the dynamic array, of any type.
  • index The location for inserting in the slice.
  • slice The slice you wish to insert at index.
  • width The sizeof(T) where T is the type of the individual elements stored within the array and slice.

Return: How much capacity increased.

Works like extend, but extends or splices the array with a slice at some given, arbitrary position.

function cut

GenericSlice cut(
    u0 * self,
    usize from,
    isize upto,
    usize width
)

Parameters:

  • self A pointer to the array.
  • from Index to start removing from.
  • upto Index of final element to remove in the range. If parameter is negative, it indicates an index from the end of the array.

Return: A slice holding a void-pointer to the removed elements,

Deletes a range of elements from an array, starting from some index.

function null

usize null(
    u0 * self,
    usize width
)

Return: Number of bytes nulled.

Zero out an array.

function from_cstring

string from_cstring(
    const byte * 
)

NUL-terminated string to library string.

function string_eq

bool string_eq(
    const string,
    const string
)

Compare two strings for equality.

function string_cmp

i16 string_cmp(
    const string,
    const string
)

Compare two strings for alphabetic rank.

function string_ncmp

i16 string_ncmp(
    const string,
    const string,
    usize n
)

Compare two strings for alphabetic rank upto a given number of bytes.

function hash_string

u64 hash_string(
    const string
)

Hash a string.

function hash_bytes

u64 hash_bytes(
    const MemSlice
)

Hash a byte slice.

function associate

u0 associate(
    u0 * self,
    const u0 * key,
    const u0 * value
)

Map / associate a key with a value, i.e. insert into the hash-map/table.

function lookup

u0 * lookup(
    u0 * self,
    const u0 * key
)

Look-up / get value from hash-map/table given the key.

function drop

bool drop(
    u0 * self,
    const u0 * key
)

Return: true if key was present, and node was deleted, false if not.

Drop / delete / remove key-value pair from the hash-table. This frees/deallocates the node, and the key and value are deleted. Does nothing if key did not exist in the table.

function get_keys

GenericSlice get_keys(
    u0 * self
)

Note: Returns a heap allocated slice, remember to free.

Get slice of key pointers of all keys in map/hash-table.

function has_key

bool has_key(
    u0 * self,
    u0 * key
)

Checks if entry / key-value pair is present in hash-table/map given a key.

function empty_map

u0 empty_map(
    u0 * self
)

Empties out / deallocates all key-value pairs from the map. Map may still be repopulated again after this.

function is_empty_map

bool is_empty_map(
    u0 * self
)

Checks if the hash-table / map is empty or freed.

function free_map

u0 free_map(
    u0 * self
)

Frees the map. Not only empties it, but deallocates bucket array such that the map may not be used again.

function init_hashnode

usize init_hashnode(
    u0 * ,
    const u0 * ,
    u64 ,
    const u0 * ,
    const u0 * 
)

Internal use 99% of the time.

function dump_hashmap

u0 dump_hashmap(
    u0 * self,
    byte * key_formatter,
    byte * value_formatter
)

Hashmap debugging function.

function attribute

__attribute__(
    (unused) 
) const

function hash_bytes

return hash_bytes(
    __extension__({ __auto_type _self=(runes);MemSlice _bytes={ .len=_self.len *sizeof(*_self.value),.value=(umin *) _self.value };_bytes;}) 
)

function hash_string

return hash_string(
    from_cstring(*(byte **) key) 
)

Attributes Documentation

variable hash

u64 hash;

variable value

u0 * value;

variable key

struct @21 key;

variable next

u0 * next;

variable NUL

static const ierr NUL = 0;

variable NUL_BYTE

static const byte NUL_BYTE = '\0';

variable NUL_STRING

static const string NUL_STRING = { .[len](/crelude/Files/base64_8h.md#variable-len) = 0, .[value](/crelude/Files/common_8h.md#variable-value) = ([byte](/crelude/Files/common_8h.md#typedef-byte) *)&[NUL_BYTE](/crelude/Files/common_8h.md#variable-nul_byte) };

variable ZERO

static const iword ZERO = 0;

variable _

usize _ {
	[UNUSED1](/crelude/Files/common_8h.md#define-unused1);

variable runes

runic runes = *([runic](/crelude/Classes/structrunic.md) *)[key](/crelude/Files/common_8h.md#variable-key);

variable size

usize size {
	[u64](/crelude/Files/common_8h.md#typedef-u64)[hash](/crelude/Files/common_8h.md#variable-hash) = 0;

Macros Documentation

define _GNU_SOURCE

#define _GNU_SOURCE 1

Use GNU specific source.

define FREE

#define FREE free

define MALLOC

#define MALLOC malloc

define REALLOC

#define REALLOC realloc

define VA_NUM_ARGS_IMPL

#define VA_NUM_ARGS_IMPL(
    _1,
    _2,
    _3,
    _4,
    _5,
    N,
    ...
)
N

define VA_NUM_ARGS

#define VA_NUM_ARGS(
    ...
)
[VA_NUM_ARGS_IMPL](/crelude/Files/common_8h.md#define-va_num_args_impl)(__VA_ARGS__, 5, 4, 3, 2, 1)

define _TSTR

#define _TSTR(
    x
)
#x

define TSTR

#define TSTR(
    x
)
[_TSTR](/crelude/Files/common_8h.md#define-_tstr)(x)

define TODO

#define TODO(
    ...
)
                            DO_PRAGMA(message("TODO: " #__VA_ARGS__ \
                            " (" __FILE__ ":" [TSTR](/crelude/Files/common_8h.md#define-tstr)(__LINE__)")"))

define crelude_V_MAJOR

#define crelude_V_MAJOR 0

define crelude_V_MINOR

#define crelude_V_MINOR 1

define crelude_V_PATCH

#define crelude_V_PATCH 0

define crelude_VERSION

#define crelude_VERSION 	"v" TSTR([crelude_V_MAJOR](/crelude/Files/common_8h.md#define-crelude_v_major)) \
	"." [TSTR](/crelude/Files/common_8h.md#define-tstr)([crelude_V_MINOR](/crelude/Files/common_8h.md#define-crelude_v_minor)) \
	"." [TSTR](/crelude/Files/common_8h.md#define-tstr)([crelude_V_PATCH](/crelude/Files/common_8h.md#define-crelude_v_patch))

define ARRAY_REALLOC_FACTOR

#define ARRAY_REALLOC_FACTOR 1.5

define loop

#define loop while (1)

define whilst

#define whilst while

define unless

#define unless(
    cond
)
if (!(cond))

define never

#define never if (0)

define always

#define always if (1)

define until

#define until(
    cond
)
while (!(cond))

define unqualify

#define unqualify(
    D,
    T
)
typedef D T T

define record

#define record(
    NAME
)
typedef struct [_](/crelude/Files/common_8h.md#variable-_)##NAME NAME; struct [_](/crelude/Files/common_8h.md#variable-_)##NAME

define enumerable

#define enumerable(
    NAME
)
typedef   enum [_](/crelude/Files/common_8h.md#variable-_)##NAME NAME;   enum [_](/crelude/Files/common_8h.md#variable-_)##NAME

define overlap

#define overlap(
    NAME
)
typedef  union [_](/crelude/Files/common_8h.md#variable-_)##NAME NAME;  union [_](/crelude/Files/common_8h.md#variable-_)##NAME

define newtype

#define newtype(
    NT,
    T
)
typedef struct [_](/crelude/Files/common_8h.md#variable-_)##NT { T [value](/crelude/Files/common_8h.md#variable-value); } NT

define arrayof

#define arrayof(
    T
)
	struct { \
	T (*[value](/crelude/Files/common_8h.md#variable-value)); \
	[usize](/crelude/Files/common_8h.md#typedef-usize)[len](/crelude/Files/base64_8h.md#variable-len);  \
	[usize](/crelude/Files/common_8h.md#typedef-usize) cap;  \
}

define newarray

#define newarray(
    NT,
    T
)
typedef [arrayof](/crelude/Files/common_8h.md#define-arrayof)(T) NT

define sliceof

#define sliceof(
    T
)
	struct { \
	T (*[value](/crelude/Files/common_8h.md#variable-value)); \
	[usize](/crelude/Files/common_8h.md#typedef-usize)[len](/crelude/Files/base64_8h.md#variable-len);  \
}

define newslice

#define newslice(
    NT,
    T
)
typedef [sliceof](/crelude/Files/common_8h.md#define-sliceof)(T) NT

define hashof

#define hashof(
    T
)
	struct { \
	[u64](/crelude/Files/common_8h.md#typedef-u64)[hash](/crelude/Files/common_8h.md#variable-hash); \
	T [value](/crelude/Files/common_8h.md#variable-value);  \
}

define newhashable

#define newhashable(
    NT,
    T
)
typedef [hashof](/crelude/Files/common_8h.md#define-hashof)(T) NT

define HASHMAP_LOAD_THRESHOLD

#define HASHMAP_LOAD_THRESHOLD 0.85

define HASHMAP_GROWTH_FACTOR

#define HASHMAP_GROWTH_FACTOR 2

define newmap

#define newmap(
    NT,
    K,
    V
)
typedef [mapof](/crelude/Files/common_8h.md#define-mapof)(K, V) NT

define mapof

#define mapof(
    K,
    V
)
	struct { \
	[usize](/crelude/Files/common_8h.md#typedef-usize)[len](/crelude/Files/base64_8h.md#variable-len); \
	[arrayof](/crelude/Files/common_8h.md#define-arrayof)([hashnode](/crelude/Files/common_8h.md#define-hashnode)(K, V)) buckets; /* a [hash](/crelude/Files/common_8h.md#variable-hash)[value](/crelude/Files/common_8h.md#variable-value) of [zero](/crelude/Files/common_8h.md#function-zero) indicates absence. */\
	[usize](/crelude/Files/common_8h.md#typedef-usize) value_size; \
	[usize](/crelude/Files/common_8h.md#typedef-usize)   key_size; \
	[usize](/crelude/Files/common_8h.md#typedef-usize)  node_size; \
	/* offsets of `[hashnode](/crelude/Files/common_8h.md#define-hashnode)` struct. */ \
	[usize](/crelude/Files/common_8h.md#typedef-usize)  hash_offset; \
	[usize](/crelude/Files/common_8h.md#typedef-usize)   key_offset; \
	[usize](/crelude/Files/common_8h.md#typedef-usize) value_offset; \
	[usize](/crelude/Files/common_8h.md#typedef-usize)  next_offset; \
	[HashKeyType](/crelude/Files/common_8h.md#enum-hashkeytype) key_type; /* < how should the [hash](/crelude/Files/common_8h.md#variable-hash)-function [hash](/crelude/Files/common_8h.md#variable-hash) the key. */ \
	[u64](/crelude/Files/common_8h.md#typedef-u64) (*hasher)(const [u0](/crelude/Files/common_8h.md#typedef-u0) *, [usize](/crelude/Files/common_8h.md#typedef-usize)); \
}

define hashnode

#define hashnode(
    K,
    V
)
	struct { \
	[hashof](/crelude/Files/common_8h.md#define-hashof)(K) [key](/crelude/Files/common_8h.md#variable-key); \
	V [value](/crelude/Files/common_8h.md#variable-value);  /* < [value](/crelude/Files/common_8h.md#variable-value) stored.   */ \
	[u0](/crelude/Files/common_8h.md#typedef-u0) *[next](/crelude/Files/common_8h.md#variable-next); /* < [next](/crelude/Files/common_8h.md#variable-next)[hash](/crelude/Files/common_8h.md#variable-hash)-node. */ \
}

define MMAKE

#define MMAKE(
    K,
    V,
    CAP
)

define MNEW

#define MNEW(
    VARIABLE,
    CAP
)
	(typeof(VARIABLE))[MMAKE](/crelude/Files/common_8h.md#define-mmake)( \
	typeof((VARIABLE).[buckets.value](/crelude/Files/common_8h.md#variable-value)[0].[key.value](/crelude/Files/common_8h.md#variable-value)), \
	typeof((VARIABLE).[buckets.value](/crelude/Files/common_8h.md#variable-value)[0].[value](/crelude/Files/common_8h.md#variable-value)), \
	CAP)

Create new map / initialise map from map variable.

define NTH

#define NTH(
    LIST,
    N
)
[UNWRAP](/crelude/Files/common_8h.md#define-unwrap)(([LIST](/crelude/Files/common_8h.md#define-list)))[(N)]

define GET

#define GET(
    LIST,
    N
)
	__extension__\
	({ __auto_type _list = ([LIST](/crelude/Files/common_8h.md#define-list)); \
	   __auto_type _n = (N); \
	   [usize](/crelude/Files/common_8h.md#typedef-usize) _index = _n < 0 ? ([usize](/crelude/Files/common_8h.md#typedef-usize))([_list.len](/crelude/Files/base64_8h.md#variable-len) + _n) : ([usize](/crelude/Files/common_8h.md#typedef-usize))_n; \
	   [UNWRAP](/crelude/Files/common_8h.md#define-unwrap)(_list)[_index]; })

define SET

#define SET(
    LIST,
    N,
    V
)
	__extension__\
	({ __auto_type _list = ([LIST](/crelude/Files/common_8h.md#define-list)); \
	   __auto_type _n = (N); \
	   [usize](/crelude/Files/common_8h.md#typedef-usize) _index = _n < 0 ? ([usize](/crelude/Files/common_8h.md#typedef-usize))([_list.len](/crelude/Files/base64_8h.md#variable-len) + _n) : ([usize](/crelude/Files/common_8h.md#typedef-usize))_n; \
	   [UNWRAP](/crelude/Files/common_8h.md#define-unwrap)(_list)[_index] = (V); })

define UNUSED1

#define UNUSED1(
    z
)
(void)(z)

define UNUSED2

#define UNUSED2(
    y,
    z
)
[UNUSED1](/crelude/Files/common_8h.md#define-unused1)(y),[UNUSED1](/crelude/Files/common_8h.md#define-unused1)(z)

define UNUSED3

#define UNUSED3(
    x,
    y,
    z
)
[UNUSED1](/crelude/Files/common_8h.md#define-unused1)(x),[UNUSED2](/crelude/Files/common_8h.md#define-unused2)(y,z)

define UNUSED4

#define UNUSED4(
    b,
    x,
    y,
    z
)
[UNUSED2](/crelude/Files/common_8h.md#define-unused2)(b,x),[UNUSED2](/crelude/Files/common_8h.md#define-unused2)(y,z)

define UNUSED5

#define UNUSED5(
    a,
    b,
    x,
    y,
    z
)
[UNUSED2](/crelude/Files/common_8h.md#define-unused2)(a,b),[UNUSED3](/crelude/Files/common_8h.md#define-unused3)(x,y,z)

define UNUSED_IMPL_

#define UNUSED_IMPL_(
    nargs
)
[UNUSED](/crelude/Files/common_8h.md#define-unused) ## nargs

define UNUSED_IMPL

#define UNUSED_IMPL(
    nargs
)
[UNUSED_IMPL_](/crelude/Files/common_8h.md#define-unused_impl_)(nargs)

define UNUSED

#define UNUSED(
    ...
)
[UNUSED_IMPL](/crelude/Files/common_8h.md#define-unused_impl)([VA_NUM_ARGS](/crelude/Files/common_8h.md#define-va_num_args)(__VA_ARGS__))(__VA_ARGS__)

define NO_ERROR

#define NO_ERROR EXIT_SUCCESS

define OK

#define OK EXIT_SUCCESS

define OKAY

#define OKAY EXIT_SUCCESS

define FAIL

#define FAIL EXIT_FAILURE

define NOOP

#define NOOP ((void)0)

define nil

#define nil ((void *)NULL)

define FLOOR

#define FLOOR(
    T,
    N
)
	__extension__\
	({ typeof(N) _n = (N); \
	   (T)_n - (_n < 0 ? 1 : 0); })

define CEIL

#define CEIL(
    T,
    N
)
	__extension__\
	({ typeof(N) _n = (N); \
	   (T)_n + (_n < 0 ? 0 : 1); })

define UNIT

#define UNIT ;

define WORD_SIZE

#define WORD_SIZE sizeof(long)

Size of a machine word in bytes.

define UCHAR8

#define __UCHAR8__ char

define UCHAR8

#define __UCHAR8__ char

define _FLOAT_BIT

#define _FLOAT_BIT (__SIZEOF_FLOAT__       * CHAR_BIT)

define _DOUBLE_BIT

#define _DOUBLE_BIT (__SIZEOF_DOUBLE__      * CHAR_BIT)

define _LDOUBLE_BIT

#define _LDOUBLE_BIT (__SIZEOF_LONG_DOUBLE__ * CHAR_BIT)

define COPY

#define COPY(
    SELF
)
	__extension__\
	({ __auto_type _self = (SELF); \
	   __auto_type _copy = _self; \
	   [PTR](/crelude/Files/common_8h.md#define-ptr)(_copy) = [emalloc](/crelude/Files/common_8h.md#function-emalloc)([_self.len](/crelude/Files/base64_8h.md#variable-len), sizeof(*[PTR](/crelude/Files/common_8h.md#define-ptr)(_self))); \
	   memcpy([PTR](/crelude/Files/common_8h.md#define-ptr)(_copy), [PTR](/crelude/Files/common_8h.md#define-ptr)(_self), sizeof(*[PTR](/crelude/Files/common_8h.md#define-ptr)(_copy)) * [_copy.len](/crelude/Files/base64_8h.md#variable-len)); \
	   _copy; })

Note: Allocates on the heap.

Copy an array or slice.

define TO_BYTES

#define TO_BYTES(
    SELF
)
	__extension__\
	({ __auto_type _self = (SELF); \
	   [MemSlice](/crelude/Classes/structMemSlice.md) _bytes = { \
		  .[len](/crelude/Files/base64_8h.md#variable-len) = [_self.len](/crelude/Files/base64_8h.md#variable-len) * sizeof(*[_self.value](/crelude/Files/common_8h.md#variable-value)), \
		  .[value](/crelude/Files/common_8h.md#variable-value) = ([umin](/crelude/Files/common_8h.md#typedef-umin) *)[_self.value](/crelude/Files/common_8h.md#variable-value) \
	   }; _bytes; })

Convert array/slice to slice of bytes.

define FROM_BYTES

#define FROM_BYTES(
    T,
    SELF
)
	__extension__\
	({ __auto_type _self = (SELF); \
	   T _normal = { \
		  .[len](/crelude/Files/base64_8h.md#variable-len) = [_self.len](/crelude/Files/base64_8h.md#variable-len) / sizeof(*[_self.value](/crelude/Files/common_8h.md#variable-value)), \
		  .[value](/crelude/Files/common_8h.md#variable-value) = ([u0](/crelude/Files/common_8h.md#typedef-u0) *)[_self.value](/crelude/Files/common_8h.md#variable-value) \
	   }; _normal; })

Convert from a byte array/slice into an array/slice of another type.

define REVERSE

#define REVERSE(
    SELF
)
	__extension__\
	({ __auto_type _self = &(SELF); \
	   [reverse](/crelude/Files/common_8h.md#function-reverse)(_self, sizeof(*_self->[value](/crelude/Files/common_8h.md#variable-value))); \
	   *_self; })

In-place reverse.

define SWAP

#define SWAP(
    SELF,
    PIVOT
)
	__extension__\
	({ __auto_type _self = &(SELF); \
	   [swap](/crelude/Files/common_8h.md#function-swap)(_self, (PIVOT), sizeof(*_self->[value](/crelude/Files/common_8h.md#variable-value))); })

define PUSH

#define PUSH(
    SELF,
    ELEM
)
	__extension__\
	({ __auto_type           _self = &(SELF); \
	   typeof(*_self->[value](/crelude/Files/common_8h.md#variable-value)) _elem =  (ELEM); \
	   [push](/crelude/Files/common_8h.md#function-push)(_self, &_elem, sizeof(_elem)); })

define POP

#define POP(
    SELF
)
	__extension__\
	({ __auto_type _self = &(SELF); \
	   (typeof(_self->[value](/crelude/Files/common_8h.md#variable-value)))[pop](/crelude/Files/common_8h.md#function-pop)(_self, sizeof(*_self->[value](/crelude/Files/common_8h.md#variable-value))); })

define SHIFT

#define SHIFT(
    SELF
)
	__extension__\
	({ __auto_type _self = &(SELF); \
	   (typeof(_self->[value](/crelude/Files/common_8h.md#variable-value)))[shift](/crelude/Files/common_8h.md#function-shift)(_self, sizeof(*_self->[value](/crelude/Files/common_8h.md#variable-value))); })

define INSERT

#define INSERT(
    SELF,
    INDEX,
    ELEM
)
	__extension__\
	({ __auto_type           _self = &(SELF); \
	   typeof(*_self->[value](/crelude/Files/common_8h.md#variable-value)) _elem =  (ELEM); \
	   [insert](/crelude/Files/common_8h.md#function-insert)(_self, (INDEX), &_elem, sizeof(_elem)); })

define EXTEND

#define EXTEND(
    SELF,
    SLIC
)
	__extension__\
	({ __auto_type _self = &(SELF); \
	   __auto_type _slic =  (SLIC); \
	   [extend](/crelude/Files/common_8h.md#function-extend)(_self, &_slic, sizeof(*[_slic.value](/crelude/Files/common_8h.md#variable-value))); })

define SPLICE

#define SPLICE(
    SELF,
    INDEX,
    SLIC
)
	__extension__\
	({ __auto_type _self = &(SELF); \
	   __auto_type _slic =  (SLIC); \
	   [splice](/crelude/Files/common_8h.md#function-splice)(_self, (INDEX), &_slic, sizeof(*[_slic.value](/crelude/Files/common_8h.md#variable-value))); })

define CUT

#define CUT(
    SELF,
    FROM,
    UPTO
)
	__extension__\
	({ __auto_type _self = &(SELF); \
	   static [GenericSlice](/crelude/Classes/structGenericSlice.md) _cut; \
	   _cut = [cut](/crelude/Files/common_8h.md#function-cut)(_self, (FROM), (UPTO), sizeof(*_self->[value](/crelude/Files/common_8h.md#variable-value))); \
	   ([u0](/crelude/Files/common_8h.md#typedef-u0) *)&_cut; })

define REMOVE

#define REMOVE(
    SELF,
    INDEX
)
	__extension__\
	({ __auto_type _self = &(SELF); \
	   [usize](/crelude/Files/common_8h.md#typedef-usize) _indx = (INDEX); \
	   [GenericSlice](/crelude/Classes/structGenericSlice.md) _cut \
	       = [cut](/crelude/Files/common_8h.md#function-cut)(_self, _indx, _indx, sizeof(*_self->[value](/crelude/Files/common_8h.md#variable-value))); \
	   (typeof(_self->[value](/crelude/Files/common_8h.md#variable-value)))[PTR](/crelude/Files/common_8h.md#define-ptr)(_cut); })

define ASSOCIATE

#define ASSOCIATE(
    SELF,
    KEY,
    VAL
)
	__extension__\
	({ __auto_type _self = &(SELF); \
	   __auto_type _key = (KEY); \
	   __auto_type _val = (VAL); \
	   [associate](/crelude/Files/common_8h.md#function-associate)(_self, &_key, &_val); })

define LOOKUP

#define LOOKUP(
    SELF,
    KEY
)
	__extension__\
	({ __auto_type _self = &(SELF); \
	   __auto_type _key = (KEY); \
	   (typeof(_self->[buckets.value](/crelude/Files/common_8h.md#variable-value)[0].[value](/crelude/Files/common_8h.md#variable-value)) *)[lookup](/crelude/Files/common_8h.md#function-lookup)(_self, &_key); })

define DROP

#define DROP(
    SELF,
    KEY
)
	__extension__\
	({ __auto_type _self = &(SELF); \
	   __auto_type _key = (KEY); \
	   [drop](/crelude/Files/common_8h.md#function-drop)(_self, &_key); })

define KEYS

#define KEYS(
    MAP
)
	__extension__\
	({ __auto_type _map = &(MAP); \
	   static [GenericSlice](/crelude/Classes/structGenericSlice.md) _keys; \
	   _keys = [get_keys](/crelude/Files/common_8h.md#function-get_keys)(_map); \
	   ([u0](/crelude/Files/common_8h.md#typedef-u0) *)&_keys; })

define HAS_KEY

#define HAS_KEY(
    MAP,
    KEY
)
	__extension__\
	({ __auto_type _map = &(MAP); \
	   __auto_type _key = (KEY); \
	   [has_key](/crelude/Files/common_8h.md#function-has_key)(_map, &_key); })

define APPEND

#define APPEND(
    SELF,
    ELEM
)
[PUSH](/crelude/Files/common_8h.md#define-push)(SELF, ELEM)

define PREPEND

#define PREPEND(
    SELF,
    ELEM
)
[INSERT](/crelude/Files/common_8h.md#define-insert)(SELF, 0, ELEM)

define UNSHIFT

#define UNSHIFT(
    SELF,
    ELEM
)
[PREPEND](/crelude/Files/common_8h.md#define-prepend)(SELF, ELEM)

define PREFIX

#define PREFIX(
    SELF,
    SLIC
)
[SPLICE](/crelude/Files/common_8h.md#define-splice)(SELF, 0, SLIC)

define HEAD

#define HEAD(
    SELF,
    END
)
[SLICE](/crelude/Files/common_8h.md#define-slice)(SELF, 0, END)

define TAIL

#define TAIL(
    SELF,
    BEG
)
[SLICE](/crelude/Files/common_8h.md#define-slice)(SELF, BEG, -1)

define FIRST

#define FIRST(
    SELF
)
[NTH](/crelude/Files/common_8h.md#define-nth)(SELF, 0)

define LAST

#define LAST(
    SELF
)
[GET](/crelude/Files/common_8h.md#define-get)(SELF, -1)

define QSORT

#define QSORT(
    SELF,
    CMPR
)
	__extension__\
	({ __auto_type _self = (SELF); \
	   qsort([PTR](/crelude/Files/common_8h.md#define-ptr)(_self), [_self.len](/crelude/Files/base64_8h.md#variable-len), sizeof(*[_self.value](/crelude/Files/common_8h.md#variable-value)), CMPR); \
	   _self; })

Minimal helper/wrapper around in-place qsort for arrays/slices.

define SORT

#define SORT(
    SELF,
    SUBJ,
    OTHR,
    ...
)
	__extension__\
	({ int compar_fn_(const [u0](/crelude/Files/common_8h.md#typedef-u0) *SUBJ, const [u0](/crelude/Files/common_8h.md#typedef-u0) *OTHR) __VA_ARGS__ \
	   __auto_type _self = (SELF); \
	   qsort([PTR](/crelude/Files/common_8h.md#define-ptr)(_self), [_self.len](/crelude/Files/base64_8h.md#variable-len), sizeof(*[_self.value](/crelude/Files/common_8h.md#variable-value)), compar_fn_); \
	   _self; })

Wrapper around in-place qsort for arrays and slices. Creates a nested function within a statement-expression that is defined by the function body passed in as VA_ARGS to the macro, which defines the comparison function for the sort. This makes use of two GNU extensions: 'nested functions', and 'statement expressions'. e.g. SORT(arr, self, other, { if ((Struct *)self->x == (Struct *)other->x) return 0; if ((Struct *)self->x < (Struct *)other->x) return -1; if ((Struct *)self->x > (Struct *)other->x) return +1; }); SORT(arr, self, other, { return my_compare(self, other); }); SORT(arr, self, other, CMP((Struct *)self->x, (Struct *)other->x));

define CMP

#define CMP(
    A,
    B
)
	{ if ((A) <  (B)) return -1; \
	  if ((A) == (B)) return  0; \
	  if ((A) >  (B)) return +1; }

Function body for comparison of items which have '>', '=' and '<' defined, e.g. int comparef(float a, float b) CMP(a, b)

define ANSI

#define ANSI(
    CODE
)
"\x1b[" CODE "m"

define BOLD

#define BOLD "1"

define FAINT

#define FAINT "2"

define DIM

#define DIM [FAINT](/crelude/Files/common_8h.md#define-faint)

define ITALIC

#define ITALIC "3"

define UNDER

#define UNDER "4"
#define BLINK "5"

define RAPID

#define RAPID "6"

define INVERT

#define INVERT "7"

define HIDDEN

#define HIDDEN "8"

define STRIKE

#define STRIKE "9"

define BOLD_OFF

#define BOLD_OFF "21"

define FAINT_OFF

#define FAINT_OFF "22"

define ITALIC_OFF

#define ITALIC_OFF "23"

define UNDER_OFF

#define UNDER_OFF "24"
#define BLINK_OFF "25"

define RAPID_OFF

#define RAPID_OFF "26"

define INVERT_OFF

#define INVERT_OFF "27"

define HIDDEN_OFF

#define HIDDEN_OFF "28"

define STRIKE_OFF

#define STRIKE_OFF "29"

define RESET

#define RESET "0"

define min

#define min(
    A,
    B
)
	__extension__({ \
	typeof(A) _a = (A); \
	typeof(B) _b = (B); \
	_a > _b ? _b : _a; })

define max

#define max(
    A,
    B
)
	__extension__({ \
	typeof(A) _a = (A); \
	typeof(B) _b = (B); \
	_b > _a ? _b : _a; })

define deref

#define deref(
    T,
    PTR,
    ALT
)
	__extension__\
	({ T _alt = (ALT); \
	   *(T *)[or](/crelude/Files/common_8h.md#function-or)([PTR](/crelude/Files/common_8h.md#define-ptr), &_alt); })

define WRAP

#define WRAP(
    TYPE,
    VALUE
)
= ((TYPE){ .[value](/crelude/Files/common_8h.md#variable-value) = (VALUE) })

Wrap value in wrapper struct.

define UNWRAP

#define UNWRAP(
    STRUCTURE
)
(STRUCTURE).[value](/crelude/Files/common_8h.md#variable-value)

Unwraps pointer/value in sizing wrapper struct.

define PTR

#define PTR(
    ARR
)
(ARR).[value](/crelude/Files/common_8h.md#variable-value)

Explicitly only extract pointer from array/slice.

define FREE_INSIDE

#define FREE_INSIDE(
    S
)
[FREE](/crelude/Files/common_8h.md#define-free)((S).[value](/crelude/Files/common_8h.md#variable-value))

Call to free of inside of slice/array/newtype, etc.

define INIT

#define INIT(
    TYPE,
    ...
)
	{ \
	.[len](/crelude/Files/base64_8h.md#variable-len) = sizeof((TYPE[])__VA_ARGS__)/sizeof(TYPE), \
	.[value](/crelude/Files/common_8h.md#variable-value) = (TYPE[])__VA_ARGS__ \
}

Initialise sizing wrapper with literal.

define LIST

#define LIST(
    TYPE,
    ...
)
	__extension__({ \
	TYPE _slice; \
	typeof(*[_slice.value](/crelude/Files/common_8h.md#variable-value)) _elem; \
	static typeof(_elem) _list[] = __VA_ARGS__; \
	_slice = ((typeof(_slice)){ \
		.[len](/crelude/Files/base64_8h.md#variable-len) = sizeof(_list)/sizeof(_elem), \
		.[value](/crelude/Files/common_8h.md#variable-value) = _list \
	}); _slice; })

Can be used to make slices from literal arrays.

define STRING

#define STRING(
    ...
)
	{ \
	.[len](/crelude/Files/base64_8h.md#variable-len) = sizeof(([byte](/crelude/Files/common_8h.md#typedef-byte)[]){ __VA_ARGS__ }) - 1, \
	.[value](/crelude/Files/common_8h.md#variable-value) = ([byte](/crelude/Files/common_8h.md#typedef-byte)[]){ __VA_ARGS__ } \
}

Initialise sizing wrapper with of string literal.

define STR

#define STR(
    ...
)
(([string](/crelude/Classes/structstring.md))[STRING](/crelude/Files/common_8h.md#define-string)(__VA_ARGS__))

define SEMPTY

#define SEMPTY(
    TYPE
)
((TYPE){ .[len](/crelude/Files/base64_8h.md#variable-len) = 0, .[value](/crelude/Files/common_8h.md#variable-value) = [nil](/crelude/Files/common_8h.md#define-nil) })

Empty slice of certain type.

define AEMPTY

#define AEMPTY(
    TYPE
)
((TYPE){ .[len](/crelude/Files/base64_8h.md#variable-len) = 0, .cap = 0, .[value](/crelude/Files/common_8h.md#variable-value) = [nil](/crelude/Files/common_8h.md#define-nil) })

Empty array of certain type.

define EMPTY

#define EMPTY(
    TYPE
)
((TYPE){ 0 })

Empty / zero struct.

define IS_EMPTY

#define IS_EMPTY(
    ARR
)
((ARR).[len](/crelude/Files/base64_8h.md#variable-len) == 0)

Is array empty?

define AMAKE

#define AMAKE(
    TYPE,
    CAP
)
	{ \
	.[len](/crelude/Files/base64_8h.md#variable-len) = 0, \
	.cap = (CAP), \
	.[value](/crelude/Files/common_8h.md#variable-value) = [emalloc](/crelude/Files/common_8h.md#function-emalloc)((CAP), sizeof(TYPE)) \
}

Heap allocates a variable sized array.

define ANEW

#define ANEW(
    VARIABLE,
    CAP
)
(typeof(VARIABLE))[AMAKE](/crelude/Files/common_8h.md#define-amake)(typeof((VARIABLE).[value](/crelude/Files/common_8h.md#variable-value)[0]), CAP)

Create new array / initialise array from array variable.

define SMAKE

#define SMAKE(
    TYPE,
    LEN
)
	{ \
	.[len](/crelude/Files/base64_8h.md#variable-len) = (LEN), \
	.[value](/crelude/Files/common_8h.md#variable-value) = [emalloc](/crelude/Files/common_8h.md#function-emalloc)((LEN), sizeof(TYPE)) \
}

Heap allocates a constant sized slice type.

define SNEW

#define SNEW(
    VARIABLE,
    LEN
)
(typeof(VARIABLE))[SMAKE](/crelude/Files/common_8h.md#define-smake)(typeof((VARIABLE).[value](/crelude/Files/common_8h.md#variable-value)[0]), LEN)

Create new slice / initialise slice from slice variable.

define SLICE

#define SLICE(
    TYPE,
    OBJ,
    START,
    END
)
	((TYPE){ \
	.[len](/crelude/Files/base64_8h.md#variable-len) = ((([isize](/crelude/Files/common_8h.md#typedef-isize))(END) < 0) ? (OBJ).[len](/crelude/Files/base64_8h.md#variable-len) + 1 : 0) + (END) - (START), \
	.[value](/crelude/Files/common_8h.md#variable-value) = (OBJ).[value](/crelude/Files/common_8h.md#variable-value) + (START) \
})

Take a slice/substring/view of sized type.

define VIEW

#define VIEW(
    TYPE,
    PTR,
    START,
    END
)
	((TYPE){ \
	.[len](/crelude/Files/base64_8h.md#variable-len) = (END) - (START), \
	.[value](/crelude/Files/common_8h.md#variable-value) = ([PTR](/crelude/Files/common_8h.md#define-ptr)) + (START) \
})

Works like SLICE, but on a pointer instead of an array.

define SYMBOLIC

#define SYMBOLIC(
    STR
)
	(([symbol](/crelude/Classes/structsymbol.md)){ \
	.[hash](/crelude/Files/common_8h.md#variable-hash) = [hash_string](/crelude/Files/common_8h.md#function-hash_string)([STR](/crelude/Files/common_8h.md#define-str)), \
	.[value](/crelude/Files/common_8h.md#variable-value) = [STR](/crelude/Files/common_8h.md#define-str) \
})

define SYMBOL_LITERAL

#define SYMBOL_LITERAL(
    STR_LIT
)
	(([symbol](/crelude/Classes/structsymbol.md)){ \
	.[hash](/crelude/Files/common_8h.md#variable-hash) = [hash_string](/crelude/Files/common_8h.md#function-hash_string)([STRING](/crelude/Files/common_8h.md#define-string)(STR_LIT)), \
	.[value](/crelude/Files/common_8h.md#variable-value) = [STRING](/crelude/Files/common_8h.md#define-string)(STR_LIT) \
})

define ACOLLECT

#define ACOLLECT(
    T,
    count,
    pointer
)
	((T){ \
	.[len](/crelude/Files/base64_8h.md#variable-len) = count,     \
	.cap = count,     \
	.[value](/crelude/Files/common_8h.md#variable-value) = pointer, \
})

C array to dynamic array wrapper.

define SCOLLECT

#define SCOLLECT(
    T,
    count,
    pointer
)
	((T){ \
	.[len](/crelude/Files/base64_8h.md#variable-len) = count,     \
	.[value](/crelude/Files/common_8h.md#variable-value) = pointer, \
})

C array to slice wrapper.

define SMAP

#define SMAP(
    T,
    func,
    list
)
	__extension__({ \
	T _mapped; \
	_mapped = ((T)[SMAKE](/crelude/Files/common_8h.md#define-smake)(typeof(*[_mapped.value](/crelude/Files/common_8h.md#variable-value)), (list).[len](/crelude/Files/base64_8h.md#variable-len))); \
	for ([usize](/crelude/Files/common_8h.md#typedef-usize) _i = 0; _i < (list).[len](/crelude/Files/base64_8h.md#variable-len); ++_i) \
		[_mapped.value](/crelude/Files/common_8h.md#variable-value)[_i] = (func)((list).[value](/crelude/Files/common_8h.md#variable-value)[_i]); \
	_mapped; \
})

define AMAP

#define AMAP(
    T,
    func,
    list
)
	__extension__({ \
	T _mapped; \
	_mapped = ((T)[AMAKE](/crelude/Files/common_8h.md#define-amake)(typeof(*[_mapped.value](/crelude/Files/common_8h.md#variable-value)), (list).[len](/crelude/Files/base64_8h.md#variable-len))); \
	for ([usize](/crelude/Files/common_8h.md#typedef-usize) _i = 0; _i < (list).[len](/crelude/Files/base64_8h.md#variable-len); ++_i, ++[_mapped.len](/crelude/Files/base64_8h.md#variable-len)) \
		[_mapped.value](/crelude/Files/common_8h.md#variable-value)[_i] = (func)((list).[value](/crelude/Files/common_8h.md#variable-value)[_i]); \
	_mapped; \
})

define FOR_EACH

#define FOR_EACH(
    ELEM,
    ELEMS
)
	for (struct { typeof(*(ELEMS).[value](/crelude/Files/common_8h.md#variable-value)) item; \
			      typeof((ELEMS).[value](/crelude/Files/common_8h.md#variable-value)) ptr, start; \
				  [usize](/crelude/Files/common_8h.md#typedef-usize) index; \
				  bool first, once; \
				} it = { .item = *(ELEMS).[value](/crelude/Files/common_8h.md#variable-value), \
				         .ptr = (ELEMS).[value](/crelude/Files/common_8h.md#variable-value), \
				         .start = (ELEMS).[value](/crelude/Files/common_8h.md#variable-value), \
				         .index = 0, \
						 .first = true, \
						 .once = true \
				       }; it.once; it.once = false) \
		for (typeof(*(ELEMS).[value](/crelude/Files/common_8h.md#variable-value)) ELEM = *(ELEMS).[value](/crelude/Files/common_8h.md#variable-value); \
		    it.index < (ELEMS).[len](/crelude/Files/base64_8h.md#variable-len); \
			++it.ptr, it.index = (it.ptr - it.start), \
			  it.item = *it.ptr, it.first = false, ELEM = it.item)

For-each loop, iterates across an array or slice. It creates an it variable, that holds:

  • it.index (index in array);
  • it.item (current item of array);
  • it.ptr (pointer to current item in array);
  • it.first (pointer to frist item in array);
  • it.once (a bool, true if we are on the first iteration). For example:
newarray(IntArray, int);
IntArray xs = AMAKE(IntArray, 2);

int elem1 = 5;
int elem2 = 3;
sliceof(int) elems = INIT(int, { 6, 9, 1 });

push(&xs, &elem1, sizeof(int));
push(&xs, &elem2, sizeof(int));
extend(&xs, &elems, sizeof(int));

FOR_EACH(x, xs) {
    printf("xs[%zu] = %d\n", it.index, x);
}

Filename: .c

Will print:

xs[0] = 5
xs[1] = 3
xs[2] = 6
xs[3] = 9
xs[4] = 1

define foreach

#define foreach [FOR_EACH](/crelude/Files/common_8h.md#define-for_each)

Source code


#ifndef COMMON_HEADER_
#define COMMON_HEADER_

#undef  _GNU_SOURCE
#define _GNU_SOURCE 1 

#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#include <stdarg.h>
#include <limits.h>
#include <string.h>
#include <assert.h>
#ifdef __linux__
    #include <unistd.h>
#endif

/* Default macros */
#ifndef FREE
    #define FREE free
#endif
#ifndef MALLOC
    #define MALLOC malloc
#endif
#ifndef REALLOC
    #define REALLOC realloc
#endif

/* Misc macros */
#define VA_NUM_ARGS_IMPL(_1,_2,_3,_4,_5, N,...) N
#define VA_NUM_ARGS(...) VA_NUM_ARGS_IMPL(__VA_ARGS__, 5, 4, 3, 2, 1)

#define _TSTR(x) #x
#define TSTR(x) _TSTR(x)

#if defined(_MSC_VER) || defined(__INTEL_COMPILER)
    #define DO_PRAGMA(x) __pragma(x)
    #define PRAGMA_NO_WARNING __pragma(warning(push, 0))
    #define PRAGMA_POP_WARNING __pragma(warning(pop))
    #define WARNING(...) __pragma(message(Warning: __VA_ARGS__))
#elif defined(__clang__)
    #define DO_PRAGMA(x) _Pragma(#x)
    #define PRAGMA_NO_WARNING \
        _Pragma("clang diagnostic push") \
        _Pragma("clang diagnostic ignored \"-Wall\"") \
        _Pragma("clang diagnostic ignored \"-Wextra\"") \
        _Pragma("clang diagnostic ignored \"-Wpedantic\"")
    #define PRAGMA_POP_WARNING _Pragma("clang diagnostic pop")
    #define WARNING(S) DO_PRAGMA(clang warning S)
#elif defined(__GNUC__)
    #define DO_PRAGMA(x) _Pragma(#x)
    // Does not behave as nicely as CLANG version does.
    #define PRAGMA_NO_WARNING \
        _Pragma("GCC diagnostic push") \
        _Pragma("GCC diagnostic ignored \"-Wall\"") \
        _Pragma("GCC diagnostic ignored \"-Wextra\"") \
        _Pragma("GCC diagnostic ignored \"-Wpedantic\"")
    #define PRAGMA_POP_WARNING _Pragma("GCC diagnostic pop")
    #define WARNING(S) DO_PRAGMA(GCC warning S)
#endif

#define TODO(...) DO_PRAGMA(message("TODO: " #__VA_ARGS__ \
                            " (" __FILE__ ":" TSTR(__LINE__)")"))

/* Version number */
#define crelude_V_MAJOR 0
#define crelude_V_MINOR 1
#define crelude_V_PATCH 0

#define crelude_VERSION \
    "v" TSTR(crelude_V_MAJOR) \
    "." TSTR(crelude_V_MINOR) \
    "." TSTR(crelude_V_PATCH)

#define ARRAY_REALLOC_FACTOR 1.5

/* Syntax helpers */
#define loop while (1)
#define whilst while
#define unless(cond) if (!(cond))
#define never if (0)
#define always if (1)
#define until(cond) while (!(cond))
// Newtypes and arrays, slices, maps, etc.
#define unqualify(D, T)  typedef D T T
#define record(NAME)     typedef struct _##NAME NAME; struct _##NAME
#define enumerable(NAME) typedef   enum _##NAME NAME;   enum _##NAME
#define overlap(NAME)    typedef  union _##NAME NAME;  union _##NAME
#define newtype(NT, T) typedef struct _##NT { T value; } NT
#define arrayof(T) struct { \
    T (*value); \
    usize len;  \
    usize cap;  \
}
#define newarray(NT, T) typedef arrayof(T) NT
#define sliceof(T) struct { \
    T (*value); \
    usize len;  \
}
#define newslice(NT, T) typedef sliceof(T) NT
#define hashof(T) struct { \
    u64 hash; \
    T value;  \
}
#define newhashable(NT, T) typedef hashof(T) NT
enum HashKeyType {  // Use this to pick a default hash method/function.
    HKT_STRING  = 1 << 0,
    HKT_RUNIC   = 1 << 1,
    HKT_CSTRING = 1 << 2,
    HKT_MEM_SLICE = 1 << 3, //< can be used for any slice, convert with TO_BYTES(...).
    HKT_RAW_BYTES = 1 << 4,  //< just hash the raw bytes.
    HKT_SMALL_INTEGER = 1 << 5  //< integer size ≤ than hash size, just upcast.
}; unqualify(enum, HashKeyType);
#define HASHMAP_LOAD_THRESHOLD 0.85
#define HASHMAP_GROWTH_FACTOR  2
#define newmap(NT, K, V) typedef mapof(K, V) NT
#define mapof(K, V) struct { \
    usize len; \
    arrayof(hashnode(K, V)) buckets; /* a hash value of zero indicates absence. */\
    usize value_size; \
    usize   key_size; \
    usize  node_size; \
    /* offsets of `hashnode` struct. */ \
    usize  hash_offset; \
    usize   key_offset; \
    usize value_offset; \
    usize  next_offset; \
    HashKeyType key_type; /* < how should the hash-function hash the key. */ \
    u64 (*hasher)(const u0 *, usize); \
}
#define hashnode(K, V) struct { \
    hashof(K) key; \
    V value;  /* < value stored.   */ \
    u0 *next; /* < next hash-node. */ \
}
#define MMAKE(K, V, CAP) { \
    .len = 0, \
    .buckets = AMAKE(hashnode(K, V), CAP), \
    .key_size = sizeof(K), \
    .value_size = sizeof(V), \
    .node_size = sizeof(hashnode(K, V)), \
    .hash_offset  = offsetof(hashnode(K, V), key) + offsetof(hashof(K), hash), \
    .key_offset   = offsetof(hashnode(K, V), key) + offsetof(hashof(K), value), \
    .value_offset = offsetof(hashnode(K, V), value), \
    .next_offset  = offsetof(hashnode(K, V), next), \
    .key_type = _Generic(*(K *)NULL, \
        string: HKT_STRING, \
        runic: HKT_RUNIC, \
        byte *: HKT_CSTRING, \
        MemSlice: HKT_MEM_SLICE, \
        char[8]: HKT_SMALL_INTEGER, \
        char[7]: HKT_SMALL_INTEGER, \
        char[6]: HKT_SMALL_INTEGER, \
        char[5]: HKT_SMALL_INTEGER, \
        char[4]: HKT_SMALL_INTEGER, \
        char[3]: HKT_SMALL_INTEGER, \
        char[2]: HKT_SMALL_INTEGER, \
        char[1]: HKT_SMALL_INTEGER, \
        signed char:        HKT_SMALL_INTEGER, \
        signed short:       HKT_SMALL_INTEGER, \
        signed int:         HKT_SMALL_INTEGER, \
        signed long:        HKT_SMALL_INTEGER, \
        signed long long:   HKT_SMALL_INTEGER, \
        unsigned char:      HKT_SMALL_INTEGER, \
        unsigned short:     HKT_SMALL_INTEGER, \
        unsigned int:       HKT_SMALL_INTEGER, \
        unsigned long:      HKT_SMALL_INTEGER, \
        unsigned long long: HKT_SMALL_INTEGER, \
        float:  HKT_SMALL_INTEGER, \
        double: HKT_SMALL_INTEGER, \
        default: HKT_RAW_BYTES), \
    .hasher = _Generic(*(K *)NULL, \
        string: string_hash, \
        runic: runic_hash, \
        byte *: cstring_hash, \
        MemSlice: mem_hash, \
        char[8]: upcast_hash, \
        char[7]: upcast_hash, \
        char[6]: upcast_hash, \
        char[5]: upcast_hash, \
        char[4]: upcast_hash, \
        char[3]: upcast_hash, \
        char[2]: upcast_hash, \
        char[1]: upcast_hash, \
        signed char:        upcast_hash, \
        signed short:       upcast_hash, \
        signed int:         upcast_hash, \
        signed long:        upcast_hash, \
        signed long long:   upcast_hash, \
        unsigned char:      upcast_hash, \
        unsigned short:     upcast_hash, \
        unsigned int:       upcast_hash, \
        unsigned long:      upcast_hash, \
        unsigned long long: upcast_hash, \
        float:  upcast_hash, \
        double: upcast_hash, \
        default: default_hash) \
}
#define MNEW(VARIABLE, CAP) (typeof(VARIABLE))MMAKE( \
    typeof((VARIABLE).buckets.value[0].key.value), \
    typeof((VARIABLE).buckets.value[0].value), \
    CAP)

#define NTH(LIST, N) UNWRAP((LIST))[(N)]
#define GET(LIST, N) __extension__\
    ({ __auto_type _list = (LIST); \
       __auto_type _n = (N); \
       usize _index = _n < 0 ? (usize)(_list.len + _n) : (usize)_n; \
       UNWRAP(_list)[_index]; })
#define SET(LIST, N, V) __extension__\
    ({ __auto_type _list = (LIST); \
       __auto_type _n = (N); \
       usize _index = _n < 0 ? (usize)(_list.len + _n) : (usize)_n; \
       UNWRAP(_list)[_index] = (V); })

#define UNUSED1(z) (void)(z)
#define UNUSED2(y,z) UNUSED1(y),UNUSED1(z)
#define UNUSED3(x,y,z) UNUSED1(x),UNUSED2(y,z)
#define UNUSED4(b,x,y,z) UNUSED2(b,x),UNUSED2(y,z)
#define UNUSED5(a,b,x,y,z) UNUSED2(a,b),UNUSED3(x,y,z)
#define UNUSED_IMPL_(nargs) UNUSED ## nargs
#define UNUSED_IMPL(nargs) UNUSED_IMPL_(nargs)
#define UNUSED(...) UNUSED_IMPL(VA_NUM_ARGS(__VA_ARGS__))(__VA_ARGS__)

#define NO_ERROR EXIT_SUCCESS
#define OK EXIT_SUCCESS
#define OKAY EXIT_SUCCESS
#define FAIL EXIT_FAILURE

#define NOOP ((void)0)
#define nil ((void *)NULL)

#define FLOOR(T, N) __extension__\
    ({ typeof(N) _n = (N); \
       (T)_n - (_n < 0 ? 1 : 0); })
#define CEIL(T, N) __extension__\
    ({ typeof(N) _n = (N); \
       (T)_n + (_n < 0 ? 0 : 1); })

/* Types */
newtype(atomic_t, int);

typedef void u0;
#define UNIT ;

typedef   signed int ierr;
typedef unsigned int uerr;  
typedef unsigned long uword;  
typedef   signed long iword;  
#define WORD_SIZE sizeof(long)

typedef unsigned int ufast;
typedef   signed int ifast;

typedef ptrdiff_t isize;
typedef    size_t usize; 

typedef  intptr_t iptr;
typedef uintptr_t uptr; 

typedef  intmax_t imax;
typedef uintmax_t umax;

typedef unsigned char umin;
typedef   signed char imin;

_Static_assert(sizeof(umin) == 1 && sizeof(imin) == 1,
               "`umin` and `imin` must have size one (1).");

#define __UCHAR8__ char
#if (CHAR_BIT == 8)
    typedef   signed char i8;
    typedef unsigned char u8;

    #if (CHAR_MIN < 0)
        WARNING("Crelude works best with -funsigned-char.")
        #undef  __UCHAR8__
        #define __UCHAR8__ unsigned char
    #endif
#else
    typedef  __int8_t i8;
    typedef __uint8_t u8;

    #undef  __UCHAR8__
    #define __UCHAR8__ __uint8_t;
#endif

#ifndef IMPLEMENTATION
    typedef __UCHAR8__ byte;
#else
    typedef __uint8_t byte;  
#endif

typedef  __int16_t i16;
typedef __uint16_t u16;

typedef  __int32_t i32;
typedef __uint32_t u32;

typedef u32 rune;

#if (__LONG_WIDTH__ == 64)
    typedef   signed long i64;
    typedef unsigned long u64;
#elif (__LONG_LONG_WIDTH__ == 64)
    typedef   signed long long i64;
    typedef unsigned long long u64;
#else
    typedef  __int64_t i64;
    typedef __uint64_t u64;
#endif

#ifdef __SIZEOF_INT128__
    typedef  __int128_t i128;
    typedef __uint128_t u128;
#endif

#define   _FLOAT_BIT (__SIZEOF_FLOAT__       * CHAR_BIT)
#define  _DOUBLE_BIT (__SIZEOF_DOUBLE__      * CHAR_BIT)
#define _LDOUBLE_BIT (__SIZEOF_LONG_DOUBLE__ * CHAR_BIT)

#ifdef __STDC_IEC_559__
    typedef  float f32;
    typedef double f64;
#else
    #if _FLOAT_BIT == 32
        typedef float f32;
    #endif
    #if _DOUBLE_BIT == 64
        typedef double f64;
    #endif
#endif

#if (_LDOUBLE_BIT == 80)
    typedef long double f80;
#elif (_LDOUBLE_BIT == 128)
    typedef long double f128;
#endif

newarray(GenericArray, u0);
newarray(MemArray, umin);
newslice(GenericSlice, u0);
newslice(MemSlice, umin);
newmap(GenericMap, u0 *, u0 *);

newslice(string, byte);
newslice(runic, rune);

newarray(StringBuilder, byte);
newarray(RunicBuilder, rune);

newhashable(symbol, string);

/* Common Constants */
static const ierr NUL = 0;
static const byte NUL_BYTE = '\0';
static const string NUL_STRING = { .len = 0, .value = (byte *)&NUL_BYTE };
static const iword ZERO = 0;

/* Common Functions */
extern u0 panic(const byte *, ...) __attribute__((noreturn));
extern u0 *or(const u0 *nullable, const u0 *nonnull);
extern bool is_zero(imax);
extern bool is_zerof(f64);
extern bool is_zeroed(u0 *, usize);
extern u0 zero(u0 *blk, usize width);
extern u0 *emalloc(usize, usize);
extern u0 reverse(u0 *self, usize width);
extern MemSlice reverse_endianness(MemSlice bytes);
bool is_little_endian(void);
u128 big_endian(umin *start, usize bytes);
extern u0 swap(u0 *self, usize pivot, usize width);
extern u0 memswap(umin *a, umin *b, usize bytes);
extern usize resize(u0 *self, usize cap, usize width);
extern usize grow(u0 *self, usize count, usize width);
extern u0 *get(u0 *self, usize index, usize width);
extern u0 *set(u0 *self, usize index, const u0 *elem, usize width);
extern usize push(u0 *self, const u0 *element, usize width);
extern u0 *pop(u0 *self, usize width);
extern u0 *shift(u0 *self, usize width);
extern usize insert(u0 *self, usize index, const u0 *element, usize width);
extern usize extend(u0 *self, const u0 *slice, usize width);
extern usize splice(u0 *self, usize index, const u0 *slice, usize width);
//           along with the number of removed bytes (`len` of slice).
extern GenericSlice cut(u0 *self, usize from, isize upto, usize width);
extern usize null(u0 *self, usize width);
extern string from_cstring(const byte *);
extern bool string_eq(const string, const string);
extern i16 string_cmp(const string, const string);
extern i16 string_ncmp(const string, const string, usize n);
extern u64 hash_string(const string);
extern u64 hash_bytes(const MemSlice);
extern u0 associate(u0 *self, const u0 *key, const u0 *value);
extern u0 *lookup(u0 *self, const u0 *key);
extern bool drop(u0 *self, const u0 *key);
extern GenericSlice get_keys(u0 *self);
extern bool has_key(u0 *self, u0 *key);
extern u0 empty_map(u0 *self);
extern bool is_empty_map(u0 *self);
extern u0 free_map(u0 *self);
extern usize init_hashnode(u0 *, const u0 *, u64, const u0 *, const u0 *);
extern u0 dump_hashmap(u0 *self, byte *key_formatter, byte *value_formatter);

/* Common Macros */

#define COPY(SELF) __extension__\
    ({ __auto_type _self = (SELF); \
       __auto_type _copy = _self; \
       PTR(_copy) = emalloc(_self.len, sizeof(*PTR(_self))); \
       memcpy(PTR(_copy), PTR(_self), sizeof(*PTR(_copy)) * _copy.len); \
       _copy; })

#define TO_BYTES(SELF) __extension__\
    ({ __auto_type _self = (SELF); \
       MemSlice _bytes = { \
          .len = _self.len * sizeof(*_self.value), \
          .value = (umin *)_self.value \
       }; _bytes; })

#define FROM_BYTES(T, SELF) __extension__\
    ({ __auto_type _self = (SELF); \
       T _normal = { \
          .len = _self.len / sizeof(*_self.value), \
          .value = (u0 *)_self.value \
       }; _normal; })

// ---
// Macros for array functions to avoid use of `sizeof(T)` everywhere.
// These macros do the referencing and void-pointer casting for you, and thus
// let you use non-LVALUES as input (except SELF, SELF must sill be an LVALUE).
// ---

#define REVERSE(SELF) __extension__\
    ({ __auto_type _self = &(SELF); \
       reverse(_self, sizeof(*_self->value)); \
       *_self; })

#define SWAP(SELF, PIVOT) __extension__\
    ({ __auto_type _self = &(SELF); \
       swap(_self, (PIVOT), sizeof(*_self->value)); })

#define PUSH(SELF, ELEM) __extension__\
    ({ __auto_type           _self = &(SELF); \
       typeof(*_self->value) _elem =  (ELEM); \
       push(_self, &_elem, sizeof(_elem)); })

#define POP(SELF) __extension__\
    ({ __auto_type _self = &(SELF); \
       (typeof(_self->value))pop(_self, sizeof(*_self->value)); })

#define SHIFT(SELF) __extension__\
    ({ __auto_type _self = &(SELF); \
       (typeof(_self->value))shift(_self, sizeof(*_self->value)); })

#define INSERT(SELF, INDEX, ELEM) __extension__\
    ({ __auto_type           _self = &(SELF); \
       typeof(*_self->value) _elem =  (ELEM); \
       insert(_self, (INDEX), &_elem, sizeof(_elem)); })

#define EXTEND(SELF, SLIC) __extension__\
    ({ __auto_type _self = &(SELF); \
       __auto_type _slic =  (SLIC); \
       extend(_self, &_slic, sizeof(*_slic.value)); })

#define SPLICE(SELF, INDEX, SLIC) __extension__\
    ({ __auto_type _self = &(SELF); \
       __auto_type _slic =  (SLIC); \
       splice(_self, (INDEX), &_slic, sizeof(*_slic.value)); })

#define CUT(SELF, FROM, UPTO) __extension__\
    ({ __auto_type _self = &(SELF); \
       static GenericSlice _cut; \
       _cut = cut(_self, (FROM), (UPTO), sizeof(*_self->value)); \
       (u0 *)&_cut; })

#define REMOVE(SELF, INDEX) __extension__\
    ({ __auto_type _self = &(SELF); \
       usize _indx = (INDEX); \
       GenericSlice _cut \
           = cut(_self, _indx, _indx, sizeof(*_self->value)); \
       (typeof(_self->value))PTR(_cut); })

/* macros for hash-tables (maps) */
#define ASSOCIATE(SELF, KEY, VAL) __extension__\
    ({ __auto_type _self = &(SELF); \
       __auto_type _key = (KEY); \
       __auto_type _val = (VAL); \
       associate(_self, &_key, &_val); })

#define LOOKUP(SELF, KEY) __extension__\
    ({ __auto_type _self = &(SELF); \
       __auto_type _key = (KEY); \
       (typeof(_self->buckets.value[0].value) *)lookup(_self, &_key); })

#define DROP(SELF, KEY) __extension__\
    ({ __auto_type _self = &(SELF); \
       __auto_type _key = (KEY); \
       drop(_self, &_key); })

#define KEYS(MAP) __extension__\
    ({ __auto_type _map = &(MAP); \
       static GenericSlice _keys; \
       _keys = get_keys(_map); \
       (u0 *)&_keys; })

#define HAS_KEY(MAP, KEY) __extension__\
    ({ __auto_type _map = &(MAP); \
       __auto_type _key = (KEY); \
       has_key(_map, &_key); })

// Some aliases and shortcuts:
#define APPEND(SELF, ELEM) PUSH(SELF, ELEM)
#define PREPEND(SELF, ELEM) INSERT(SELF, 0, ELEM)
#define UNSHIFT(SELF, ELEM) PREPEND(SELF, ELEM)
#define PREFIX(SELF, SLIC) SPLICE(SELF, 0, SLIC)
#define HEAD(SELF, END) SLICE(SELF, 0, END)
#define TAIL(SELF, BEG) SLICE(SELF, BEG, -1)
#define FIRST(SELF) NTH(SELF, 0)
#define LAST(SELF) GET(SELF, -1)

#define QSORT(SELF, CMPR) __extension__\
    ({ __auto_type _self = (SELF); \
       qsort(PTR(_self), _self.len, sizeof(*_self.value), CMPR); \
       _self; })

#define SORT(SELF, SUBJ, OTHR, ...) __extension__\
    ({ int compar_fn_(const u0 *SUBJ, const u0 *OTHR) __VA_ARGS__ \
       __auto_type _self = (SELF); \
       qsort(PTR(_self), _self.len, sizeof(*_self.value), compar_fn_); \
       _self; })

#define CMP(A, B) \
    { if ((A) <  (B)) return -1; \
      if ((A) == (B)) return  0; \
      if ((A) >  (B)) return +1; }

// --- ANSI colour codes. ---

#define ANSI(CODE) "\x1b[" CODE "m"
#define BOLD   "1"
#define FAINT  "2"
#define DIM FAINT
#define ITALIC "3"
#define UNDER  "4"
#define BLINK  "5"
#define RAPID  "6"
#define INVERT "7"
#define HIDDEN "8"
#define STRIKE "9"
#define BOLD_OFF   "21"  // Or sometimes, double-underline...
#define FAINT_OFF  "22"
#define ITALIC_OFF "23"
#define UNDER_OFF  "24"
#define BLINK_OFF  "25"
#define RAPID_OFF  "26"
#define INVERT_OFF "27"
#define HIDDEN_OFF "28"
#define STRIKE_OFF "29"
#define RESET "0"

#define min(A, B) __extension__({ \
    typeof(A) _a = (A); \
    typeof(B) _b = (B); \
    _a > _b ? _b : _a; })
#define max(A, B) __extension__({ \
    typeof(A) _a = (A); \
    typeof(B) _b = (B); \
    _b > _a ? _b : _a; })

#define deref(T, PTR, ALT) __extension__\
    ({ T _alt = (ALT); \
       *(T *)or(PTR, &_alt); })

#define WRAP(TYPE, VALUE) = ((TYPE){ .value = (VALUE) })
#define UNWRAP(STRUCTURE) (STRUCTURE).value
#define PTR(ARR) (ARR).value
#define FREE_INSIDE(S) FREE((S).value)
#define INIT(TYPE, ...) { \
    .len = sizeof((TYPE[])__VA_ARGS__)/sizeof(TYPE), \
    .value = (TYPE[])__VA_ARGS__ \
}
#define LIST(TYPE, ...) __extension__({ \
    TYPE _slice; \
    typeof(*_slice.value) _elem; \
    static typeof(_elem) _list[] = __VA_ARGS__; \
    _slice = ((typeof(_slice)){ \
        .len = sizeof(_list)/sizeof(_elem), \
        .value = _list \
    }); _slice; })

#define STRING(...) { \
    .len = sizeof((byte[]){ __VA_ARGS__ }) - 1, \
    .value = (byte[]){ __VA_ARGS__ } \
}

#define STR(...) ((string)STRING(__VA_ARGS__))

#define SEMPTY(TYPE) ((TYPE){ .len = 0, .value = nil })
#define AEMPTY(TYPE) ((TYPE){ .len = 0, .cap = 0, .value = nil })
#define EMPTY(TYPE) ((TYPE){ 0 })

#define IS_EMPTY(ARR) ((ARR).len == 0)

#define AMAKE(TYPE, CAP) { \
    .len = 0, \
    .cap = (CAP), \
    .value = emalloc((CAP), sizeof(TYPE)) \
}
#define ANEW(VARIABLE, CAP) (typeof(VARIABLE))AMAKE(typeof((VARIABLE).value[0]), CAP)

#define SMAKE(TYPE, LEN) { \
    .len = (LEN), \
    .value = emalloc((LEN), sizeof(TYPE)) \
}
#define SNEW(VARIABLE, LEN) (typeof(VARIABLE))SMAKE(typeof((VARIABLE).value[0]), LEN)

#define SLICE(TYPE, OBJ, START, END) ((TYPE){ \
    .len = (((isize)(END) < 0) ? (OBJ).len + 1 : 0) + (END) - (START), \
    .value = (OBJ).value + (START) \
})

#define VIEW(TYPE, PTR, START, END) ((TYPE){ \
    .len = (END) - (START), \
    .value = (PTR) + (START) \
})

#define SYMBOLIC(STR) ((symbol){ \
    .hash = hash_string(STR), \
    .value = STR \
})

#define SYMBOL_LITERAL(STR_LIT) ((symbol){ \
    .hash = hash_string(STRING(STR_LIT)), \
    .value = STRING(STR_LIT) \
})

#define ACOLLECT(T, count, pointer) ((T){ \
    .len = count,     \
    .cap = count,     \
    .value = pointer, \
})

#define SCOLLECT(T, count, pointer) ((T){ \
    .len = count,     \
    .value = pointer, \
})

#define SMAP(T, func, list) __extension__({ \
    T _mapped; \
    _mapped = ((T)SMAKE(typeof(*_mapped.value), (list).len)); \
    for (usize _i = 0; _i < (list).len; ++_i) \
        _mapped.value[_i] = (func)((list).value[_i]); \
    _mapped; \
})

#define AMAP(T, func, list) __extension__({ \
    T _mapped; \
    _mapped = ((T)AMAKE(typeof(*_mapped.value), (list).len)); \
    for (usize _i = 0; _i < (list).len; ++_i, ++_mapped.len) \
        _mapped.value[_i] = (func)((list).value[_i]); \
    _mapped; \
})

#define FOR_EACH(ELEM, ELEMS) \
    for (struct { typeof(*(ELEMS).value) item; \
                  typeof((ELEMS).value) ptr, start; \
                  usize index; \
                  bool first, once; \
                } it = { .item = *(ELEMS).value, \
                         .ptr = (ELEMS).value, \
                         .start = (ELEMS).value, \
                         .index = 0, \
                         .first = true, \
                         .once = true \
                       }; it.once; it.once = false) \
        for (typeof(*(ELEMS).value) ELEM = *(ELEMS).value; \
            it.index < (ELEMS).len; \
            ++it.ptr, it.index = (it.ptr - it.start), \
              it.item = *it.ptr, it.first = false, ELEM = it.item)

#define foreach FOR_EACH

/* static functions */
/* |- default hash functions */

 __attribute__((unused))
static u64 string_hash(const u0 *key, usize _)
{
    UNUSED(_);
    return hash_string(*(string *)key);
}

 __attribute__((unused))
static u64 runic_hash(const u0 *key, usize _)
{
    UNUSED(_);
    runic runes = *(runic *)key;
    return hash_bytes(TO_BYTES(runes));
}

 __attribute__((unused))
static u64 mem_hash(const u0 *key, usize _)
{
    UNUSED(_);
    return hash_bytes(*(MemSlice *)key);
}

 __attribute__((unused))
static u64 upcast_hash(const u0 *key, usize size)
{
    u64 hash = 0;
    memcpy(&hash, key, size);
    return hash;  // no hashing, just cast the key to a u64.
}

 __attribute__((unused))
static u64 cstring_hash(const u0 *key, usize _)
{
    UNUSED(_);
    return hash_string(from_cstring(*(byte **)key));
}

 __attribute__((unused))
static u64 default_hash(const u0 *key, usize size)
{  // just hashes the plain raw bytes.
    return hash_bytes(VIEW(MemSlice, (umin *)key, 0, size));
}

/* Only define a `main` if ENTRY_FUNCTION is defined */
#ifdef ENTRY_FUNCTION
    newslice(Arguments, string);
    newslice(CArguments, const char *);

    ierr (ENTRY_FUNCTION)(Arguments);

    ierr main(ifast argc, const char **argv)
    {
        Arguments args;
        ierr res;

        args = SMAP(Arguments,
            from_cstring, SCOLLECT(CArguments, argc, argv));
        res = (ENTRY_FUNCTION)(args);

        free(UNWRAP(args));
        return res;
    }
#endif

#endif

Updated on 23 August 2022 at 00:54:19 UTC


title: src/crelude/io.c


src/crelude/io.c

Source code

#include "io.h"
#include "common.h"
#include "utf.h"

#include <ctype.h>
#include <stdarg.h>
#include <wchar.h>

#ifndef IMPLEMENTATION

u0 panic(const byte *message, ...)
{
    va_list args;
    va_start(args, message);
    ierr res = novel_vfprintf_newline(stderr, message, args);
    va_end(args);

    if (res < 0)
        eputs("panic: Out of space.");

    abort();
}

ierr fput(string s, FILE *stream)
{
    ierr err = 1;
    foreach (c, s) {
        unless (err == NUL) err = fputc(c, stream);
        else return err;
    }
    err = fputc('\n', stream);
    return err;
}

ierr put(string s)
{ return fput(s, stdout); }

ierr eput(string s)
{ return fput(s, stderr); }

ierr eputs(const byte *s)
{
    ierr err;
    err = fputs(s, stderr);
    if (err <= 0) return err;
    err = fputc('\n', stderr);
    return err;
}

usize sizeof_specifier(const byte *spec)
{
    UNUSED(spec);
    PANIC("Not implemented.");
    return 0;
}

static string FORMATTER_FLAGS
    = INIT(byte, { '+', '-', ' ', '#', '0' });
static string LENGTH_SUBSPECIFIERS
    = INIT(byte, { 'h', 'l', 'j', 'z', 't', 'L' });


struct Formatter {
    string flags;
    string width;
    string precision; 
    string length;
    byte specifier;

    usize offset;
};
unqualify(struct, Formatter);

// NOTE: Allocates on heap.
static string formatter_string(Formatter formatter)
{
    StringBuilder repr = AMAKE(byte, formatter.offset + 2);

    push(&repr, "%", 1);
    extend(&repr, &formatter.flags, 1);
    extend(&repr, &formatter.width, 1);
    unless (IS_EMPTY(formatter.precision)) {
        push(&repr, ".", 1);
        extend(&repr, &formatter.precision, 1);
    }
    extend(&repr, &formatter.length, 1);
    push(&repr, &formatter.specifier, 1);

    // NUL-terminate the string slice.
    push(&repr, &NUL_BYTE, 1);
    return SLICE(string, repr, 0, -2);
}

static Formatter parse_formatter(string formatter)
{
    usize i = 0;
    if (formatter.value[i] == '%') ++i;

    usize flags_start = i;
    loop {  // Parse flags.
        bool found = false;
        foreach (ss, FORMATTER_FLAGS) {
            if (formatter.value[i] == ss) {
                found = true;
                ++i;
            }
        }
        unless (found) break;
    }
    string flags = SLICE(string, formatter, flags_start, i);


    usize width_start = i;  // Parse width.
    if (formatter.value[i] == '*') ++i;
    else while (isdigit(formatter.value[i])) ++i;
    string width = SLICE(string, formatter, width_start, i);

    usize precision_start = i;
    if (formatter.value[i] == '.') {  // Parse precision.
        ++i;  // Skip '.'
        if (formatter.value[i] == '*') ++i;
        else while (isdigit(formatter.value[i])) ++i;
    }
    string precision = SLICE(string, formatter, precision_start, i);

    usize length_start = i;
    loop {  // Parse length subspecifier.
        bool found = false;
        foreach (ss, LENGTH_SUBSPECIFIERS) {
            if (formatter.value[i] == ss) {
                found = true;
                ++i;
            }
        }
        unless (found) break;
    }
    string length = SLICE(string, formatter, length_start, i);

    // Parse specifier as the single byte immediately after.
    byte specifier = formatter.value[i++];

    return ((Formatter){
        .flags = flags,
        .width = width,
        .precision = precision,
        .length = length,
        .specifier = specifier,
        .offset = i
    });
}

// TODO(maybe): Add a binary formatter.
string novel_vsprintf(const byte *format, va_list args)
{
    newarray(ByteArray, byte);
    ByteArray bytes = AMAKE(byte, strlen(format) + 64);

    usize i = 0;
    byte c;
    until ('\0' == (c = format[i++])) {
        unless (c == '%') {
            push(&bytes, &c, sizeof(byte));  // Other characters are preserved.
            continue;
        }

        string format_string = VIEW(string, (byte *)format, i, -1);
        Formatter formatter = parse_formatter(format_string);
        i += formatter.offset;

        bool is_array_formatter = false;

        // TODO: Padding, text formatting etc. on the novel formatters,
        //       i.e. %b, %S, %C, %r, %U, %D and %V.
        switch (formatter.specifier) {
        case 'S': {  // '%S', string slice formatter.
            string value = va_arg(args, string);
            extend(&bytes, &value, sizeof(byte));
        } break;
        case 'C': {  // '%C', rune formatter.
            rune value = va_arg(args, rune);
            string ucs_bytes = INIT(byte, { 0, 0, 0, 0, 0 });
            ucs_bytes = rune_to_utf8(ucs_bytes, value);
            extend(&bytes, &ucs_bytes, sizeof(byte));
        } break;
        case 'r': {  // '%r', runic (UCS-4) string formatter.
            runic value = va_arg(args, runic);
            string ucs_bytes = SMAKE(byte, 4 * (value.len + 1));
            ucs_bytes = ucs4_to_utf8(ucs_bytes, value);
            extend(&bytes, &ucs_bytes, sizeof(byte));
        } break;
        case 'U': {  // '%U', runic 8-(hex)digit unicode codepoint formatter.
            rune value = va_arg(args, rune);
            byte res[] = "U+00000000";
            string sliced = VIEW(string, res, 0, 10);
            sprintf(res + 2, "%08X", value);
            extend(&bytes, &sliced, sizeof(byte));
        } break;
        case 'b': {  // '%b' boolean formatter.
            int value = va_arg(args, int);  //< _Bool gets promoted to int.
            string bool_string = value ? STR("true") : STR("false");
            extend(&bytes, &bool_string, sizeof(byte));
        } break;
        case 'D':  // '%D{·}{·}' dynamic array type formatter.
            // Following the 'D' must be the format specifier for the elements,
            // and finally the delimiter characters.
            // e.g. array of `int`s, separated by command and a space (", "):
            // `println("[%Dd{, }]", my_arr);`.
            is_array_formatter = true;
            /* fallthrough */
        case 'V': {  // '%V{·}{·}' view/slice type formatter.
            // Following the 'V' must be the format specifier for the elements.
            // and finally the delimiter characters.
            // e.g. slice of doubles, separated by tabs:
            // `println("{ %V{%0.3lf}\t }", my_slice);`.
            string elem_repr = SEMPTY(string);
            Formatter elem_formatter;

            if (format[i] == '{') {
                usize begin = ++i;  // TODO: Nesting '{...}'?
                until (format[i] == '}') ++i;
                usize len = i - begin;
                ++i;  // Skip '}'.

                // `elem_repr` must NUL-terminate, hence we make a copy.
                byte *buf = emalloc(len, sizeof(byte));
                buf = memcpy(buf, format + begin, len * sizeof(byte));
                buf[len] = '\0';
                elem_repr = VIEW(string, buf, 0, len);

                usize j = 0;
                until (elem_repr.value[j] == '%')  // TODO: Better error message.
                    if (j >= elem_repr.len)
                        PANIC("Broken printf element formatter, missing '%%'.");
                    else ++j;
                // Slice with everything after the
                // '%' sign in the element formatter.
                string elem_format_string = SLICE(string, elem_repr, j + 1, -1);
                elem_formatter = parse_formatter(elem_format_string);
            } else {  // Otherwise, it should just have a specifier.
                // Prepend a '%' character, if not given.
                if (format[i] == '%') ++i;

                string elem_format_string = VIEW(string, (byte *)format, i, -1);
                elem_formatter = parse_formatter(elem_format_string);
                usize offs = elem_formatter.offset;

                // `elem_repr` must NUL-terminate, hence we make a copy.
                byte *buf = emalloc(offs + 2, sizeof(byte));
                buf[0] = '%';
                memcpy(buf + 1, format + i, offs * sizeof(byte));
                buf[offs + 1] = '\0';
                elem_repr = VIEW(string, buf, 0, offs + 1);

                i += offs;
            }

            string delim = SEMPTY(string);
            if (format[i] == '{') {
                usize begin = i + 1;  // TODO: Nesting '{...}'?
                until (format[++i] == '}');
                delim = VIEW(string, (byte *)format, begin, i);
                ++i;  // Skip last brace.
            } else {  // Single character for the delimiter.
                delim = VIEW(string, (byte *)format, i, i + 1);
                ++i;
            }

            #define SPRINT_ELEM(TYPE) do { \
                newslice(TSlice, TYPE); \
                newarray(TArray, TYPE); \
                TSlice slice = { 0 }; \
                if (is_array_formatter) { \
                    TArray arr = va_arg(args, TArray); \
                    slice = SLICE(TSlice, arr, 0, -1); \
                } else { \
                    slice = va_arg(args, TSlice); \
                } \
                for (usize n = 0; n < slice.len; ++n) { \
                    TYPE *elem = slice.value + n; \
                    string elem_str = novel_sprintf(elem_repr.value, *elem); \
                    extend(&bytes, &elem_str, sizeof(byte)); \
                    FREE(elem_str.value); \
                    unless (n == slice.len - 1) \
                        extend(&bytes, &delim, sizeof(byte)); \
                } \
            } while (false)

            string fmt_l = elem_formatter.length;

            switch (elem_formatter.specifier) {
            // New formatters.
            case 'C': SPRINT_ELEM(rune); break;
            case 'U': SPRINT_ELEM(rune); break;
            case 'r': SPRINT_ELEM(runic); break;
            case 'S': SPRINT_ELEM(string); break;
            case 'D': SPRINT_ELEM(GenericArray); break;
            case 'V': SPRINT_ELEM(GenericSlice); break;
            // Standard C formatters:
            case 'i': case 'd':
                if (string_eq(fmt_l, STR("hh")))
                    SPRINT_ELEM(signed char);
                else if (string_eq(fmt_l, STR("ll")))
                    SPRINT_ELEM(long long int);
                else if (string_eq(fmt_l, STR("h")))
                    SPRINT_ELEM(short int);
                else if (string_eq(fmt_l, STR("l")))
                    SPRINT_ELEM(long int);
                else if (string_eq(fmt_l, STR("j")))
                    SPRINT_ELEM(imax);
                else if (string_eq(fmt_l, STR("z")))
                    SPRINT_ELEM(isize);
                else if (string_eq(fmt_l, STR("t")))
                    SPRINT_ELEM(iptr);
                else
                    SPRINT_ELEM(int);
                break;
            case 'o': case 'u': case 'x': case 'X':
                if (string_eq(fmt_l, STR("hh")))
                    SPRINT_ELEM(unsigned char);
                else if (string_eq(fmt_l, STR("ll")))
                    SPRINT_ELEM(unsigned long long int);
                else if (string_eq(fmt_l, STR("h")))
                    SPRINT_ELEM(unsigned short int);
                else if (string_eq(fmt_l, STR("l")))
                    SPRINT_ELEM(unsigned long int);
                else if (string_eq(fmt_l, STR("j")))
                    SPRINT_ELEM(umax);
                else if (string_eq(fmt_l, STR("z")))
                    SPRINT_ELEM(usize);
                else if (string_eq(fmt_l, STR("t")))
                    SPRINT_ELEM(uptr);
                else
                    SPRINT_ELEM(unsigned int);
                break;
            case 'c':
                if (string_eq(fmt_l, STR("l")))
                    SPRINT_ELEM(wint_t);
                else
                    SPRINT_ELEM(char);
                break;
            case 'e': case 'f': case 'g': case 'a':
            case 'E': case 'F': case 'G': case 'A':
                if (string_eq(fmt_l, STR("L")))
                    SPRINT_ELEM(long double);
                else
                    SPRINT_ELEM(double);
                break;
            case 's':
                if (string_eq(fmt_l, STR("l")))
                    SPRINT_ELEM(wchar_t *);
                else
                    SPRINT_ELEM(char *);
                break;
            case 'p':
                SPRINT_ELEM(uptr); break;
            case 'n':
                if (string_eq(fmt_l, STR("hh")))
                    SPRINT_ELEM(signed char *);
                else if (string_eq(fmt_l, STR("ll")))
                    SPRINT_ELEM(long long int *);
                else if (string_eq(fmt_l, STR("h")))
                    SPRINT_ELEM(short int *);
                else if (string_eq(fmt_l, STR("l")))
                    SPRINT_ELEM(long int *);
                else if (string_eq(fmt_l, STR("j")))
                    SPRINT_ELEM(imax *);
                else if (string_eq(fmt_l, STR("z")))
                    SPRINT_ELEM(usize *);
                else if (string_eq(fmt_l, STR("t")))
                    SPRINT_ELEM(iptr *);
                else
                    SPRINT_ELEM(int *);
                break;
            default:
                PANIC("Unknown formatter ('%%%c') in array/slice.",
                    elem_formatter.specifier);
                break;
            }

            FREE_INSIDE(elem_repr);
        } break;
        default: {
            // Send it off to libc `vsprintf`.
            string c_formatter = formatter_string(formatter);
            // We cannot pass in `va_list args` to the libc printf,
            // since it is technically undefined behaviour to use a
            // va_list after it's been passed by value into another
            // variadic function.  We must make a copy, and skip it.
            va_list args_copy;
            va_copy(args_copy, args);
            byte *buf;
            isize len = vasprintf(&buf, c_formatter.value, args_copy);
            va_end(args_copy);
            string buf_slice = VIEW(string, buf, 0, len);
            extend(&bytes, &buf_slice, sizeof(byte));
            FREE(buf);
            FREE(c_formatter.value);
            // Skip/discard the argument!
            switch (formatter.specifier) {
            case 'i': case 'd':
                // narrower types (h,hh) are promoted to int.
                if (string_eq(formatter.length, STR("ll")))
                    va_arg(args, long long int);
                else if (string_eq(formatter.length, STR("l")))
                    va_arg(args, long int);
                else if (string_eq(formatter.length, STR("j")))
                    va_arg(args, imax);
                else if (string_eq(formatter.length, STR("z")))
                    va_arg(args, isize);
                else if (string_eq(formatter.length, STR("t")))
                    va_arg(args, iptr);
                else
                    va_arg(args, int);
                break;
            case 'o': case 'u': case 'x': case 'X':
                // narrower types (h,hh) are promoted to int.
                if (string_eq(formatter.length, STR("ll")))
                    va_arg(args, unsigned long long int);
                else if (string_eq(formatter.length, STR("l")))
                    va_arg(args, unsigned long int);
                else if (string_eq(formatter.length, STR("j")))
                    va_arg(args, umax);
                else if (string_eq(formatter.length, STR("z")))
                    va_arg(args, usize);
                else if (string_eq(formatter.length, STR("t")))
                    va_arg(args, uptr);
                else
                    va_arg(args, unsigned int);
                break;
            case 'c':
                if (string_eq(formatter.length, STR("l")))
                    va_arg(args, wint_t);
                else
                    va_arg(args, int);  // char is promoted to int in va_list
                break;
            case 'e': case 'f': case 'g': case 'a':
            case 'E': case 'F': case 'G': case 'A':
                if (string_eq(formatter.length, STR("L")))
                    va_arg(args, long double);
                else
                    va_arg(args, double);
                break;
            case 's':
                if (string_eq(formatter.length, STR("l")))
                    va_arg(args, wchar_t *);
                else
                    va_arg(args, char *);
                break;
            case 'p':
                va_arg(args, uptr); break;
            case 'n':
                if (string_eq(formatter.length, STR("hh")))
                    va_arg(args, signed char *);
                else if (string_eq(formatter.length, STR("ll")))
                    va_arg(args, long long int *);
                else if (string_eq(formatter.length, STR("h")))
                    va_arg(args, short int *);
                else if (string_eq(formatter.length, STR("l")))
                    va_arg(args, long int *);
                else if (string_eq(formatter.length, STR("j")))
                    va_arg(args, imax *);
                else if (string_eq(formatter.length, STR("z")))
                    va_arg(args, usize *);
                else if (string_eq(formatter.length, STR("t")))
                    va_arg(args, iptr *);
                else
                    va_arg(args, int *);
                break;
            default:
                panic("unknown specifier: %%%s", formatter.specifier);
            }
        } break;
        }
    }

    // NUL-terminate the string slice.
    push(&bytes, &NUL_BYTE, sizeof(byte));
    --bytes.len;  //< But, don't count the NUL-byte as part of the length.
    return SLICE(string, bytes, 0, -1);
}

string novel_sprintf(const byte *format, ...)
{
    va_list args;
    va_start(args, format);
    string res = novel_vsprintf(format, args);
    va_end(args);
    return res;
}

ierr novel_vfprintf(FILE *stream, const byte *format, va_list args)
{
    string s = novel_vsprintf(format, args);
    ierr res = fputs(s.value, stream);
    FREE(s.value);
    return res;
}

ierr novel_fprintf(FILE *stream, const byte *format, ...)
{
    va_list args;
    va_start(args, format);
    ierr res = novel_vfprintf(stream, format, args);
    va_end(args);
    return res;
}

ierr novel_vfprintf_newline(FILE *stream, const byte *format, va_list args)
{
    ierr res = novel_vfprintf(stream, format, args);
    if (res < 0) return res;
    if (EOF == fputc('\n', stream))
        return EOF;
    return res + 1;
}

ierr novel_fprintf_newline(FILE *stream, const byte *format, ...)
{
    va_list args;
    va_start(args, format);
    ierr res = novel_vfprintf_newline(stream, format, args);
    va_end(args);
    return res;
}

ierr novel_printf(const byte *format, ...)
{
    va_list args;
    va_start(args, format);
    ierr res = novel_vfprintf(stdout, format, args);
    va_end(args);
    return res;
}

#endif

Updated on 23 August 2022 at 00:54:19 UTC


title: src/crelude/io.h


src/crelude/io.h

More...

Functions

Name
ierrfput(string , FILE * )
fputs(...) with string.
ierrput(string )
Same as fput(..., stdout).
ierreput(string )
Same as fput(..., stderr).
ierreputs(const byte * )
puts(...) to STDERR.
stringnovel_vsprintf(const byte * , va_list )
stringnovel_sprintf(const byte * , ... )
ierrnovel_vfprintf(FILE * , const byte * , va_list )
ierrnovel_fprintf(FILE * , const byte * , ... )
ierrnovel_vfprintf_newline(FILE * , const byte * , va_list )
ierrnovel_fprintf_newline(FILE * , const byte * , ... )
ierrnovel_printf(const byte * , ... )
usizesizeof_specifier(const byte * )

Defines

Name
PANIC(lit, ...)
print(...)
sprint
println(...)
eprintf(...)
eprint(...)
eprintln(...)
fprint

Detailed Description

New printf style functions and aliases.

Functions Documentation

function fput

ierr fput(
    string ,
    FILE * 
)

fputs(...) with string.

function put

ierr put(
    string 
)

Same as fput(..., stdout).

function eput

ierr eput(
    string 
)

Same as fput(..., stderr).

function eputs

ierr eputs(
    const byte * 
)

puts(...) to STDERR.

function novel_vsprintf

string novel_vsprintf(
    const byte * ,
    va_list 
)

Note: Heap allocates memory, should be freed after printing.

Custom printf for other data-types.

function novel_sprintf

string novel_sprintf(
    const byte * ,
    ... 
)

Note: Returns heap-allocated memory, should be freed.

function novel_vfprintf

ierr novel_vfprintf(
    FILE * ,
    const byte * ,
    va_list 
)

function novel_fprintf

ierr novel_fprintf(
    FILE * ,
    const byte * ,
    ... 
)

function novel_vfprintf_newline

ierr novel_vfprintf_newline(
    FILE * ,
    const byte * ,
    va_list 
)

function novel_fprintf_newline

ierr novel_fprintf_newline(
    FILE * ,
    const byte * ,
    ... 
)

function novel_printf

ierr novel_printf(
    const byte * ,
    ... 
)

function sizeof_specifier

usize sizeof_specifier(
    const byte * 
)

Size of the type of a printf-style format specifer. e.g. sizeof_specifier("hx") == sizeof(unsigned short int);.

Macros Documentation

define PANIC

#define PANIC(
    lit,
    ...
)
[panic](/crelude/Files/common_8h.md#function-panic)("\n[**] Panicking!\n[**] CAUSE:\n -- \t%s(): " \
	      lit "\n[**] Aborting...\n", __func__, ## __VA_ARGS__)

define print

#define print(
    ...
)
[novel_fprintf](/crelude/Files/io_8h.md#function-novel_fprintf)(stdout, __VA_ARGS__)

define sprint

#define sprint [novel_sprintf](/crelude/Files/io_8h.md#function-novel_sprintf)

define println

#define println(
    ...
)
[novel_fprintf_newline](/crelude/Files/io_8h.md#function-novel_fprintf_newline)(stdout, __VA_ARGS__)

define eprintf

#define eprintf(
    ...
)
[novel_fprintf](/crelude/Files/io_8h.md#function-novel_fprintf)(stderr, __VA_ARGS__)

define eprint

#define eprint(
    ...
)
[eprintf](/crelude/Files/io_8h.md#define-eprintf)(__VA_ARGS__)

define eprintln

#define eprintln(
    ...
)
[novel_fprintf_newline](/crelude/Files/io_8h.md#function-novel_fprintf_newline)(stderr, __VA_ARGS__)

define fprint

#define fprint [novel_fprintf](/crelude/Files/io_8h.md#function-novel_fprintf)

Source code


#pragma once
#include "common.h"

#define PANIC(lit, ...) \
    panic("\n[**] Panicking!\n[**] CAUSE:\n -- \t%s(): " \
          lit "\n[**] Aborting...\n", __func__, ## __VA_ARGS__)

#define print(...) novel_fprintf(stdout, __VA_ARGS__)
#define sprint novel_sprintf
#define println(...) novel_fprintf_newline(stdout, __VA_ARGS__)
#define eprintf(...) novel_fprintf(stderr, __VA_ARGS__)
#define eprint(...) eprintf(__VA_ARGS__)
#define eprintln(...) novel_fprintf_newline(stderr, __VA_ARGS__)
#define fprint novel_fprintf

extern ierr fput(string, FILE *);
extern ierr put(string);
extern ierr eput(string);
extern ierr eputs(const byte *);

extern string novel_vsprintf(const byte *, va_list);
extern string novel_sprintf(const byte *, ...);
extern ierr novel_vfprintf(FILE *, const byte *, va_list);
extern ierr novel_fprintf(FILE *, const byte *, ...);
extern ierr novel_vfprintf_newline(FILE *, const byte *, va_list);
extern ierr novel_fprintf_newline(FILE *, const byte *, ...);
extern ierr novel_printf(const byte *, ...);

extern usize sizeof_specifier(const byte *);

Updated on 23 August 2022 at 00:54:19 UTC


title: src/crelude/log.h


src/crelude/log.h

More...

Attributes

Name
const ifast_verbosity

Defines

Detailed Description

Logging given verbosity. You must define a VERBOSE_VAR before including this header. Otherwise, the default value is 1, i.e. only errors & warnings.

Attributes Documentation

variable _verbosity

static const ifast _verbosity = 1;

Macros Documentation

define VERBOSE_VAR

#define VERBOSE_VAR [_verbosity](/crelude/Files/log_8h.md#variable-_verbosity)

define ERROR

#define ERROR 0

define WARN

#define WARN 1

define DEBUG

#define DEBUG 2

define INFO

#define INFO 3

define S_ERROR

#define S_ERROR "  error"

define S_WARN

#define S_WARN "warning"

define S_DEBUG

#define S_DEBUG "  debug"

define S_INFO

#define S_INFO "   info"

define LOG

#define LOG(
    LEVEL,
    FORMATTER,
    ...
)
	__extension__\
	({ if ([VERBOSE_VAR](/crelude/Files/log_8h.md#define-verbose_var) >= LEVEL) \
		   [eprintln](/crelude/Files/io_8h.md#define-eprintln)("[ " S_##LEVEL " ] log<%s()>: " FORMATTER, __func__, ##__VA_ARGS__); \
	   LEVEL; })

Source code


#pragma once
#include "io.h"

#ifndef VERBOSE_VAR
    #define VERBOSE_VAR _verbosity
    static const ifast _verbosity = 1;
#endif

#define ERROR 0
#define WARN  1
#define DEBUG 2
#define INFO  3

#define S_ERROR "  error"
#define S_WARN  "warning"
#define S_DEBUG "  debug"
#define S_INFO  "   info"

#define LOG(LEVEL, FORMATTER, ...) __extension__\
    ({ if (VERBOSE_VAR >= LEVEL) \
           eprintln("[ " S_##LEVEL " ] log<%s()>: " FORMATTER, __func__, ##__VA_ARGS__); \
       LEVEL; })

Updated on 23 August 2022 at 00:54:19 UTC


title: src/crelude/utf.c


src/crelude/utf.c

Attributes

Attributes Documentation

variable OffsetsFromUTF8

static const rune OffsetsFromUTF8 = {
    0x00000000UL, 0x00003080UL, 0x000E2080UL,
    0x03C82080UL, 0xFA082080UL, 0x82082080UL
};

variable TrailingBytesForUTF8

static const u8 TrailingBytesForUTF8 = {
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
    2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
};

Source code

#include "utf.h"

static const rune OffsetsFromUTF8[6] = {
    0x00000000UL, 0x00003080UL, 0x000E2080UL,
    0x03C82080UL, 0xFA082080UL, 0x82082080UL
};

static const u8 TrailingBytesForUTF8[256] = {
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
    2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
};

#ifndef IMPLEMENTATION

/* Conversions. */

usize utf_seqlen(string s)
{
    return TrailingBytesForUTF8[(usize)(*s.value)] + 1;
}

runic utf8_to_ucs4(runic dest, string src)
{
    byte *src_end = src.value + src.len;
    byte *sptr = src.value;

    usize i;
    for (i = 0; i < dest.len - 1; ++i) {
        u8 nb = TrailingBytesForUTF8[(usize)(*sptr)];
        if (sptr + nb >= src_end)
            break;

        rune ch = 0;
        switch (nb) {
            case 3: ch += *sptr++; ch <<= 6; /* fallthrough */
            case 2: ch += *sptr++; ch <<= 6; /* fallthrough */
            case 1: ch += *sptr++; ch <<= 6; /* fallthrough */
            case 0: ch += *sptr++;
        }
        ch -= OffsetsFromUTF8[(usize)nb];
        dest.value[i] = ch;
    }

    dest.value[i] = 0; // Always NUL-terminate, despite known length.
    return SLICE(runic, dest, 0, i);
}

string ucs4_to_utf8(string dest, runic src)
{
    byte *dest_end = dest.value + dest.len;
    byte *dptr = dest.value;

    for (usize i = 0; i < src.len; ++i) {
        rune ch = src.value[i];

        if (ch < 0x80) {
            if (dptr >= dest_end)
                break;
            *dptr++ = (byte)ch;
        } else if (ch < 0x800) {
            if (dptr >= dest_end - 1)
                break;
            *dptr++ = (ch >> 6) | 0xC0;
            *dptr++ = (ch & 0x3F) | 0x80;
        } else if (ch < 0x10000) {
            if (dptr >= dest_end - 2)
                break;
            *dptr++ = (ch >> 12) | 0xE0;
            *dptr++ = ((ch >> 6) & 0x3F) | 0x80;
            *dptr++ = (ch & 0x3F) | 0x80;
        } else if (ch < 0x110000) {
            if (dptr >= dest_end - 3)
                break;
            *dptr++ = (ch >> 18) | 0xF0;
            *dptr++ = ((ch >> 12) & 0x3F) | 0x80;
            *dptr++ = ((ch >> 6) & 0x3F) | 0x80;
            *dptr++ = (ch & 0x3F) | 0x80;
        }
    }

    if (dptr < dest_end)
        *dptr = '\0';
    return SLICE(string, dest, 0, dptr - dest.value);
}

string rune_to_utf8(string dest, rune ch)
{
    byte *const dptr = dest.value;
    zero(dptr, dest.len * sizeof(byte));

    if (ch < 0x80) {
        dptr[0] = (byte)ch;
        return SLICE(string, dest, 0, 1);
    }
    if (ch < 0x800) {
        dptr[0] = (ch >> 6) | 0xC0;
        dptr[1] = (ch & 0x3F) | 0x80;
        return SLICE(string, dest, 0, 2);
    }
    if (ch < 0x10000) {
        dptr[0] = (ch >> 12) | 0xE0;
        dptr[1] = ((ch >> 6) & 0x3F) | 0x80;
        dptr[2] = (ch & 0x3F) | 0x80;
        return SLICE(string, dest, 0, 3);
    }
    if (ch < 0x110000) {
        dptr[0] = (ch >> 18) | 0xF0;
        dptr[1] = ((ch >> 12) & 0x3F) | 0x80;
        dptr[2] = ((ch >> 6) & 0x3F) | 0x80;
        dptr[3] = (ch & 0x3F) | 0x80;
        return SLICE(string, dest, 0, 4);
    }
    return SLICE(string, dest, 0, 0);
}

/* Moving through stirngs. */

usize byte_offset(string s, usize cn)
{
    usize offs = 0;

    while (cn-- > 0 && offs < s.len)
        UNUSED(is_utf(s.value[++offs]) || is_utf(s.value[++offs])
            || is_utf(s.value[++offs]) || ++offs);

    return offs;
}

usize char_num(string s, usize offset)
{
    usize cn = 0, offs = 0;

    while (offs < offset && offs < s.len) {
        UNUSED(is_utf(s.value[++offs]) || is_utf(s.value[++offs])
            || is_utf(s.value[++offs]) || ++offs);
        ++cn;
    }

    return cn;
}

rune read_rune(string s, usize *i)
{
    if (*i >= s.len) return 0;

    rune ch = 0;
    usize len = 0;

    do {
        ch <<= 6;
        ch += s.value[(*i)++];
        ++len;
    } while (*i < s.len && !is_utf(s.value[*i]));
    ch -= OffsetsFromUTF8[len - 1];

    return ch;
}

u0 next_rune(string str, usize *i)
{
    byte *s = str.value;
    UNUSED(is_utf(s[++(*i)]) || is_utf(s[++(*i)])
        || is_utf(s[++(*i)]) || ++(*i));
}

u0 prev_rune(string str, usize *i)
{
    byte *s = str.value;
    UNUSED(is_utf(s[--(*i)]) || is_utf(s[--(*i)])
        || is_utf(s[--(*i)]) || --(*i));
}

/* Unicode Escapes. */

usize read_escape(string src, rune *dest)
{
    rune ch;
    byte *sptr = src.value;
    byte digs[8 + 1] = "\0\0\0\0\0\0\0\0";
    usize dno = 0, i = 1;

    ch = (rune)sptr[0];  // Take the literal character.

    switch (sptr[0]) {
    case 'n': { ch = U'\n'; } break;
    case 't': { ch = U'\t'; } break;
    case 'r': { ch = U'\r'; } break;
    case 'b': { ch = U'\b'; } break;
    case 'f': { ch = U'\f'; } break;
    case 'v': { ch = U'\v'; } break;
    case 'a': { ch = U'\a'; } break;
    case 'x':  // e.g. \x41 = 'A'
        while (is_hex_digit(sptr[i]) && dno < 2)
            digs[dno++] = sptr[i++];
        break;
    case 'u':  // e.g. \u6CB3 = '河'
        while (is_hex_digit(sptr[i]) && dno < 4)
            digs[dno++] = sptr[i++];
        break;
    case 'U':  // e.g. \U0001F920 = '🤠'
        while (is_hex_digit(sptr[i]) && dno < 8)
            digs[dno++] = sptr[i++];
        break;
    default:
        break;
    }

    if (dno > 0)
        ch = strtol(digs, nil, 16);

    *dest = ch;
    return i;
}

string escape_rune(string dest, rune ch)
{
    byte *buf = dest.value;
    usize len = dest.len;

    switch (ch) {
        case U'\n': return SLICE(string, dest, 0, snprintf(buf, len, "\\n"));
        case U'\t': return SLICE(string, dest, 0, snprintf(buf, len, "\\t"));
        case U'\r': return SLICE(string, dest, 0, snprintf(buf, len, "\\r"));
        case U'\b': return SLICE(string, dest, 0, snprintf(buf, len, "\\b"));
        case U'\f': return SLICE(string, dest, 0, snprintf(buf, len, "\\f"));
        case U'\v': return SLICE(string, dest, 0, snprintf(buf, len, "\\v"));
        case U'\a': return SLICE(string, dest, 0, snprintf(buf, len, "\\a"));
        case U'\\': return SLICE(string, dest, 0, snprintf(buf, len, "\\\\"));
        default: break;
    }

    if (ch < 32 || ch == 0x7F)
        return SLICE(string, dest, 0, snprintf(buf, len, "\\x%hhX", (u8)ch));
    else if (ch > 0xFFFF)
        return SLICE(string, dest, 0, snprintf(buf, len, "\\U%X",  (u32)ch));
    else if (ch >= 0x80 && ch <= 0xFFFF)
        return SLICE(string, dest, 0, snprintf(buf, len, "\\u%hX", (u16)ch));

    return SLICE(string, dest, 0, snprintf(buf, len, "%c", (byte)ch));
}

string utf8_unescape(string dest, string src)
{
    rune ch;
    byte buf[4];
    string temp = { .len = 4, .value = buf };
    usize c = 0, i = 0, amt;

    while (i < src.len && c < dest.len) {
        if (src.value[i] == '\\') {
            ++i;
            amt = read_escape(SLICE(string, src, i, -1), &ch);
        } else {
            ch = (rune)(src.value[i]);
            amt = 1;
        }

        i += amt;
        temp = rune_to_utf8(temp, ch);
        amt = temp.len;

        if (amt > dest.len - c)
            break;

        memcpy(dest.value + c, temp.value, amt);
        c += amt;
    }

    if (c < dest.len)  // Try to NUL-terminate.
        dest.value[c] = '\0';

    return SLICE(string, dest, 0, c);
}


string utf8_escape(string dest, string src, bool escape_quotes)
{
    byte *buf = dest.value;
    usize c = 0, i = 0;

    while (c < dest.len && i < src.len) {
        string slice = { .value = buf, .len = dest.len - c };

        if (escape_quotes && src.value[i++] == '"')
            slice = SLICE(string, slice, 0, snprintf(buf, slice.len, "\\\""));
        else
            slice = escape_rune(slice, read_rune(src, &i));

        c   += slice.len;
        buf += slice.len;
    }

    if (c < dest.len)
        *buf = '\0';

    return SLICE(string, dest, 0, c);
}

/* Standard function replacements. */

string utf_strchr(string s, rune ch, usize *i)
{
    usize offs = 0, last_offs = 0;
    rune c;

    *i = 0;
    until (offs >= s.len) {
        c = read_rune(s, &offs);
        if (c == ch)
            return SLICE(string, s, last_offs, -1);

        last_offs = offs;
        ++(*i);
    }

    return EMPTY(string);
}

usize utf_strlen(string s)
{
    usize i = 0, count = 0;

    until (read_rune(s, &i) == 0)
        ++count;

    return count;
}


bool is_locale_utf8(byte *locale)
{
    const byte *cp = locale;

    for (; *cp != '\0' && *cp != '@' && *cp != '+' && *cp != ','; cp++) {
        if (*cp == '.') {
            const byte *encoding = ++cp;
            for (; *cp != '\0' && *cp != '@' && *cp != '+' && *cp != ','; cp++)
                NOOP;
            if ((cp - encoding == 5 && !strncmp(encoding, "UTF-8", 5))
             || (cp - encoding == 4 && !strncmp(encoding, "utf8", 4)))
                return true;  // It's UTF-8.
            break;
        }
    }

    return false;  // :(
}

#endif

Updated on 23 August 2022 at 00:54:19 UTC


title: src/crelude/utf.h


src/crelude/utf.h

Functions

Name
stringto_string(const byte * c)
runicutf8_to_ucs4(runic dest, string src)
stringucs4_to_utf8(string dest, runic src)
stringrune_to_utf8(string dest, rune ch)
usizebyte_offset(string , usize )
usizechar_num(string , usize )
runeread_rune(string s, usize * i)
u0next_rune(string , usize * )
Update byte-index to move next rune, skipping it.
u0prev_rune(string , usize * )
Update byte-index to move to previous rune.
usizeread_escape(string src, rune * dest)
stringescape_rune(string dest, rune ch)
stringutf8_unescape(string dest, string src)
stringutf8_escape(string dest, string src, bool escape_quotes)
stringutf_strchr(string s, rune ch, usize * i)
usizeutf_strlen(string s)
usizeutf_seqlen(string )
Returns length of next UTF-8 sequence.
boolis_locale_utf8(byte * locale)
boolis_octal_digit(byte c)
boolis_hex_digit(byte c)

Defines

Name
is_utf(c)
Is c the start of a UTF-8 sequence?

Functions Documentation

function to_string

static inline string to_string(
    const byte * c
)

Parameters:

  • c NUL-terminated C-string to be wrapped

Return: UTF-8 string slice.

Wrap C-string to internal string slice. Length does not count the NUL-terminator.

function utf8_to_ucs4

runic utf8_to_ucs4(
    runic dest,
    string src
)

Parameters:

  • dest Empty runic structure.
  • src UTF-8 encoded string.

Return: Slice of converted runic dest with correct length.

Convert UTF-8 to UCS-4 (4-byte wide characters) No error checking is done, must be valid UTF-8 and dest must be large enough. If dest.len >= src.len + 1, then there will always be enough space.

function ucs4_to_utf8

string ucs4_to_utf8(
    string dest,
    runic src
)

Parameters:

  • dest Empty string structure.
  • src UCS-4/UTF-32 encoded string.

Return: Slice of dest with converted UTF-8 bytes with correct length.

Convert UCS-4 to UTF-8, will try to NUL-terminate, if there is space. No error checking is done. dest.len >= 4 * src.len + 4 To ensure a fit. Ensure enough space in dest string.

function rune_to_utf8

string rune_to_utf8(
    string dest,
    rune ch
)

Parameters:

  • dest Empty string structure to hold UTF-8 bytes.
  • ch Single UCS-4 character / rune.

Return: Slice of dest with correct length.

Single UCS-4 rune to UTF-8 string. dest should allocate 4 bytes, or 5 if it is desired to NUL-terminate.

function byte_offset

usize byte_offset(
    string ,
    usize 
)

Character number to byte offset. Given the n-th character/rune in a string, how many bytes is that from the start of a UTF-8 string.

function char_num

usize char_num(
    string ,
    usize 
)

Byte offset to character number. Given a byte-offset from the start of a UTF-8 string, return how many characters/runes precede it.

function read_rune

rune read_rune(
    string s,
    usize * i
)

Parameters:

  • s String to step through.
  • i Index with value of current position in the string, updating to the next start-of-character byte.

Return: The character (rune).

Step through a string, one rune/character at a time. Given the previous index, and updating the index to the begining of the next character.

function next_rune

u0 next_rune(
    string ,
    usize * 
)

Update byte-index to move next rune, skipping it.

function prev_rune

u0 prev_rune(
    string ,
    usize * 
)

Update byte-index to move to previous rune.

function read_escape

usize read_escape(
    string src,
    rune * dest
)

Parameters:

  • src String pointing to char after backslash.
  • dest Pointer to location where resulting rune is to be stored.

Return: How many bytes read as part of parsing (including u/U). Returns 0 if escape is invalid.

Take an unescaped UTF-8 string, where the start of the string is pointing to the character right after the backslash. If this character is a U, then max eight (8) hexadecimal digits are expected to succeed it, otherwise a u is expected, where max four (4) hexdecimal digits are to succeed it. The resulting rune is stored in dest.

function escape_rune

string escape_rune(
    string dest,
    rune ch
)

Parameters:

  • dest Empty string, should be large enough for minimum 4 bytes, plus 1 byte for the NUL-terminator.
  • ch The UCS-4 rune to convert from.

Return: Slice of dest with correct length.

Given a rune, convert it to an ASCII escape sequence.

function utf8_unescape

string utf8_unescape(
    string dest,
    string src
)

Parameters:

  • dest Empty string structure.
  • src String containg escapes.

Return: Slice of dest with correct length.

Convert a string containing ASCII escape sequences to a proper UTF-8 string.

function utf8_escape

string utf8_escape(
    string dest,
    string src,
    bool escape_quotes
)

Parameters:

  • dest Empty string structure.
  • src String containg valid UTF-8.
  • escape_quotes If true, quotation-marks will have backslashes prepended too.

Return: Slice of dest string with correct length.

Convert a string containing UTF-8 to ASCII with escape sequences.

function utf_strchr

string utf_strchr(
    string s,
    rune ch,
    usize * i
)

Parameters:

  • s String to search through.
  • ch Rune/character to find.
  • i Pointer to be set to index of character in string.

Return: Slice of s string, starting at first occurence. Points to nil with zero (0) length if no such character is found.

Find first occurrence of character ch in string c.

function utf_strlen

usize utf_strlen(
    string s
)

Counts number of characters (runes) in a UTF-8 string. Not the number of bytes, which is s.len.

function utf_seqlen

usize utf_seqlen(
    string 
)

Returns length of next UTF-8 sequence.

function is_locale_utf8

bool is_locale_utf8(
    byte * locale
)

Give the C-string returned by setlocale, determine whether the current locale speaks UTF-8.

function is_octal_digit

static inline bool is_octal_digit(
    byte c
)

function is_hex_digit

static inline bool is_hex_digit(
    byte c
)

Macros Documentation

define is_utf

#define is_utf(
    c
)
(((c) & 0xC0) != 0x80)

Is c the start of a UTF-8 sequence?

Source code

#include "common.h"
#include <string.h>

#pragma once

#define is_utf(c) (((c) & 0xC0) != 0x80)

static inline
string to_string(const byte *c)
    { return ((string){ .len = strlen(c), .value = (byte *)c }); }

/* Conversions. */

runic utf8_to_ucs4(runic dest, string src);

string ucs4_to_utf8(string dest, runic src);

string rune_to_utf8(string dest, rune ch);

/* Moving through stirngs. */

usize byte_offset(string, usize);

usize char_num(string, usize);

rune read_rune(string s, usize *i);

u0 next_rune(string, usize *);

u0 prev_rune(string, usize *);

/* Unicode Escapes. */

usize read_escape(string src, rune *dest);

string escape_rune(string dest, rune ch);

string utf8_unescape(string dest, string src);

string utf8_escape(string dest, string src, bool escape_quotes);

/* UTF-8 oriented standard function replacements. */

string utf_strchr(string s, rune ch, usize *i);

usize utf_strlen(string s);

usize utf_seqlen(string);

bool is_locale_utf8(byte *locale);

/* Utility. */
static inline bool is_octal_digit(byte c)
{
    return (c >= '0' && c <= '7');
}

static inline bool is_hex_digit(byte c)
{
    return ((c >= '0' && c <= '9')
         || (c >= 'A' && c <= 'F')
         || (c >= 'a' && c <= 'f'));
}

Updated on 23 August 2022 at 00:54:19 UTC


title: src/tests.c


src/tests.c

More...

Classes

Name
struct_Natural

Types

Name
typedef struct _NaturalNatural

Functions

Name
u0utf8_ucs4_conversions(byte * cstring)

Defines

Name
TEST(DOES)

Detailed Description

File for testing the library. Not part of the library.

Types Documentation

typedef Natural

typedef struct _Natural Natural;

Functions Documentation

function utf8_ucs4_conversions

u0 utf8_ucs4_conversions(
    byte * cstring
)

Macros Documentation

define TEST

#define TEST(
    DOES
)
	do { \
	[println](/crelude/Files/io_8h.md#define-println)("\n" ANSI([BOLD](/crelude/Files/common_8h.md#define-bold)) "[###]" [ANSI](/crelude/Files/common_8h.md#define-ansi)([RESET](/crelude/Files/common_8h.md#define-reset)) " "\
		[ANSI](/crelude/Files/common_8h.md#define-ansi)([UNDER](/crelude/Files/common_8h.md#define-under)) "Test Case" ANSI([RESET](/crelude/Files/common_8h.md#define-reset)) ": %s", DOES); \
} while (false); [always](/crelude/Files/common_8h.md#define-always)

Source code


#include <crelude/common.h>
#include <crelude/io.h>
#include <crelude/utf.h>
#include <crelude/base64.h>
#include <crelude/argparse.h>

#include <stdio.h>
#include <locale.h>

#define TEST(DOES) do { \
    println("\n" ANSI(BOLD) "[###]" ANSI(RESET) " "\
        ANSI(UNDER) "Test Case" ANSI(RESET) ": %s", DOES); \
} while (false); always

u0 utf8_ucs4_conversions(byte *cstring)
{
    // UTF-8 string.
    string s = to_string(cstring);
    println("s = \"%s\"", UNWRAP(s));
    FOR_EACH(c, s) println("byte: 0x%X", c);
    println("s.len = %zu", s.len);
    // Convert to UCS-4.
    println("[***] Convert to UCS-4.");
    runic ucs4 = SMAKE(rune, s.len + 1);
    ucs4 = utf8_to_ucs4(ucs4, s);
    println("ucs4 = \"%ls\"", (wchar_t *)UNWRAP(ucs4));
    FOR_EACH(c, ucs4) println("rune: U+%08X = '%lc'", c, c);
    println("ucs4.len = %zu", ucs4.len);
    // And back again.
    println("[***] Convert to UTF-8.");
    string utf8 = SMAKE(byte, 4 * ucs4.len + 4);
    utf8 = ucs4_to_utf8(utf8, ucs4);
    println("utf8 = \"%s\"", UNWRAP(utf8));
    FOR_EACH(c, utf8) println("byte: 0x%X", c);
    println("utf8.len = %zu", utf8.len);
}

newtype(Natural, u64);  // New-type idiom.

#ifndef IMPLEMENTATION

ierr main(i32 argc, const byte **argv)
{
    UNUSED(argc); UNUSED(argv);
    byte *locale; UNUSED(locale);
    locale = setlocale(LC_ALL, "");
    println("Locale is UTF-8?  %s.",
        is_locale_utf8(locale) ? "true" : "false");

    Natural n = { 7 };  // How to use newtypes.
    n.value = 4;  UNUSED(n);

    // --  UTF-8 <--> UCS-4, conversions. -- //
    TEST("Converts between UTF-8 and UCS-4.") {
        println("=== Chinese Characters ===");
        utf8_ucs4_conversions("你好");
        println("\n=== Latin-1/ASCII Characters ===");
        utf8_ucs4_conversions("AaBb");
        println("\n=== Look-alike Characters ===");
        utf8_ucs4_conversions("maña");  // U+00F1 = ñ.
        utf8_ucs4_conversions("maña");  // n + U+0303 = ñ.
        // The second string has one extra 'code-point', but both
        // have the exact same number of human distinguishable 'graphemes'.
    }

    // -- UTF-8 Escapes -- //
    TEST("Unescaped ASCII/UTF-8 strings to UTF-8") {
        println("=== Unescaping Strings ===");
        string unescaped = SMAKE(byte, 128);
        string escaped = STRING("Hello, \\U1F30E.");
        println("escaped.len = %zu", escaped.len);
        unescaped = utf8_unescape(unescaped, escaped);
        println("unescaped.len = %zu", unescaped.len);
        println("\"%s\" --> \"%s\"", escaped.value, unescaped.value);
    }

    TEST("Escaped UTF-8 strings to escaped ASCII") {
        println("=== Escaped Strings ===");
        string escaped = SMAKE(byte, 128);
        string unescaped = STRING("Hello, 🌎.");
        println("unescaped.len = %zu", unescaped.len);
        escaped = utf8_escape(escaped, unescaped, false);
        println("escaped.len = %zu", escaped.len);
        println("\"%s\" --> \"%s\"", unescaped.value, escaped.value);
    }

    TEST("The new (internal) printf variant function") {
        rune c = U'𰻝';
        println("Code point for '%C' = %U.", c, c);

        runic phrase = INIT(rune, { 0x73AB, 0x7470, 0x6C34 });
        println("The runes { %V{U+%04X}{, } }, represent: \"%r\".", phrase, phrase);
        println("Which are the three graphemes: %V{'%C'}{, }.", phrase);

        long long int some_ll_int = 36;
        println("A normal base-10 long long integer: %05lld.", some_ll_int);

        StringBuilder builder = AMAKE(byte, 5);

        extend(&builder, &STR("Hello"), sizeof(byte));
        push(&builder, ",", sizeof(byte));
        string name = STRING("Bob.");
        extend(&builder, &name, sizeof(byte));
        assert('.' == *(byte *)pop(&builder, sizeof(byte)));
        push(&builder, "!", sizeof(byte));
        insert(&builder, 6, " ", sizeof(byte));

        string slice = SLICE(string, builder, 0, -1);
        println("The characters %D{(%c)}-, say: \"%S\".", builder, slice);
        string slice_with_nul = SLICE(string, builder, 0, 12);
        println("With ASCII values: [%V{#%02hhX}{, }].", slice_with_nul);

        string alt = STRING("Strings may be printed like this too.\n");
        print("%Vc{}", alt);
        println("(that is, %V02hhX-)", alt);  // No curly-braces needed if unambiguous!
    }

    TEST("String comparison: string_cmp vs. strcmp") {
        string s0 = STRING("Latino");
        string s1 = STRING("Latina");
        byte *p0 = UNWRAP(s0);
        byte *p1 = UNWRAP(s1);

        println("string_cmp: %s <~> %s = %hd", p0, p1, string_cmp(s0, s1));
        println("strcmp:     %s <~> %s = %d",  p0, p1, strcmp(p0, p1));
        assert(string_cmp(s0, s1) == strcmp(p0, p1));

        string s2 = SLICE(string, s0, 0, -2);
        string s3 = SLICE(string, s1, 0, -2);
        println("string_cmp: %S <~> %S = %hd", s2, s3, string_cmp(s2, s3));

        println("string_cmp: %S <~> %S = %hd", s3, s0, string_cmp(s3, s0));
        println("string_cmp: %S <~> %S = %hd", s2, s0, string_cmp(s2, s0));
        p1[s1.len - 1] = '\0';
        println("strcmp:     %s <~> %s = %d", p1, p0, strcmp(p1, p0));
    }

    TEST("Array removal and block swapping") {
        newarray(IntArray, int);
        IntArray arr = AMAKE(int, 5);
        PUSH(arr, 0);
        __auto_type list = LIST(sliceof(int), { 1, 2, 3, 4, 5, 6 });
        __auto_type more = LIST(sliceof(int), { 7, 8, 9 });
        UNSHIFT(arr, -1);
        println("list[..%zu] = { %V%d{, } };", list.len, list);
        println("more[..%zu] = { %V%d{, } };", more.len, more);
        EXTEND(arr, list);
        EXTEND(arr, more);
        println("arr = %D%d{, };", arr);
        int *popped = SHIFT(arr);
        println("arr = %D%d{, };  (popped: %d)", arr, *popped);
        SWAP(arr, 3);  // Swap the three first elements to the back.
        println("arr = %D%d{, };  (swapped 3 first elements around)", arr);
        int *rmvd = REMOVE(arr, 7);  // Remove 8th element (0).
        println("arr = %D%d{, };  (removed at index %zu, element: %d)", arr, 7, *rmvd);
        SWAP(arr, 7);
        println("arr = %D%d{, };  (swapped at index 7)", arr);
        sliceof(int) *cutout = CUT(arr, 2, 4);
        println("arr = %D%d{, };  (cut out: %V%d{, })", arr, *cutout);
    }

    TEST("Base64 encoding and decoding.") {
        sliceof(u16) data = INIT(u16, {
            __extension__ 0b0000000000000000,  //<      0 (0x00 0x00).
            __extension__ 0b0011001000110010,  //< 12,850 (0x32 0x32).
            __extension__ 0b0000011000000110,  //<  1,542 (0x06 0x06).
            __extension__ 0b0111010101110101   //< 30,069 (0x75 0x75).
        }); //< 64-bit integer value = 55,190,430,840,181 (big endian).

        MemSlice bytes = TO_BYTES(data);
        println("{ %V%hu{, } } <=> { %V{0x%02hhX}{, } }", data, bytes);

        MemSlice repr = bytes;
        if (is_little_endian())  // Convert to big endian byte order.
            repr = reverse_endianness(repr);
        println("  (decimal: %lu)", *(u64 *)PTR(repr));

        MemSlice encoded = base64_encode(bytes);
        println("encodes to: %V%c{}", encoded);

        if (is_little_endian())
                FREE_INSIDE(repr);

        MemSlice decoded = base64_decode(encoded);
        repr = decoded;
        if (is_little_endian())
            repr = reverse_endianness(repr);

        println("decodes to: %lu", *(u64 *)PTR(repr));

        FREE_INSIDE(encoded);
        if (is_little_endian())
            FREE_INSIDE(repr);

        string str = STRING("Hello, World!");
        println("\"%S\" <=> { %V{0x%hhX}{, } }", str, str);
        encoded = base64_encode(TO_BYTES(str));
        println("encodes to: %V%c{}", encoded);

        decoded = base64_decode(encoded);
        println("decodes to: \"%V%c{}\".", decoded);

        FREE_INSIDE(encoded);
        FREE_INSIDE(decoded);
    }

    TEST("Maps / Hash Tables.") {
        //  key type, value type,             bucket capacity.
        //      v       v                           v
        mapof(byte *, i32) map = MMAKE(byte *, i32, 9);
        // ^ Initial bucket size (9) is an inflated guess of how
        //   many entries we might expect the hash-map to have.
        // Hash-function can be changed.
        // Important, since different types are hashed differently.
        // i.e. pointers should be hashed for what's behind them,
        //      instead of their raw address value, (usually).
        assert(map.hasher == cstring_hash);
        map.hasher = cstring_hash;  //< this is set by default using _Generic.

        mapof(string, i16) _m = MMAKE(string, i16, 5);
        hashnode(string, i16) node;
        string some_key = STRING("key");
        i16 val = 69;
        init_hashnode(&node, &_m, 93967, &some_key, &val);
        println("node.key.hash = %lu;", node.key.hash);
        println("node.key.value = \"%S\";", node.key.value);
        println("node.value = %hd;", node.value);
        println("node.next = %p;", node.next);

        puts("");

        i32 *value;
        println("number of entries: %zu.", map.len);
        ASSOCIATE(map, "hello", 38);
        value = LOOKUP(map, "hello");
        // `value` points to the item mapped to by key "hello".
        println("value (%p): %d", value, *value);
        println("number of entries: %zu.", map.len);
        // drop value at key "hello" from table.
        DROP(map, "hello");
        value = LOOKUP(map, "hello");
        // `value` should now point to NULL.
        println("value (%p): -", value);
        println("number of entries: %zu.", map.len);
        puts("");

        free_map(&map);

        // maps work with any key and value types.
        mapof(string, u16) dict = MMAKE(string, u16, 2);
        assert(dict.hasher == string_hash);
        println("hash(\"ad\") = %llX; hash(\"da\") = %llX;",
                hash_string(STR("ad")), hash_string(STR("da")));
        assert(DROP(dict, STR("ab")) == false);
        string ab = STRING("ab");
        string bc = STRING("bc");
        string ac = STRING("ac");
        string ca = STRING("ca");
        string ad = STRING("ad");
        string da = STRING("da");
        ASSOCIATE(dict, ab, 0x6162);
        ASSOCIATE(dict, bc, 0x6263);
        ASSOCIATE(dict, ac, 0x6163);
        ASSOCIATE(dict, ca, 0x0000);
        ASSOCIATE(dict, ca, 0x6361);  //< overwrite.
        ASSOCIATE(dict, ad, 0x6164);
        ASSOCIATE(dict, da, 0x6461);
        println("dict: %S -> 0x%04hX", STR("ab"), deref(u16, LOOKUP(dict, STR("ab")), 0));
        println("dict: %S -> 0x%04hX", STR("bc"), deref(u16, LOOKUP(dict, STR("bc")), 0));
        println("dict: %S -> 0x%04hX", STR("ac"), deref(u16, LOOKUP(dict, STR("ac")), 0));
        println("dict: %S -> 0x%04hX", STR("ca"), deref(u16, LOOKUP(dict, STR("ca")), 0));
        println("dict: %S -> 0x%04hX", STR("ad"), deref(u16, LOOKUP(dict, STR("ad")), 0));
        println("dict: %S -> 0x%04hX", STR("da"), deref(u16, LOOKUP(dict, STR("da")), 0));

        println("\nDump hashmap layout:");
        dump_hashmap(&dict, "\"%S\"", "0x%02hX");
        assert(DROP(dict, STR("bc")) == true);
        dump_hashmap(&dict, "\"%S\"", "0x%02hX");
        println("  ^^ after dropping \"bc\".");
        assert(DROP(dict, STR("da")) == true);
        dump_hashmap(&dict, "\"%S\"", "0x%02hX");
        println("  ^^ after dropping \"da\".");

        assert(!is_empty_map(&dict));
        assert(HAS_KEY(dict, STR("ac")) == true);
        empty_map(&dict);
        assert(HAS_KEY(dict, STR("ac")) == false);
        assert(is_empty_map(&dict));
        free_map(&dict);
        assert(is_empty_map(&dict));

        mapof(i32, string) table = MMAKE(i32, string, 3);
        assert(table.hasher == upcast_hash);

        string hello = STRING("Hello, ");
        string world = STRING("World!");
        ASSOCIATE(table, -3, hello);
        ASSOCIATE(table, +7, world);
        // Iterate through all keys present in table.
        sliceof(i32) *keys = KEYS(table);
        println("All keys in table: %V%d{, }.", *keys);
        foreach (key, *keys)
            println("table[%d] = \"%S\";", key, *LOOKUP(table, key));

        assert(!HAS_KEY(table, 3));
        assert(HAS_KEY(table, -3));

        FREE_INSIDE(*keys);

        assert(!is_empty_map(&table));

        free_map(&table);

        assert(is_empty_map(&table));
    }

    TEST("Argument parsing") {
        ArgParser ctx;
        mapof(ArgID, Arg) options = MMAKE(ArgID, Arg, 15);
        ArgID arg_a, arg_b, arg_c, arg_q, arg_m, arg_l, arg_v, arg_n, arg_s;

        // init for parsing
        arginit(&ctx);
        // register arguments
        arg_a = argreg(&ctx, "a", nil, false, "Option A.");
        arg_b = argreg(&ctx, "-b", nil, false, "Option B.");
        arg_c = argreg(&ctx, "c", nil, false, "Option C.");
        arg_q = argreg(&ctx, "q", "--quiet", false, "Stay quiet.");
        arg_m = argreg(&ctx, "-m", "message", true, "Send a message.");
        arg_l = argreg(&ctx, "-l", "--list", false, "List values.");
        arg_v = argreg(&ctx, "-v", "--volume", true, "Sets the volume.");
        arg_n = argreg(&ctx, "-n", "--number", true, "Sets the number.");
        arg_s = argreg(&ctx, nil, "--skip", true, "Sets the skip count.");

        // sample cli arguments
        sliceof(string) args = INIT(string, {
            STRING("-abc"), STRING("+q"),  // '+' toggles off instead.
            STRING("--message"), STRING("hello world"),
            STRING("-l"), STRING("-v"), STRING("10"),
            STRING("-n43"),      // short option with argument glued on.
            STRING("--skip=19")  // long option with argument glued on.
        });
        // ./prog -abc +q --message "hello world" -l -v 10 -n43 --skip=19

        ierr err;
        foreach (arg, args) {
            err = argparse(&ctx, &options, arg);
            if (OK != err) {
                eprintln("error parsing arguments: %S", ctx.error_message);
                break;
            }
        }
        if (OK == err) {
            println("Option -a enabled?   %b", (*LOOKUP(options, arg_a)).is_on);
            println("Option -b enabled?   %b", (*LOOKUP(options, arg_b)).is_on);
            println("Option -c enabled?   %b", (*LOOKUP(options, arg_c)).is_on);
            println("Option -q enabled?   %b", (*LOOKUP(options, arg_q)).is_on);
            println("Option -m value?     %S", (*LOOKUP(options, arg_m)).value);
            println("Option -l enabled?   %b", (*LOOKUP(options, arg_l)).is_on);
            println("Option -v value?     %S", (*LOOKUP(options, arg_v)).value);
            println("Option -n value?     %S", (*LOOKUP(options, arg_n)).value);
            println("Option --skip value? %S", (*LOOKUP(options, arg_s)).value);
        }
    }

    return EXIT_SUCCESS;
}

#endif

Updated on 23 August 2022 at 00:54:19 UTC