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 mapsvoid *
tovoid *
. - 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
Name | |
---|---|
usize | len |
u64 | hash |
u0 * | value |
struct GenericMap::@18::@19::@20 | key |
u0 * | next |
struct GenericMap::@18::@19 * | value |
usize | cap |
struct GenericMap::@18 | buckets |
usize | value_size |
usize | key_size |
usize | node_size |
usize | hash_offset |
usize | key_offset |
usize | value_offset |
usize | next_offset |
HashKeyType | key_type |
u64(* | hasher |
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
Name | |
---|---|
usize | len |
u64 | hash |
ArgID | value |
struct Args::@1::@2::@3 | key |
Arg | value |
u0 * | next |
struct Args::@1::@2 * | value |
usize | cap |
struct Args::@1 | buckets |
usize | value_size |
usize | key_size |
usize | node_size |
usize | hash_offset |
usize | key_offset |
usize | value_offset |
usize | next_offset |
HashKeyType | key_type |
u64(* | hasher |
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
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 | |
---|---|
int | value |
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 | |
---|---|
u32 | magic |
ArgTemplate * | value |
usize | len |
usize | cap |
struct _ArgParser::@5 | templates |
u64 | hash |
byte | value |
struct _ArgParser::@6::@8::@9::@10 | key |
usize | value |
u0 * | next |
struct _ArgParser::@6::@8::@9 * | value |
struct _ArgParser::@6::@8 | buckets |
usize | value_size |
usize | key_size |
usize | node_size |
usize | hash_offset |
usize | key_offset |
usize | value_offset |
usize | next_offset |
HashKeyType | key_type |
u64(* | hasher |
struct _ArgParser::@6 | index_of_short_form |
string | value |
struct _ArgParser::@7::@12::@13::@14 | key |
struct _ArgParser::@7::@12::@13 * | value |
struct _ArgParser::@7::@12 | buckets |
struct _ArgParser::@7 | index_of_long_form |
bool | awaiting_value |
ArgID | awaiting_value_id |
usize | string_count |
string | error_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
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
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
- dir src
- dir src/crelude
- file src/crelude/argparse.c
- file src/crelude/argparse.h
- file src/crelude/base64.c
- file src/crelude/base64.h
- file src/crelude/common.c
- file src/crelude/common.h
- file src/crelude/io.c
- file src/crelude/io.h
- file src/crelude/log.h
- file src/crelude/utf.c
- file src/crelude/utf.h
- file src/tests.c
- dir src/crelude
Updated on 23 August 2022 at 00:54:19 UTC
title: src
src
Directories
Name |
---|
src/crelude |
Files
Name |
---|
src/tests.c |
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
Classes
Name | |
---|---|
struct | _Arg |
struct | _ArgID |
struct | Args |
struct | _ArgTemplate |
struct | _ArgParser |
Types
Name | |
---|---|
enum | @0 { ARGPARSE_OK = EXIT_SUCCESS, ARGPARSE_INVALID, ARGPARSE_UNEXPECTED, ARGPARSE_EXPECTED_ARGUMENT, ARGPARSE_UNKNOWN} |
typedef struct _Arg | Arg |
typedef struct _ArgID | ArgID |
typedef struct _ArgTemplate | ArgTemplate |
typedef struct _ArgParser | ArgParser |
Functions
Name | |
---|---|
ArgID | argreg(ArgParser * ctx, char * short_form, char * long_form, bool takes_value, char * help) |
u0 | arginit(ArgParser * ctx) Initialise argument parser. |
ierr | argparse_c(ArgParser * ctx, u0 * map_id_to_arg, char * arg) Parse arguments one value of argv at the time. |
ierr | argparse(ArgParser * ctx, u0 * map_id_to_arg, string arg) Parse arguments one string value from argv at the time. |
ierr | argparseall_c(ArgParser * ctx, u0 * map_id_to_arg, usize argc, char ** argv) Parse arguments from argc and argv. |
Attributes
Defines
Detailed Description
Parsing command line arguments.
Types Documentation
enum @0
Enumerator | Value | Description |
---|---|---|
ARGPARSE_OK | EXIT_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
Functions
Name | |
---|---|
i8 * | base64_decode_table(void ) |
bool | is_base64_digit(byte d) |
usize | base64_encoded_size(usize ) Calculate encoded size given original size. |
usize | base64_decoded_size(MemSlice ) Calculate decoded size given the endcoded data. |
MemSlice | base64_encode(MemSlice ) |
MemSlice | base64_decode(MemSlice ) |
Attributes
Name | |
---|---|
byte * | value |
usize | len |
const struct @16 | BASE64_DIGITS Encoding characters / digits, each representing a value between 0..63. |
const struct @17 | BASE64_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
Classes
Name | |
---|---|
struct | _atomic_t Useful for resource counting etc. |
struct | GenericArray Array with pointer to void. |
struct | MemArray Array with pointer type to smallest addressable units of memory. |
struct | GenericSlice Slice with pointer to void. |
struct | MemSlice Slice with pointer type to smallest addressable units of memory. |
struct | GenericMap Hash-table that maps void * to void * . |
struct | string Immutable wrapper for UTF-8 encoded string (bytes are mutable). |
struct | runic Imutable warpper for UCS-4/UTF-32 encoded runic string (runes are mutable). |
struct | StringBuilder Mutable string which is built/pushed-to over time. |
struct | RunicBuilder Mutable runic string which is built/pushed-to over time. |
struct | symbol Symbols are interned strings. |
Types
Name | |
---|---|
enum | HashKeyType { 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 HashKeyType | HashKeyType |
typedef struct _atomic_t | atomic_t Useful for resource counting etc. |
typedef void | u0 |
typedef signed int | ierr |
typedef unsigned int | uerr |
typedef unsigned long | uword long is always the same size as a machine word. |
typedef signed long | iword |
typedef unsigned int | ufast |
typedef signed int | ifast |
typedef ptrdiff_t | isize |
typedef size_t | usize Use for storing array indices or object sizes. |
typedef intptr_t | iptr |
typedef uintptr_t | uptr Large enough to store a pointer, like (void *). |
typedef intmax_t | imax |
typedef uintmax_t | umax |
typedef unsigned char | umin Such that sizeof(umin) == 1 . |
typedef signed char | imin Such that sizeof(imin) == 1 . |
typedef __int8_t | i8 |
typedef __uint8_t | u8 |
typedef __uint8_t | byte Don't use char when you want byte . |
typedef __int16_t | i16 |
typedef __uint16_t | u16 |
typedef __int32_t | i32 |
typedef __uint32_t | u32 |
typedef u32 | rune |
typedef __int64_t | i64 |
typedef __uint64_t | u64 |
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)." ) | |
u0 | panic(const byte * , ... ) |
u0 * | or(const u0 * nullable, const u0 * nonnull) |
bool | is_zero(imax ) |
bool | is_zerof(f64 ) |
bool | is_zeroed(u0 * , usize ) |
u0 | zero(u0 * blk, usize width) |
u0 * | emalloc(usize , usize ) Malloc with zeros, and panics when out of memory. |
u0 | reverse(u0 * self, usize width) |
MemSlice | reverse_endianness(MemSlice bytes) |
bool | is_little_endian(void ) |
u128 | big_endian(umin * start, usize bytes) Read big-endian integer. |
u0 | swap(u0 * self, usize pivot, usize width) |
u0 | memswap(umin * a, umin * b, usize bytes) |
usize | resize(u0 * self, usize cap, usize width) |
usize | grow(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) |
usize | push(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. |
usize | insert(u0 * self, usize index, const u0 * element, usize width) |
usize | extend(u0 * self, const u0 * slice, usize width) |
usize | splice(u0 * self, usize index, const u0 * slice, usize width) |
GenericSlice | cut(u0 * self, usize from, isize upto, usize width) |
usize | null(u0 * self, usize width) |
string | from_cstring(const byte * ) NUL-terminated string to library string. |
bool | string_eq(const string, const string) Compare two strings for equality. |
i16 | string_cmp(const string, const string) Compare two strings for alphabetic rank. |
i16 | string_ncmp(const string, const string, usize n) Compare two strings for alphabetic rank upto a given number of bytes. |
u64 | hash_string(const string) Hash a string. |
u64 | hash_bytes(const MemSlice) Hash a byte slice. |
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. |
u0 * | lookup(u0 * self, const u0 * key) Look-up / get value from hash-map/table given the key. |
bool | drop(u0 * self, const u0 * key) |
GenericSlice | get_keys(u0 * self) |
bool | has_key(u0 * self, u0 * key) Checks if entry / key-value pair is present in hash-table/map given a key. |
u0 | empty_map(u0 * self) |
bool | is_empty_map(u0 * self) Checks if the hash-table / map is empty or freed. |
u0 | free_map(u0 * self) |
usize | init_hashnode(u0 * , const u0 * , u64 , const u0 * , const u0 * ) Internal use 99% of the time. |
u0 | dump_hashmap(u0 * self, byte * key_formatter, byte * value_formatter) Hashmap debugging function. |
attribute((unused) ) const | |
return | hash_bytes(extension({ __auto_type _self=(runes);MemSlice _bytes={ .len=_self.len sizeof(_self.value),.value=(umin *) _self.value };_bytes;}) ) |
return | hash_string(from_cstring(*(byte **) key) ) |
Attributes
Name | |
---|---|
u64 | hash |
u0 * | value |
struct @21 | key |
u0 * | next |
const ierr | NUL |
const byte | NUL_BYTE |
const string | NUL_STRING |
const iword | ZERO |
usize | _ |
runic | runes |
usize | size |
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
Enumerator | Value | Description |
---|---|---|
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 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)
whereT
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 forb
. - b Block of memory
b
, to be swapped fora
. - bytes The common size of block
a
andb
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)
whereT
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)
whereT
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)
whereT
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
#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
#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
Functions
Name | |
---|---|
ierr | fput(string , FILE * )fputs(...) with string . |
ierr | put(string ) Same as fput(..., stdout) . |
ierr | eput(string ) Same as fput(..., stderr) . |
ierr | eputs(const byte * ) puts(...) to STDERR. |
string | novel_vsprintf(const byte * , va_list ) |
string | novel_sprintf(const byte * , ... ) |
ierr | novel_vfprintf(FILE * , const byte * , va_list ) |
ierr | novel_fprintf(FILE * , const byte * , ... ) |
ierr | novel_vfprintf_newline(FILE * , const byte * , va_list ) |
ierr | novel_fprintf_newline(FILE * , const byte * , ... ) |
ierr | novel_printf(const byte * , ... ) |
usize | sizeof_specifier(const byte * ) |
Defines
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
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
Name | |
---|---|
const rune | OffsetsFromUTF8 |
const u8 | TrailingBytesForUTF8 |
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 | |
---|---|
string | to_string(const byte * c) |
runic | utf8_to_ucs4(runic dest, string src) |
string | ucs4_to_utf8(string dest, runic src) |
string | rune_to_utf8(string dest, rune ch) |
usize | byte_offset(string , usize ) |
usize | char_num(string , usize ) |
rune | read_rune(string s, usize * i) |
u0 | next_rune(string , usize * ) Update byte-index to move next rune, skipping it. |
u0 | prev_rune(string , usize * ) Update byte-index to move to previous rune. |
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) |
string | utf_strchr(string s, rune ch, usize * i) |
usize | utf_strlen(string s) |
usize | utf_seqlen(string ) Returns length of next UTF-8 sequence. |
bool | is_locale_utf8(byte * locale) |
bool | is_octal_digit(byte c) |
bool | is_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
Classes
Name | |
---|---|
struct | _Natural |
Types
Functions
Name | |
---|---|
u0 | utf8_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