From 36f449e027efffb8193dc16a2fbf69a01dc5939b Mon Sep 17 00:00:00 2001 From: Juni Kim Date: Mon, 10 Nov 2025 19:02:56 -0500 Subject: [PATCH 01/44] start work on adding new instructions --- src/finchlite/codegen/c.py | 28 +++ src/finchlite/codegen/hashtable.py | 226 +++++++++++++++++++ src/finchlite/finch_assembly/interpreter.py | 19 ++ src/finchlite/finch_assembly/map.py | 80 +++++++ src/finchlite/finch_assembly/nodes.py | 72 ++++++ src/finchlite/finch_assembly/type_checker.py | 30 +++ 6 files changed, 455 insertions(+) create mode 100644 src/finchlite/codegen/hashtable.py create mode 100644 src/finchlite/finch_assembly/map.py diff --git a/src/finchlite/codegen/c.py b/src/finchlite/codegen/c.py index 45294f98..f19178f2 100644 --- a/src/finchlite/codegen/c.py +++ b/src/finchlite/codegen/c.py @@ -936,6 +936,34 @@ def construct_from_c(self, res): """ +class CMapFType(BufferFType, CArgumentFType, ABC): + """ + Abstract base class for the ftype of datastructures. The ftype defines how + the data in an Buffer is organized and accessed. + """ + + @abstractmethod + def c_existsmap(self, ctx, map, idx1, idx2): + """ + Return C code which checks whether a given key exists in a map. + """ + ... + + @abstractmethod + def c_loadmap(self, ctx, buffer, idx1, idx2): + """ + Return C code which gets a value corresponding to a certain key. + """ + ... + + @abstractmethod + def c_storemap(self, ctx, buffer, idx1, idx2, value): + """ + Return C code which stores a certain value given a certain key (idx1, idx2) + """ + ... + + class CBufferFType(BufferFType, CArgumentFType, ABC): """ Abstract base class for the ftype of datastructures. The ftype defines how diff --git a/src/finchlite/codegen/hashtable.py b/src/finchlite/codegen/hashtable.py new file mode 100644 index 00000000..236fbd8a --- /dev/null +++ b/src/finchlite/codegen/hashtable.py @@ -0,0 +1,226 @@ +import ctypes +import numpy as np + +from finchlite.codegen.c import CBufferFType, CMapFType, CStackFType + +from ..finch_assembly.map import Map +from ..util.print import qual_str + + +def _is_integer_pair(x): + return ( + isinstance(x, tuple) + and len(x) == 2 + and isinstance(x[0], int) + and isinstance(x[1], int) + ) + + +class HashTable(Map): + """ + A hashmap that maps (int64, int64) to int64 + + Since we are only working with int64, we can afford to not use type generics + """ + + def __init__(self, map: "dict[tuple[int,int],int] | None" = None): + if map is None: + map = {} + self.map = {} + for key, value in map.items(): + if not _is_integer_pair(key): + raise TypeError(f"Supplied key {key} is not a tuple of two integers") + self.map[key] = value + + @property + def ftype(self): + """ + Returns the ftype of the buffer, which is a NumpyBufferFType. + """ + return HashTableFType() + + def exists(self, idx1: int, idx2: int) -> bool: + return (idx1, idx2) in self.map + + def load(self, idx1: int, idx2: int): + return self.map[(idx1, idx2)] + + def store(self, idx1: int, idx2: int, val): + self.map[(idx1,idx2)] = val + + def __str__(self): + return f"hashtable({self.map})" + + +class HashTableFType(CMapFType, CStackFType): + """ + A ftype for buffers that uses NumPy arrays. This is a concrete implementation + of the BufferFType class. + """ + + def __init__(self): + pass + + def __eq__(self, other): + return isinstance(other, HashTableFType) + + def __str__(self): + return f"hashtable_t" + + def __repr__(self): + return f"HashTableFType()" + + @property + def length_type(self): + """ + Returns the type used for the length of the buffer. + """ + return np.intp + + @property + def element_type(self): + """ + Returns the type of elements stored in the buffer. + This is typically the same as the dtype used to create the buffer. + """ + return self._dtype + + def __hash__(self): + """ + I do not understand why this is here now but sure. + """ + return hash(int) + + def __call__(self): + return HashTable() + + def c_type(self): + return ctypes.POINTER(CNumpyBuffer) + + def c_length(self, ctx, buf): + return buf.obj.length + + def c_data(self, ctx, buf): + return buf.obj.data + + def c_load(self, ctx, buf, idx): + return f"({buf.obj.data})[{ctx(idx)}]" + + def c_store(self, ctx, buf, idx, value): + ctx.exec(f"{ctx.feed}({buf.obj.data})[{ctx(idx)}] = {ctx(value)};") + + def c_resize(self, ctx, buf, new_len): + new_len = ctx(ctx.cache("len", new_len)) + data = buf.obj.data + length = buf.obj.length + obj = buf.obj.obj + t = ctx.ctype_name(c_type(self._dtype)) + ctx.exec( + f"{ctx.feed}{data} = ({t}*){obj}->resize(&{obj}->arr, {new_len});\n" + f"{ctx.feed}{length} = {new_len};" + ) + return + + def c_unpack(self, ctx, var_n, val): + """ + Unpack the buffer into C context. + """ + data = ctx.freshen(var_n, "data") + length = ctx.freshen(var_n, "length") + t = ctx.ctype_name(c_type(self._dtype)) + ctx.add_header("#include ") + ctx.exec( + f"{ctx.feed}{t}* {data} = ({t}*){ctx(val)}->data;\n" + f"{ctx.feed}size_t {length} = {ctx(val)}->length;" + ) + + return CBufferFields(data, length, var_n) + + def c_repack(self, ctx, lhs, obj): + """ + Repack the buffer from C context. + """ + ctx.exec( + f"{ctx.feed}{lhs}->data = (void*){obj.data};\n" + f"{ctx.feed}{lhs}->length = {obj.length};" + ) + return + + def serialize_to_c(self, obj): + """ + Serialize the NumPy buffer to a C-compatible structure. + """ + data = ctypes.c_void_p(obj.arr.ctypes.data) + length = obj.arr.size + obj._self_obj = ctypes.py_object(obj) + obj._c_callback = numpy_buffer_resize_callback + obj._c_buffer = CNumpyBuffer(obj._self_obj, data, length, obj._c_callback) + return ctypes.pointer(obj._c_buffer) + + def deserialize_from_c(self, obj, c_buffer): + """ + Update this buffer based on how the C call modified the CNumpyBuffer structure. + """ + # this is handled by the resize callback + + def construct_from_c(self, c_buffer): + """ + Construct a NumpyBuffer from a C-compatible structure. + """ + return NumpyBuffer(c_buffer.contents.arr) + + def numba_type(self) -> type: + return list[np.ndarray] + + def numba_jitclass_type(self) -> numba.types.Type: + return numba.types.ListType( + numba.types.Array(numba.from_dtype(self.element_type), 1, "C") + ) + + def numba_length(self, ctx, buf): + arr = buf.obj.arr + return f"len({arr})" + + def numba_load(self, ctx, buf, idx): + arr = buf.obj.arr + return f"{arr}[{ctx(idx)}]" + + def numba_store(self, ctx, buf, idx, val): + arr = buf.obj.arr + ctx.exec(f"{ctx.feed}{arr}[{ctx(idx)}] = {ctx(val)}") + + def numba_resize(self, ctx, buf, new_len): + arr = buf.obj.arr + ctx.exec(f"{ctx.feed}{arr} = numpy.resize({arr}, {ctx(new_len)})") + + def numba_unpack(self, ctx, var_n, val): + """ + Unpack the buffer into Numba context. + """ + arr = ctx.freshen(var_n, "arr") + ctx.exec(f"{ctx.feed}{arr} = {ctx(val)}[0]") + + return NumbaBufferFields(arr, var_n) + + def numba_repack(self, ctx, lhs, obj): + """ + Repack the buffer from Numba context. + """ + ctx.exec(f"{ctx.feed}{lhs}[0] = {obj.arr}") + return + + def serialize_to_numba(self, obj): + """ + Serialize the NumPy buffer to a Numba-compatible object. + """ + return numba.typed.List([obj.arr]) + + def deserialize_from_numba(self, obj, numba_buffer): + obj.arr = numba_buffer[0] + return + + def construct_from_numba(self, numba_buffer): + """ + Construct a NumpyBuffer from a Numba-compatible object. + """ + return NumpyBuffer(numba_buffer[0]) diff --git a/src/finchlite/finch_assembly/interpreter.py b/src/finchlite/finch_assembly/interpreter.py index 0f515502..49195211 100644 --- a/src/finchlite/finch_assembly/interpreter.py +++ b/src/finchlite/finch_assembly/interpreter.py @@ -212,6 +212,18 @@ def __call__(self, prgm: asm.AssemblyNode): buf_e = self(buf) idx_e = self(idx) return buf_e.load(idx_e) + case asm.LoadMap(map, idx1, idx2): + assert isinstance(map, asm.Slot) + map_e = self(map) + idx1_e = self(idx1) + idx2_e = self(idx2) + return map_e.load(idx1_e, idx2_e) + case asm.ExistsMap(map, idx1, idx2): + assert isinstance(map, asm.Slot) + map_e = self(map) + idx1_e = self(idx1) + idx2_e = self(idx2) + return map_e.exists(idx1_e, idx2_e) case asm.Store(buf, idx, val): assert isinstance(buf, asm.Slot) buf_e = self(buf) @@ -219,6 +231,13 @@ def __call__(self, prgm: asm.AssemblyNode): val_e = self(val) buf_e.store(idx_e, val_e) return None + case asm.StoreMap(map, idx1, idx2, val): + assert isinstance(map, asm.Slot) + map_e = self(map) + idx1_e = self(idx1) + idx2_e = self(idx2) + val_e = self(val) + return map_e.store(idx1_e, idx2_e, val_e) case asm.Resize(buf, len_): assert isinstance(buf, asm.Slot) buf_e = self(buf) diff --git a/src/finchlite/finch_assembly/map.py b/src/finchlite/finch_assembly/map.py new file mode 100644 index 00000000..56408e02 --- /dev/null +++ b/src/finchlite/finch_assembly/map.py @@ -0,0 +1,80 @@ +from abc import ABC, abstractmethod +from ..symbolic import FType, FTyped + + +class Map(FTyped, ABC): + """ + Abstract base class for a map data structure. + Hash tables should be such that their bucket size can be resized, with Tree + maps turning that into a no-op. + """ + + @abstractmethod + def __init__(self, length: int, dtype: type): ... + + @property + def element_type(self): + """ + Return the type of elements stored in the hash table. + This is typically the same as the dtype used to create the map. + """ + return self.ftype.element_type() + + @property + def length_type(self): + """ + Return the type of indices used to access elements in the hash table. + This is typically an integer type. + """ + return self.ftype.length_type() + + @abstractmethod + def load(self, idx1: int, idx2: int): + """ + Method to access some element in the map. Will panic if the key doesn't exist. + """ + ... + + @abstractmethod + def exists(self, idx1: int, idx2: int) -> bool: + """ + Method to check if the element exists in the map. + """ + ... + + @abstractmethod + def store(self, idx1: int, idx2: int, val): + """ + Method to store elements in the map. Ideally it should just create new + elements. + """ + ... + + +class MapFType(FType): + """ + Abstract base class for an ftype corresponding to a map. + """ + + @abstractmethod + def __call__(self, *args, **kwargs): + """ + Create an instance of an object in this ftype with the given arguments. + """ + ... + + @property + @abstractmethod + def element_type(self): + """ + Return the type of elements stored in the map. + This is typically the same as the dtype used to create the map. + """ + ... + + @property + def length_type(self): + """ + Returns the type used for the length of the map. + """ + return int diff --git a/src/finchlite/finch_assembly/nodes.py b/src/finchlite/finch_assembly/nodes.py index 7183ca44..a66bd80c 100644 --- a/src/finchlite/finch_assembly/nodes.py +++ b/src/finchlite/finch_assembly/nodes.py @@ -351,6 +351,73 @@ def children(self): return [self.buffer, self.index, self.value] +@dataclass(eq=True, frozen=True) +class ExistsMap(AssemblyExpression, AssemblyTree): + """ + Represents checking whether an integer key is in a map. + + Attributes: + map: The map to load from. + index1: The first integer in the pair + index2: The second integer in the pair + """ + + map: Slot | Stack + index1: AssemblyExpression + index2: AssemblyExpression + + @property + def children(self): + return [self.map, self.index1, self.index2] + + def result_format(self): + return bool + + +@dataclass(eq=True, frozen=True) +class LoadMap(AssemblyExpression, AssemblyTree): + """ + Represents loading a value from a map given an integer pair key. + + Attributes: + map: The map to load from. + index1: The first integer in the pair + index2: The second integer in the pair + """ + + map: Slot | Stack + index1: AssemblyExpression + index2: AssemblyExpression + + @property + def children(self): + return [self.map, self.index1, self.index2] + + def result_format(self): + return element_format(self.map.result_format) + + +@dataclass(eq=True, frozen=True) +class StoreMap(AssemblyTree): + """ + Represents storing a value into a buffer given an integer pair key. + + Attributes: + map: The map to load from. + index1: The first integer in the pair + index2: The second integer in the pair + """ + + map: Slot | Stack + index1: AssemblyExpression + index2: AssemblyExpression + value: AssemblyExpression + + @property + def children(self): + return [self.map, self.index1, self.index2, self.value] + + @dataclass(eq=True, frozen=True) class Resize(AssemblyTree): """ @@ -692,11 +759,16 @@ def __call__(self, prgm: AssemblyNode): return None case Load(buf, idx): return f"load({self(buf)}, {self(idx)})" + case LoadMap(map, idx1, idx2): + return f"loadmap({self(map)}, {self(idx1)}, {self(idx2)})" case Slot(name, type_): return f"slot({name}, {qual_str(type_)})" case Store(buf, idx, val): self.exec(f"{feed}store({self(buf)}, {self(idx)}, {self(val)})") return None + case StoreMap(map, idx1, idx2, val): + self.exec(f"{feed}storemap({self(map)}, {self(idx1)}, {self(idx2)}, {self(val)})") + return None case Resize(buf, size): self.exec(f"{feed}resize({self(buf)}, {self(size)})") return None diff --git a/src/finchlite/finch_assembly/type_checker.py b/src/finchlite/finch_assembly/type_checker.py index d8dc65a7..70dfc379 100644 --- a/src/finchlite/finch_assembly/type_checker.py +++ b/src/finchlite/finch_assembly/type_checker.py @@ -7,6 +7,7 @@ from . import nodes as asm from .buffer import BufferFType from .struct import AssemblyStructFType +from .map import MapFType class AssemblyTypeError(Exception): @@ -81,6 +82,12 @@ def check_in_ctxt(self, var_n, var_t): f"The variable '{var_n}' is not defined in the current context." ) from KeyError + def check_map(self, map): + map_type = self.check_expr(map) + if isinstance(map_type, MapFType): + return map_type + raise AssemblyTypeError(f"Expected map, got {map_type}.") + def check_buffer(self, buffer): buffer_type = self.check_expr(buffer) if isinstance(buffer_type, BufferFType): @@ -134,6 +141,20 @@ def check_expr(self, expr: asm.AssemblyExpression): case asm.Length(buffer): buffer_type = self.check_buffer(buffer) return buffer_type.length_type + case asm.ExistsMap(map, index1, index2): + map_type = self.check_map(map) + index1_type = self.check_expr(index1) + index2_type = self.check_expr(index2) + check_type_match(map_type.length_type, index1_type) + check_type_match(map_type.length_type, index2_type) + return bool + case asm.LoadMap(map, index1, index2): + map_type = self.check_map(map) + index1_type = self.check_expr(index1) + index2_type = self.check_expr(index2) + check_type_match(map_type.length_type, index1_type) + check_type_match(map_type.length_type, index2_type) + return map_type.element_type case _: raise ValueError(f"Ill-formed AssemblyExpression: {type(expr)}.") @@ -181,6 +202,15 @@ def check_stmt(self, stmt: asm.AssemblyNode): value_type = self.check_expr(value) check_type_match(buffer_type.element_type, value_type) return None + case asm.StoreMap(map, index1, index2, value): + map_type = self.check_map(map) + index1_type = self.check_expr(index1) + index2_type = self.check_expr(index2) + value_type = self.check_expr(value) + check_type_match(map_type.length_type, index1_type) + check_type_match(map_type.length_type, index2_type) + check_type_match(map_type.element_type, value_type) + return None case asm.Resize(buffer, new_size): buffer_type = self.check_buffer(buffer) new_size_type = self.check_expr(new_size) From 97b27a156266b5227104fc6a20ea01a8eff98328 Mon Sep 17 00:00:00 2001 From: Juni Kim Date: Mon, 17 Nov 2025 23:38:00 -0500 Subject: [PATCH 02/44] somewhat finish work on numba instructions --- src/finchlite/codegen/c.py | 10 +- src/finchlite/codegen/hashtable.py | 297 ++++++++++--------- src/finchlite/codegen/numba_backend.py | 31 +- src/finchlite/finch_assembly/interpreter.py | 21 +- src/finchlite/finch_assembly/map.py | 13 +- src/finchlite/finch_assembly/nodes.py | 21 +- src/finchlite/finch_assembly/type_checker.py | 28 +- 7 files changed, 226 insertions(+), 195 deletions(-) diff --git a/src/finchlite/codegen/c.py b/src/finchlite/codegen/c.py index f19178f2..198e155e 100644 --- a/src/finchlite/codegen/c.py +++ b/src/finchlite/codegen/c.py @@ -939,27 +939,27 @@ def construct_from_c(self, res): class CMapFType(BufferFType, CArgumentFType, ABC): """ Abstract base class for the ftype of datastructures. The ftype defines how - the data in an Buffer is organized and accessed. + the data in a Map is organized and accessed. """ @abstractmethod - def c_existsmap(self, ctx, map, idx1, idx2): + def c_existsmap(self, ctx, map, idx): """ Return C code which checks whether a given key exists in a map. """ ... @abstractmethod - def c_loadmap(self, ctx, buffer, idx1, idx2): + def c_loadmap(self, ctx, buffer, idx): """ Return C code which gets a value corresponding to a certain key. """ ... @abstractmethod - def c_storemap(self, ctx, buffer, idx1, idx2, value): + def c_storemap(self, ctx, buffer, idx, value): """ - Return C code which stores a certain value given a certain key (idx1, idx2) + Return C code which stores a certain value given a certain integer tuple key. """ ... diff --git a/src/finchlite/codegen/hashtable.py b/src/finchlite/codegen/hashtable.py index 236fbd8a..13430e19 100644 --- a/src/finchlite/codegen/hashtable.py +++ b/src/finchlite/codegen/hashtable.py @@ -1,81 +1,135 @@ import ctypes -import numpy as np +from typing import NamedTuple -from finchlite.codegen.c import CBufferFType, CMapFType, CStackFType +import numba -from ..finch_assembly.map import Map -from ..util.print import qual_str +from finchlite.codegen.c import CContext, CMapFType, CStackFType +from finchlite.codegen.numba_backend import NumbaContext, NumbaMapFType, NumbaStackFType +from finchlite.finch_assembly.nodes import AssemblyExpression, AssemblyNode, Stack +from finchlite.finch_assembly.struct import TupleFType +from ..finch_assembly.map import Map, MapFType -def _is_integer_pair(x): - return ( - isinstance(x, tuple) - and len(x) == 2 - and isinstance(x[0], int) - and isinstance(x[1], int) - ) +class NumbaMapFields(NamedTuple): + """ + This is a field that extracts out the map from the obj variable. Its + purpose is so that we can extract out map from obj in unpack, do + computations on the map variable, and re-insert that into obj in repack. + """ -class HashTable(Map): + map: str + obj: str + + +class CMapFields(NamedTuple): """ - A hashmap that maps (int64, int64) to int64 + TODO: for the C backend, we will pulling in a completely different library + to do the actual hash function implementation. Should we even try to + convert back? + """ + + map: str + obj: str + +def _is_integer_tuple(tup, size): + if not isinstance(tup, tuple) or len(tup) != size: + return False + for elt in tup: + if not isinstance(elt, int): + return False + return True + - Since we are only working with int64, we can afford to not use type generics +class CHashMap(ctypes.Structure): + _fields_ = [ + ("map", ctypes.py_object), + ("data", ctypes.c_void_p), + # TODO: you need more methods to work with the data. + ] + + +class HashTable(Map): + """ + A Hash Table that maps Z^{in_len} to Z^{out_len} """ - def __init__(self, map: "dict[tuple[int,int],int] | None" = None): + def __init__( + self, key_len, value_len, map: "dict[tuple[int,int],int] | None" = None + ): + """ + Constructor for the Hash Table, which maps integer tuples to integer tuples. Takes three arguments: + in_len: The + """ if map is None: map = {} self.map = {} for key, value in map.items(): - if not _is_integer_pair(key): - raise TypeError(f"Supplied key {key} is not a tuple of two integers") + if not _is_integer_tuple(key, key_len): + raise TypeError( + f"Supplied key {key} is not a tuple of {key_len} integers" + ) + if not _is_integer_tuple(value, value_len): + raise TypeError( + f"Supplied value {key} is not a tuple of {value_len} integers" + ) self.map[key] = value + self.key_len = key_len + self.value_len = value_len @property def ftype(self): """ Returns the ftype of the buffer, which is a NumpyBufferFType. """ - return HashTableFType() + return HashTableFType(self.key_len, self.value_len) - def exists(self, idx1: int, idx2: int) -> bool: - return (idx1, idx2) in self.map + def exists(self, idx) -> bool: + assert _is_integer_tuple(idx, self.key_len) + return idx in self.map - def load(self, idx1: int, idx2: int): - return self.map[(idx1, idx2)] + def load(self, idx): + assert _is_integer_tuple(idx, self.key_len) + return self.map[idx] - def store(self, idx1: int, idx2: int, val): - self.map[(idx1,idx2)] = val + def store(self, idx, val): + assert _is_integer_tuple(idx, self.key_len) + assert _is_integer_tuple(val, self.value_len) + self.map[idx] = val def __str__(self): return f"hashtable({self.map})" -class HashTableFType(CMapFType, CStackFType): +class HashTableFType(MapFType, CMapFType, CStackFType, NumbaMapFType, NumbaStackFType): """ A ftype for buffers that uses NumPy arrays. This is a concrete implementation of the BufferFType class. """ - def __init__(self): - pass + def __init__(self, key_len, value_len): + self.key_len = key_len + self.value_len = value_len + self._key_type = TupleFType.from_tuple(tuple(int for _ in range(key_len))) + self._value_type = TupleFType.from_tuple(tuple(int for _ in range(value_len))) def __eq__(self, other): - return isinstance(other, HashTableFType) + if not isinstance(other, HashTableFType): + return False + return self.key_len == other.key_len and self.value_len == other.value_len def __str__(self): - return f"hashtable_t" + return f"hashtable_t({self.key_len}, {self.value_len})" def __repr__(self): - return f"HashTableFType()" + return f"HashTableFType({self.key_len}, {self.value_len})" @property - def length_type(self): + def key_type(self): """ Returns the type used for the length of the buffer. """ - return np.intp + return self._key_type @property def element_type(self): @@ -83,78 +137,93 @@ def element_type(self): Returns the type of elements stored in the buffer. This is typically the same as the dtype used to create the buffer. """ - return self._dtype + return self._value_type - def __hash__(self): + def numba_jitclass_type(self) -> numba.types.Type: + key_t = numba.types.UniTuple(numba.types.int64, self.key_len) + value_t = numba.types.UniTuple(numba.types.int64, self.value_len) + return numba.types.ListType(numba.types.DictType(key_t, value_t)) + + def numba_existsmap(self, ctx: "NumbaContext", map: "Stack", idx: "AssemblyExpression"): + assert isinstance(map.obj, NumbaMapFields) + return f"({ctx(idx)}) in {map.obj.map}" + + def numba_loadmap(self, ctx: "NumbaContext", map: "Stack", idx: "AssemblyExpression"): + assert isinstance(map.obj, NumbaMapFields) + return f"{map.obj.map}[{ctx(idx)}]" + + def numba_storemap( + self, + ctx: "NumbaContext", + map: "Stack", + idx: "AssemblyExpression", + value: "AssemblyExpression", + ): + assert isinstance(map.obj, NumbaMapFields) + ctx.exec(f"{map.obj.map}[{ctx(idx)}] = {ctx(value)}") + + def numba_unpack(self, ctx: "NumbaContext", var_n: str, val: "AssemblyExpression") -> NumbaMapFields: """ - I do not understand why this is here now but sure. + Unpack the buffer into Numba context. + This is part of a step that will create a new slot for var_n + that contains variable names corresponding to the unpacked fields. """ - return hash(int) - - def __call__(self): - return HashTable() - - def c_type(self): - return ctypes.POINTER(CNumpyBuffer) + # the val field will always be asm.Variable(var_n, var_t) + map = ctx.freshen(var_n, "map") + ctx.exec(f"{ctx.feed}{map} = {ctx(val)}[0]") - def c_length(self, ctx, buf): - return buf.obj.length + return NumbaMapFields(map, var_n) - def c_data(self, ctx, buf): - return buf.obj.data + def numba_repack(self, ctx: "NumbaContext", lhs: str, obj: "NumbaMapFields"): + """ + Repack the buffer from Numba context. + """ + # obj is the fields corresponding to the self.slots[lhs] + ctx.exec(f"{ctx.feed}{lhs}[0] = {obj.map}") - def c_load(self, ctx, buf, idx): - return f"({buf.obj.data})[{ctx(idx)}]" + def serialize_to_numba(self, obj: "HashTable"): + """ + Serialize the hashmap to a Numba-compatible object. - def c_store(self, ctx, buf, idx, value): - ctx.exec(f"{ctx.feed}({buf.obj.data})[{ctx(idx)}] = {ctx(value)};") + We will supply the input and output length + """ + return numba.typed.List([obj.map]) - def c_resize(self, ctx, buf, new_len): - new_len = ctx(ctx.cache("len", new_len)) - data = buf.obj.data - length = buf.obj.length - obj = buf.obj.obj - t = ctx.ctype_name(c_type(self._dtype)) - ctx.exec( - f"{ctx.feed}{data} = ({t}*){obj}->resize(&{obj}->arr, {new_len});\n" - f"{ctx.feed}{length} = {new_len};" - ) + def deserialize_from_numba(self, obj: "HashTable", numba_map: "list[dict]"): + obj.map = numba_map[0] return - def c_unpack(self, ctx, var_n, val): + def construct_from_numba(self, numba_buffer): """ - Unpack the buffer into C context. + Construct a numba buffer from a Numba-compatible object. """ - data = ctx.freshen(var_n, "data") - length = ctx.freshen(var_n, "length") - t = ctx.ctype_name(c_type(self._dtype)) - ctx.add_header("#include ") - ctx.exec( - f"{ctx.feed}{t}* {data} = ({t}*){ctx(val)}->data;\n" - f"{ctx.feed}size_t {length} = {ctx(val)}->length;" - ) + return HashTable(self.key_len, self.value_len, numba_buffer[0]) - return CBufferFields(data, length, var_n) - - def c_repack(self, ctx, lhs, obj): + def __hash__(self): """ - Repack the buffer from C context. + Needs to be here because you are going to be using this type as a key + in dictionaries. """ - ctx.exec( - f"{ctx.feed}{lhs}->data = (void*){obj.data};\n" - f"{ctx.feed}{lhs}->length = {obj.length};" - ) - return + return hash(("HashTableFType", self.key_len, self.value_len)) + + def __call__(self): + return HashTable(self.key_len, self.value_len, {}) + + def c_type(self): + return ctypes.POINTER(CHashMap) + + def c_load(self, ctx: "CContext", buf, idx): + return f"({buf.obj.data})[{ctx(idx)}]" + + def c_store(self, ctx: "CContext", buf, idx, value): + ctx.exec(f"{ctx.feed}({buf.obj.data})[{ctx(idx)}] = {ctx(value)};") def serialize_to_c(self, obj): """ - Serialize the NumPy buffer to a C-compatible structure. + Serialize the Hash Map to a CHashMap structure. """ data = ctypes.c_void_p(obj.arr.ctypes.data) - length = obj.arr.size - obj._self_obj = ctypes.py_object(obj) - obj._c_callback = numpy_buffer_resize_callback - obj._c_buffer = CNumpyBuffer(obj._self_obj, data, length, obj._c_callback) + obj._c_buffer = CHashMap(obj._self_obj, data) return ctypes.pointer(obj._c_buffer) def deserialize_from_c(self, obj, c_buffer): @@ -163,64 +232,8 @@ def deserialize_from_c(self, obj, c_buffer): """ # this is handled by the resize callback - def construct_from_c(self, c_buffer): + def construct_from_c(self, c_map): """ Construct a NumpyBuffer from a C-compatible structure. """ - return NumpyBuffer(c_buffer.contents.arr) - - def numba_type(self) -> type: - return list[np.ndarray] - - def numba_jitclass_type(self) -> numba.types.Type: - return numba.types.ListType( - numba.types.Array(numba.from_dtype(self.element_type), 1, "C") - ) - - def numba_length(self, ctx, buf): - arr = buf.obj.arr - return f"len({arr})" - - def numba_load(self, ctx, buf, idx): - arr = buf.obj.arr - return f"{arr}[{ctx(idx)}]" - - def numba_store(self, ctx, buf, idx, val): - arr = buf.obj.arr - ctx.exec(f"{ctx.feed}{arr}[{ctx(idx)}] = {ctx(val)}") - - def numba_resize(self, ctx, buf, new_len): - arr = buf.obj.arr - ctx.exec(f"{ctx.feed}{arr} = numpy.resize({arr}, {ctx(new_len)})") - - def numba_unpack(self, ctx, var_n, val): - """ - Unpack the buffer into Numba context. - """ - arr = ctx.freshen(var_n, "arr") - ctx.exec(f"{ctx.feed}{arr} = {ctx(val)}[0]") - - return NumbaBufferFields(arr, var_n) - - def numba_repack(self, ctx, lhs, obj): - """ - Repack the buffer from Numba context. - """ - ctx.exec(f"{ctx.feed}{lhs}[0] = {obj.arr}") - return - - def serialize_to_numba(self, obj): - """ - Serialize the NumPy buffer to a Numba-compatible object. - """ - return numba.typed.List([obj.arr]) - - def deserialize_from_numba(self, obj, numba_buffer): - obj.arr = numba_buffer[0] - return - - def construct_from_numba(self, numba_buffer): - """ - Construct a NumpyBuffer from a Numba-compatible object. - """ - return NumpyBuffer(numba_buffer[0]) + return NumpyBuffer(c_map.contents.map) diff --git a/src/finchlite/codegen/numba_backend.py b/src/finchlite/codegen/numba_backend.py index e70b9694..4a5fa330 100644 --- a/src/finchlite/codegen/numba_backend.py +++ b/src/finchlite/codegen/numba_backend.py @@ -6,7 +6,9 @@ import numpy as np -import numba # type: ignore[import-untyped] +import numba + +from finchlite.finch_assembly.map import MapFType # type: ignore[import-untyped] from .. import finch_assembly as asm from ..algebra import query_property, register_property @@ -253,6 +255,33 @@ def construct_from_numba(fmt, numba_obj): ) +class NumbaMapFType(MapFType, NumbaArgumentFType, ABC): + """ + Abstract base class for the ftype of datastructures. The ftype defines how + the data in a Map is organized and accessed. + """ + + @abstractmethod + def numba_existsmap(self, ctx: "NumbaContext", map, idx): + """ + Return numba code which checks whether a given key exists in a map. + """ + ... + + @abstractmethod + def numba_loadmap(self, ctx, buffer, idx): + """ + Return numba code which gets a value corresponding to a certain key. + """ + ... + + @abstractmethod + def numba_storemap(self, ctx, buffer, idx, value): + """ + Return C code which stores a certain value given a certain integer tuple key. + """ + ... + class NumbaBufferFType(BufferFType, NumbaArgumentFType, ABC): @abstractmethod def numba_length(self, ctx: "NumbaContext", buffer): diff --git a/src/finchlite/finch_assembly/interpreter.py b/src/finchlite/finch_assembly/interpreter.py index 49195211..48bbfef2 100644 --- a/src/finchlite/finch_assembly/interpreter.py +++ b/src/finchlite/finch_assembly/interpreter.py @@ -212,18 +212,16 @@ def __call__(self, prgm: asm.AssemblyNode): buf_e = self(buf) idx_e = self(idx) return buf_e.load(idx_e) - case asm.LoadMap(map, idx1, idx2): + case asm.LoadMap(map, idx): assert isinstance(map, asm.Slot) map_e = self(map) - idx1_e = self(idx1) - idx2_e = self(idx2) - return map_e.load(idx1_e, idx2_e) - case asm.ExistsMap(map, idx1, idx2): + idx_e = self(idx) + return map_e.load(idx_e) + case asm.ExistsMap(map, idx): assert isinstance(map, asm.Slot) map_e = self(map) - idx1_e = self(idx1) - idx2_e = self(idx2) - return map_e.exists(idx1_e, idx2_e) + idx_e = self(idx) + return map_e.exists(idx_e) case asm.Store(buf, idx, val): assert isinstance(buf, asm.Slot) buf_e = self(buf) @@ -231,13 +229,12 @@ def __call__(self, prgm: asm.AssemblyNode): val_e = self(val) buf_e.store(idx_e, val_e) return None - case asm.StoreMap(map, idx1, idx2, val): + case asm.StoreMap(map, idx, val): assert isinstance(map, asm.Slot) map_e = self(map) - idx1_e = self(idx1) - idx2_e = self(idx2) + idx_e = self(idx) val_e = self(val) - return map_e.store(idx1_e, idx2_e, val_e) + return map_e.store(idx_e, val_e) case asm.Resize(buf, len_): assert isinstance(buf, asm.Slot) buf_e = self(buf) diff --git a/src/finchlite/finch_assembly/map.py b/src/finchlite/finch_assembly/map.py index 56408e02..ccbf5194 100644 --- a/src/finchlite/finch_assembly/map.py +++ b/src/finchlite/finch_assembly/map.py @@ -29,21 +29,21 @@ def length_type(self): return self.ftype.length_type() @abstractmethod - def load(self, idx1: int, idx2: int): + def load(self, idx: tuple): """ Method to access some element in the map. Will panic if the key doesn't exist. """ ... @abstractmethod - def exists(self, idx1: int, idx2: int) -> bool: + def exists(self, idx: tuple) -> bool: """ Method to check if the element exists in the map. """ ... @abstractmethod - def store(self, idx1: int, idx2: int, val): + def store(self, idx: tuple, val): """ Method to store elements in the map. Ideally it should just create new elements. @@ -65,7 +65,7 @@ def __call__(self, *args, **kwargs): @property @abstractmethod - def element_type(self): + def value_type(self): """ Return the type of elements stored in the map. This is typically the same as the dtype used to create the map. @@ -73,8 +73,9 @@ def element_type(self): ... @property - def length_type(self): + @abstractmethod + def key_type(self): """ Returns the type used for the length of the map. """ - return int + ... diff --git a/src/finchlite/finch_assembly/nodes.py b/src/finchlite/finch_assembly/nodes.py index a66bd80c..353e899e 100644 --- a/src/finchlite/finch_assembly/nodes.py +++ b/src/finchlite/finch_assembly/nodes.py @@ -354,7 +354,7 @@ def children(self): @dataclass(eq=True, frozen=True) class ExistsMap(AssemblyExpression, AssemblyTree): """ - Represents checking whether an integer key is in a map. + Represents checking whether an integer tuple key is in a map. Attributes: map: The map to load from. @@ -363,12 +363,11 @@ class ExistsMap(AssemblyExpression, AssemblyTree): """ map: Slot | Stack - index1: AssemblyExpression - index2: AssemblyExpression + index: AssemblyExpression @property def children(self): - return [self.map, self.index1, self.index2] + return [self.map, self.index] def result_format(self): return bool @@ -377,7 +376,7 @@ def result_format(self): @dataclass(eq=True, frozen=True) class LoadMap(AssemblyExpression, AssemblyTree): """ - Represents loading a value from a map given an integer pair key. + Represents loading a value from a map given an integer tuple key. Attributes: map: The map to load from. @@ -386,12 +385,11 @@ class LoadMap(AssemblyExpression, AssemblyTree): """ map: Slot | Stack - index1: AssemblyExpression - index2: AssemblyExpression + index: AssemblyExpression @property def children(self): - return [self.map, self.index1, self.index2] + return [self.map, self.index] def result_format(self): return element_format(self.map.result_format) @@ -400,7 +398,7 @@ def result_format(self): @dataclass(eq=True, frozen=True) class StoreMap(AssemblyTree): """ - Represents storing a value into a buffer given an integer pair key. + Represents storing a value into a buffer given an integer tuple key. Attributes: map: The map to load from. @@ -409,13 +407,12 @@ class StoreMap(AssemblyTree): """ map: Slot | Stack - index1: AssemblyExpression - index2: AssemblyExpression + index: AssemblyExpression value: AssemblyExpression @property def children(self): - return [self.map, self.index1, self.index2, self.value] + return [self.map, self.index, self.value] @dataclass(eq=True, frozen=True) diff --git a/src/finchlite/finch_assembly/type_checker.py b/src/finchlite/finch_assembly/type_checker.py index 70dfc379..7b137fee 100644 --- a/src/finchlite/finch_assembly/type_checker.py +++ b/src/finchlite/finch_assembly/type_checker.py @@ -141,20 +141,16 @@ def check_expr(self, expr: asm.AssemblyExpression): case asm.Length(buffer): buffer_type = self.check_buffer(buffer) return buffer_type.length_type - case asm.ExistsMap(map, index1, index2): + case asm.ExistsMap(map, index): map_type = self.check_map(map) - index1_type = self.check_expr(index1) - index2_type = self.check_expr(index2) - check_type_match(map_type.length_type, index1_type) - check_type_match(map_type.length_type, index2_type) + index_type = self.check_expr(index) + check_type_match(map_type.key_type, index_type) return bool - case asm.LoadMap(map, index1, index2): + case asm.LoadMap(map, index): map_type = self.check_map(map) - index1_type = self.check_expr(index1) - index2_type = self.check_expr(index2) - check_type_match(map_type.length_type, index1_type) - check_type_match(map_type.length_type, index2_type) - return map_type.element_type + index_type = self.check_expr(index) + check_type_match(map_type.key_type, index_type) + return map_type.value_type case _: raise ValueError(f"Ill-formed AssemblyExpression: {type(expr)}.") @@ -202,14 +198,12 @@ def check_stmt(self, stmt: asm.AssemblyNode): value_type = self.check_expr(value) check_type_match(buffer_type.element_type, value_type) return None - case asm.StoreMap(map, index1, index2, value): + case asm.StoreMap(map, index, value): map_type = self.check_map(map) - index1_type = self.check_expr(index1) - index2_type = self.check_expr(index2) + index_type = self.check_expr(index) value_type = self.check_expr(value) - check_type_match(map_type.length_type, index1_type) - check_type_match(map_type.length_type, index2_type) - check_type_match(map_type.element_type, value_type) + check_type_match(map_type.key_type, index_type) + check_type_match(map_type.value_type, value_type) return None case asm.Resize(buffer, new_size): buffer_type = self.check_buffer(buffer) From 9f58e83b1a3cdf084abe8494214d084822c335bf Mon Sep 17 00:00:00 2001 From: Juni Kim Date: Tue, 18 Nov 2025 11:06:06 -0500 Subject: [PATCH 03/44] set up c buffer stuff --- src/finchlite/codegen/c.py | 9 +++-- src/finchlite/codegen/hashtable.py | 52 +++++++++++++++++++-------- src/finchlite/codegen/numpy_buffer.py | 16 ++++++--- 3 files changed, 55 insertions(+), 22 deletions(-) diff --git a/src/finchlite/codegen/c.py b/src/finchlite/codegen/c.py index 198e155e..9e40b8ea 100644 --- a/src/finchlite/codegen/c.py +++ b/src/finchlite/codegen/c.py @@ -13,6 +13,8 @@ import numpy as np +from finchlite.finch_assembly.nodes import AssemblyExpression + from .. import finch_assembly as asm from ..algebra import query_property, register_property from ..finch_assembly import AssemblyStructFType, BufferFType, TupleFType @@ -1071,7 +1073,7 @@ def struct_c_type(fmt: AssemblyStructFType): ) -def struct_c_getattr(fmt: AssemblyStructFType, ctx, obj, attr): +def struct_c_getattr(fmt: AssemblyStructFType, ctx: "CContext", obj: str, attr: str): return f"{obj}->{attr}" @@ -1083,8 +1085,9 @@ def struct_c_getattr(fmt: AssemblyStructFType, ctx, obj, attr): ) -def struct_c_setattr(fmt: AssemblyStructFType, ctx, obj, attr, val): - ctx.emit(f"{ctx.feed}{obj}->{attr} = {val};") +def struct_c_setattr(fmt: AssemblyStructFType, ctx: "CContext", obj: str, attr, val: str): + # here, val has already been computed. + ctx.exec(f"{ctx.feed}{obj}->{attr} = {val};") return diff --git a/src/finchlite/codegen/hashtable.py b/src/finchlite/codegen/hashtable.py index 13430e19..f14a8753 100644 --- a/src/finchlite/codegen/hashtable.py +++ b/src/finchlite/codegen/hashtable.py @@ -55,7 +55,7 @@ class HashTable(Map): """ def __init__( - self, key_len, value_len, map: "dict[tuple[int,int],int] | None" = None + self, key_len, value_len, map: "dict[tuple,tuple] | None" = None ): """ Constructor for the Hash Table, which maps integer tuples to integer tuples. Takes three arguments: @@ -118,6 +118,9 @@ def __eq__(self, other): return False return self.key_len == other.key_len and self.value_len == other.value_len + def __call__(self): + return HashTable(self.key_len, self.value_len, {}) + def __str__(self): return f"hashtable_t({self.key_len}, {self.value_len})" @@ -139,6 +142,17 @@ def element_type(self): """ return self._value_type + def __hash__(self): + """ + Needs to be here because you are going to be using this type as a key + in dictionaries. + """ + return hash(("HashTableFType", self.key_len, self.value_len)) + + """ + Methods for the Numba Backend + """ + def numba_jitclass_type(self) -> numba.types.Type: key_t = numba.types.UniTuple(numba.types.int64, self.key_len) value_t = numba.types.UniTuple(numba.types.int64, self.value_len) @@ -199,24 +213,34 @@ def construct_from_numba(self, numba_buffer): """ return HashTable(self.key_len, self.value_len, numba_buffer[0]) - def __hash__(self): - """ - Needs to be here because you are going to be using this type as a key - in dictionaries. - """ - return hash(("HashTableFType", self.key_len, self.value_len)) - - def __call__(self): - return HashTable(self.key_len, self.value_len, {}) + """ + Methods for the C Backend + This requires an external library (stc) to work. + """ def c_type(self): return ctypes.POINTER(CHashMap) - def c_load(self, ctx: "CContext", buf, idx): - return f"({buf.obj.data})[{ctx(idx)}]" + def c_existsmap(self, ctx: "CContext", map: "Stack", idx: "AssemblyExpression"): + # TODO: call in the methods from the c library. + assert isinstance(map.obj, CMapFields) + return "" - def c_store(self, ctx: "CContext", buf, idx, value): - ctx.exec(f"{ctx.feed}({buf.obj.data})[{ctx(idx)}] = {ctx(value)};") + def c_existsmap(self, ctx: "CContext", map: "Stack", idx: "AssemblyExpression"): + # TODO: call in the methods from the c library to do existence check. + assert isinstance(map.obj, CMapFields) + return "" + + def c_storemap( + self, + ctx: "NumbaContext", + map: "Stack", + idx: "AssemblyExpression", + value: "AssemblyExpression", + ): + # TODO: pull in library code to do this. + assert isinstance(map.obj, CMapFields) + ctx.exec("") def serialize_to_c(self, obj): """ diff --git a/src/finchlite/codegen/numpy_buffer.py b/src/finchlite/codegen/numpy_buffer.py index 0e3f308b..3c22842e 100644 --- a/src/finchlite/codegen/numpy_buffer.py +++ b/src/finchlite/codegen/numpy_buffer.py @@ -5,9 +5,11 @@ import numba +from finchlite.finch_assembly.nodes import AssemblyExpression, Stack + from ..finch_assembly import Buffer from ..util import qual_str -from .c import CBufferFType, CStackFType, c_type +from .c import CBufferFType, CContext, CStackFType, c_type from .numba_backend import NumbaBufferFType @@ -123,16 +125,20 @@ def __call__(self, len: int = 0, dtype: type | None = None): def c_type(self): return ctypes.POINTER(CNumpyBuffer) - def c_length(self, ctx, buf): + def c_length(self, ctx: "CContext", buf: "Stack"): + assert isinstance(buf.obj, CBufferFields) return buf.obj.length - def c_data(self, ctx, buf): + def c_data(self, ctx: "CContext", buf: "Stack"): + assert isinstance(buf.obj, CBufferFields) return buf.obj.data - def c_load(self, ctx, buf, idx): + def c_load(self, ctx: "CContext", buf: "Stack", idx: "AssemblyExpression"): + assert isinstance(buf.obj, CBufferFields) return f"({buf.obj.data})[{ctx(idx)}]" - def c_store(self, ctx, buf, idx, value): + def c_store(self, ctx: "CContext", buf: "Stack", idx: "AssemblyExpression", value: "AssemblyExpression"): + assert isinstance(buf.obj, CBufferFields) ctx.exec(f"{ctx.feed}({buf.obj.data})[{ctx(idx)}] = {ctx(value)};") def c_resize(self, ctx, buf, new_len): From c857b3fd3873487eabb52470e96a952e209067eb Mon Sep 17 00:00:00 2001 From: Juni Kim Date: Mon, 24 Nov 2025 22:17:36 -0500 Subject: [PATCH 04/44] at least things work a little --- src/finchlite/codegen/c.py | 22 +- src/finchlite/codegen/hashtable.py | 352 ++++++++-- src/finchlite/codegen/stc/c11/fmt.h | 301 +++++++++ src/finchlite/codegen/stc/stc/algorithm.h | 11 + src/finchlite/codegen/stc/stc/arc.h | 254 +++++++ src/finchlite/codegen/stc/stc/box.h | 168 +++++ src/finchlite/codegen/stc/stc/cbits.h | 336 +++++++++ src/finchlite/codegen/stc/stc/common.h | 359 ++++++++++ src/finchlite/codegen/stc/stc/coption.h | 180 +++++ src/finchlite/codegen/stc/stc/coroutine.h | 584 ++++++++++++++++ src/finchlite/codegen/stc/stc/cregex.h | 168 +++++ src/finchlite/codegen/stc/stc/cspan.h | 480 +++++++++++++ src/finchlite/codegen/stc/stc/cstr.h | 51 ++ src/finchlite/codegen/stc/stc/csview.h | 243 +++++++ src/finchlite/codegen/stc/stc/ctest.h | 639 ++++++++++++++++++ src/finchlite/codegen/stc/stc/deque.h | 205 ++++++ src/finchlite/codegen/stc/stc/hashmap.h | 43 ++ src/finchlite/codegen/stc/stc/hashset.h | 44 ++ src/finchlite/codegen/stc/stc/hmap.h | 517 ++++++++++++++ src/finchlite/codegen/stc/stc/hset.h | 43 ++ src/finchlite/codegen/stc/stc/list.h | 431 ++++++++++++ src/finchlite/codegen/stc/stc/pqueue.h | 185 +++++ src/finchlite/codegen/stc/stc/priv/cstr_prv.h | 420 ++++++++++++ src/finchlite/codegen/stc/stc/priv/linkage.h | 77 +++ src/finchlite/codegen/stc/stc/priv/linkage2.h | 42 ++ .../codegen/stc/stc/priv/queue_prv.h | 291 ++++++++ src/finchlite/codegen/stc/stc/priv/sort_prv.h | 136 ++++ src/finchlite/codegen/stc/stc/priv/template.h | 301 +++++++++ .../codegen/stc/stc/priv/template2.h | 72 ++ src/finchlite/codegen/stc/stc/priv/utf8_prv.h | 192 ++++++ src/finchlite/codegen/stc/stc/queue.h | 39 ++ src/finchlite/codegen/stc/stc/random.h | 248 +++++++ src/finchlite/codegen/stc/stc/rc.h | 38 ++ src/finchlite/codegen/stc/stc/smap.h | 612 +++++++++++++++++ src/finchlite/codegen/stc/stc/sort.h | 109 +++ src/finchlite/codegen/stc/stc/sortedmap.h | 46 ++ src/finchlite/codegen/stc/stc/sortedset.h | 47 ++ src/finchlite/codegen/stc/stc/sset.h | 46 ++ src/finchlite/codegen/stc/stc/stack.h | 285 ++++++++ src/finchlite/codegen/stc/stc/sys/crange.h | 118 ++++ src/finchlite/codegen/stc/stc/sys/filter.h | 185 +++++ src/finchlite/codegen/stc/stc/sys/finalize.h | 5 + src/finchlite/codegen/stc/stc/sys/sumtype.h | 172 +++++ src/finchlite/codegen/stc/stc/sys/utility.h | 188 ++++++ src/finchlite/codegen/stc/stc/types.h | 223 ++++++ src/finchlite/codegen/stc/stc/utf8.h | 37 + src/finchlite/codegen/stc/stc/vec.h | 397 +++++++++++ src/finchlite/codegen/stc/stc/zsview.h | 173 +++++ src/finchlite/finch_assembly/map.py | 3 +- src/finchlite/finch_assembly/nodes.py | 2 +- 50 files changed, 10074 insertions(+), 46 deletions(-) create mode 100644 src/finchlite/codegen/stc/c11/fmt.h create mode 100644 src/finchlite/codegen/stc/stc/algorithm.h create mode 100644 src/finchlite/codegen/stc/stc/arc.h create mode 100644 src/finchlite/codegen/stc/stc/box.h create mode 100644 src/finchlite/codegen/stc/stc/cbits.h create mode 100644 src/finchlite/codegen/stc/stc/common.h create mode 100644 src/finchlite/codegen/stc/stc/coption.h create mode 100644 src/finchlite/codegen/stc/stc/coroutine.h create mode 100644 src/finchlite/codegen/stc/stc/cregex.h create mode 100644 src/finchlite/codegen/stc/stc/cspan.h create mode 100644 src/finchlite/codegen/stc/stc/cstr.h create mode 100644 src/finchlite/codegen/stc/stc/csview.h create mode 100644 src/finchlite/codegen/stc/stc/ctest.h create mode 100644 src/finchlite/codegen/stc/stc/deque.h create mode 100644 src/finchlite/codegen/stc/stc/hashmap.h create mode 100644 src/finchlite/codegen/stc/stc/hashset.h create mode 100644 src/finchlite/codegen/stc/stc/hmap.h create mode 100644 src/finchlite/codegen/stc/stc/hset.h create mode 100644 src/finchlite/codegen/stc/stc/list.h create mode 100644 src/finchlite/codegen/stc/stc/pqueue.h create mode 100644 src/finchlite/codegen/stc/stc/priv/cstr_prv.h create mode 100644 src/finchlite/codegen/stc/stc/priv/linkage.h create mode 100644 src/finchlite/codegen/stc/stc/priv/linkage2.h create mode 100644 src/finchlite/codegen/stc/stc/priv/queue_prv.h create mode 100644 src/finchlite/codegen/stc/stc/priv/sort_prv.h create mode 100644 src/finchlite/codegen/stc/stc/priv/template.h create mode 100644 src/finchlite/codegen/stc/stc/priv/template2.h create mode 100644 src/finchlite/codegen/stc/stc/priv/utf8_prv.h create mode 100644 src/finchlite/codegen/stc/stc/queue.h create mode 100644 src/finchlite/codegen/stc/stc/random.h create mode 100644 src/finchlite/codegen/stc/stc/rc.h create mode 100644 src/finchlite/codegen/stc/stc/smap.h create mode 100644 src/finchlite/codegen/stc/stc/sort.h create mode 100644 src/finchlite/codegen/stc/stc/sortedmap.h create mode 100644 src/finchlite/codegen/stc/stc/sortedset.h create mode 100644 src/finchlite/codegen/stc/stc/sset.h create mode 100644 src/finchlite/codegen/stc/stc/stack.h create mode 100644 src/finchlite/codegen/stc/stc/sys/crange.h create mode 100644 src/finchlite/codegen/stc/stc/sys/filter.h create mode 100644 src/finchlite/codegen/stc/stc/sys/finalize.h create mode 100644 src/finchlite/codegen/stc/stc/sys/sumtype.h create mode 100644 src/finchlite/codegen/stc/stc/sys/utility.h create mode 100644 src/finchlite/codegen/stc/stc/types.h create mode 100644 src/finchlite/codegen/stc/stc/utf8.h create mode 100644 src/finchlite/codegen/stc/stc/vec.h create mode 100644 src/finchlite/codegen/stc/stc/zsview.h diff --git a/src/finchlite/codegen/c.py b/src/finchlite/codegen/c.py index 90ea6fc6..abc0ea79 100644 --- a/src/finchlite/codegen/c.py +++ b/src/finchlite/codegen/c.py @@ -9,10 +9,11 @@ from functools import lru_cache from pathlib import Path from types import NoneType -from typing import Any +from typing import Any, Callable import numpy as np +from finchlite.finch_assembly.map import MapFType, FType from finchlite.finch_assembly.nodes import AssemblyExpression from .. import finch_assembly as asm @@ -571,6 +572,9 @@ def __call__(self, prgm: asm.AssemblyNode): class CContext(Context): """ A class to represent a C environment. + + The context has functionality to track which datastructure definitions need + to get declared via the stc library. """ def __init__( @@ -599,12 +603,24 @@ def __init__( self.fptr = fptr self.types = types self.slots = slots + self.datastructures: dict[FType, Any] = {} def add_header(self, header): if header not in self._headerset: self.headers.append(header) self._headerset.add(header) + def add_datastructure(self, ftype: FType, handler: 'Callable[[CContext], Any]'): + """ + Code to add a datastructure declaration. + This is the minimum required to prevent redundancy. + """ + if ftype in self.datastructures: + return + # at least mark something is there. + self.datastructures[ftype] = None + handler(self) + def emit_global(self): """ Emit the headers for the C code. @@ -938,7 +954,7 @@ def construct_from_c(self, res): """ -class CMapFType(BufferFType, CArgumentFType, ABC): +class CMapFType(MapFType, CArgumentFType, ABC): """ Abstract base class for the ftype of datastructures. The ftype defines how the data in a Map is organized and accessed. @@ -952,7 +968,7 @@ def c_existsmap(self, ctx, map, idx): ... @abstractmethod - def c_loadmap(self, ctx, buffer, idx): + def c_loadmap(self, ctx, map, idx): """ Return C code which gets a value corresponding to a certain key. """ diff --git a/src/finchlite/codegen/hashtable.py b/src/finchlite/codegen/hashtable.py index f14a8753..6ca17934 100644 --- a/src/finchlite/codegen/hashtable.py +++ b/src/finchlite/codegen/hashtable.py @@ -1,14 +1,24 @@ import ctypes -from typing import NamedTuple +from dataclasses import dataclass +from pathlib import Path +from typing import NamedTuple, TypeAlias, TypedDict import numba -from finchlite.codegen.c import CContext, CMapFType, CStackFType +from finchlite.codegen.c import ( + CContext, + CMapFType, + CStackFType, + c_type, + load_shared_lib, +) from finchlite.codegen.numba_backend import NumbaContext, NumbaMapFType, NumbaStackFType -from finchlite.finch_assembly.nodes import AssemblyExpression, AssemblyNode, Stack +from finchlite.finch_assembly.map import Map, MapFType +from finchlite.finch_assembly.nodes import AssemblyExpression, Stack from finchlite.finch_assembly.struct import TupleFType -from ..finch_assembly.map import Map, MapFType +stcpath = Path(__file__).parent / "stc" +hashmap_h = stcpath / "stc" / "hashmap.h" class NumbaMapFields(NamedTuple): @@ -32,6 +42,7 @@ class CMapFields(NamedTuple): map: str obj: str + def _is_integer_tuple(tup, size): if not isinstance(tup, tuple) or len(tup) != size: return False @@ -41,22 +52,224 @@ def _is_integer_tuple(tup, size): return True -class CHashMap(ctypes.Structure): +class CHashMapStruct(ctypes.Structure): _fields_ = [ - ("map", ctypes.py_object), - ("data", ctypes.c_void_p), - # TODO: you need more methods to work with the data. + ("map", ctypes.c_void_p), + ("obj", ctypes.py_object), ] -class HashTable(Map): +class CHashMethods(TypedDict): + init: str + exists: str + load: str + store: str + cleanup: str + + +@dataclass +class CHashTableLibrary: + library: ctypes.CDLL + methods: CHashMethods + hmap_t: str + + +# implement the hash table datastructures +class CHashTable(Map): """ - A Hash Table that maps Z^{in_len} to Z^{out_len} + CHashTable class that basically connects up to an STC library. """ + libraries: dict[tuple[int, int], CHashTableLibrary] = {} + + @classmethod + def gen_code( + cls, + ctx: "CContext", + key_type: "TupleFType", + value_type: "TupleFType", + ) -> tuple[CHashMethods, str]: + + assert isinstance(key_type, TupleFType) + assert isinstance(value_type, TupleFType) + + key_len = len(key_type.struct_fields) + value_len = len(key_type.struct_fields) + # dereference both key and value types; as given, they are both pointers. + keytype_c = ctx.ctype_name(c_type(key_type)._type_) + valuetype_c = ctx.ctype_name(c_type(value_type)._type_) + hmap_t = ctx.freshen(f"hmap", key_len, value_len) + + ctx.add_header(f"#define T {hmap_t}, {keytype_c}, {valuetype_c}") + ctx.add_header("#define i_eq c_memcmp_eq") + ctx.add_header("#include ") + ctx.add_header(f'#include "{hashmap_h}"') + + methods: CHashMethods = { + "init": ctx.freshen("finch_hmap_init", key_len, value_len), + "exists": ctx.freshen("finch_hmap_exists", key_len, value_len), + "load": ctx.freshen("finch_hmap_load", key_len, value_len), + "store": ctx.freshen("finch_hmap_store", key_len, value_len), + "cleanup": ctx.freshen("finch_hmap_cleanup", key_len, value_len), + } + # register these methods in the datastructures. + ctx.datastructures[HashTableFType(key_len, value_len)] = methods + + # basically for the load functions, you need to provide a variable that + # can be copied. + # Yeah, so which API's should we use for load and store? + lib_code = f""" +#include +void* {methods['init']}() {{ + void* ptr = malloc(sizeof({hmap_t})); + memset(ptr, 0, sizeof({hmap_t})); + return ptr; +}} +bool {methods['exists']}({hmap_t} *map, {keytype_c}* key) {{ + return {hmap_t}_contains(map, *key); +}} +void {methods['load']}({hmap_t} *map, {keytype_c}* key, {valuetype_c}* value) {{ + const {valuetype_c}* internal_val = {hmap_t}_at(map, *key); + *value = *internal_val; +}} +void {methods['store']}({hmap_t} *map, {keytype_c}* key, {valuetype_c}* value) {{ + {hmap_t}_insert_or_assign(map, *key, *value); +}} +void {methods['cleanup']}(void* ptr) {{ + {hmap_t}* hptr = ptr; + {hmap_t}_drop(hptr); + free(hptr); +}} + """ + ctx.exec(lib_code) + + return methods, hmap_t + + @classmethod + def compile(cls, key_len: int, value_len: int) -> CHashTableLibrary: + """ + compile a library to use for the c hash table. + """ + if (key_len, value_len) in cls.libraries: + return cls.libraries[(key_len, value_len)] + key_type = TupleFType.from_tuple(tuple(int for _ in range(key_len))) + value_type = TupleFType.from_tuple(tuple(int for _ in range(value_len))) + + ctx = CContext() + methods, hmap_t = cls.gen_code(ctx, key_type, value_type) + code = ctx.emit_global() + lib = load_shared_lib(code) + + # get keystruct and value types + KeyStruct = c_type(key_type)._type_ + ValueStruct = c_type(value_type)._type_ + + init_func = getattr(lib, methods["init"]) + init_func.argtypes = [] + init_func.restype = ctypes.c_void_p + + # Exists: Takes (map*, key*) -> returns bool + exists_func = getattr(lib, methods["exists"]) + exists_func.argtypes = [ctypes.c_void_p, ctypes.POINTER(KeyStruct)] + exists_func.restype = ctypes.c_bool + + # Load: Takes (map*, key*, out_val*) -> returns void + load_func = getattr(lib, methods["load"]) + load_func.argtypes = [ + ctypes.c_void_p, + ctypes.POINTER(KeyStruct), + ctypes.POINTER(ValueStruct), + ] + load_func.restype = None + + # Store: Takes (map*, key*, val*) -> returns void + store_func = getattr(lib, methods["store"]) + store_func.argtypes = [ + ctypes.c_void_p, + ctypes.POINTER(KeyStruct), + ctypes.POINTER(ValueStruct), + ] + store_func.restype = None + + # Cleanup: Takes (map*) -> returns void + cleanup_func = getattr(lib, methods["cleanup"]) + cleanup_func.argtypes = [ctypes.c_void_p] + cleanup_func.restype = None + + cls.libraries[(key_len, value_len)] = CHashTableLibrary(lib, methods, hmap_t) + return cls.libraries[(key_len, value_len)] + def __init__( - self, key_len, value_len, map: "dict[tuple,tuple] | None" = None + self, key_len: int, value_len: int, map: "dict[tuple,tuple] | None" = None ): + """ + Constructor for the C Hash Table + """ + self.lib = self.__class__.compile(key_len, value_len) + + self.key_len = key_len + self.value_len = value_len + + if map is None: + map = {} + self.map = getattr(self.lib.library, self.lib.methods["init"])() + for key, value in map.items(): + if not _is_integer_tuple(key, key_len): + raise TypeError( + f"Supplied key {key} is not a tuple of {key_len} integers" + ) + if not _is_integer_tuple(value, value_len): + raise TypeError( + f"Supplied value {key} is not a tuple of {value_len} integers" + ) + self.store(key, value) + + def __del__(self): + getattr(self.lib.library, self.lib.methods["cleanup"])(self.map) + + def exists(self, idx: tuple) -> bool: + assert _is_integer_tuple(idx, self.key_len) + KeyStruct = c_type(self.ftype().key_type)._type_ + c_key = KeyStruct(*idx) + func = getattr(self.lib.library, self.lib.methods["exists"]) + func.restype = ctypes.c_bool + return func(self.map, ctypes.byref(c_key)) + + def load(self, idx): + assert _is_integer_tuple(idx, self.key_len) + KeyStruct = c_type(self.ftype().key_type)._type_ + ValueStruct = c_type(self.ftype().value_type)._type_ + c_key = KeyStruct(*idx) + c_value = ValueStruct() + getattr(self.lib.library, self.lib.methods["load"])( + self.map, ctypes.byref(c_key), ctypes.byref(c_value) + ) + return tuple(getattr(c_value, f) for f, _ in c_value._fields_) + + def store(self, idx, val): + assert _is_integer_tuple(idx, self.key_len) + assert _is_integer_tuple(val, self.value_len) + KeyStruct = c_type(self.ftype().key_type)._type_ + ValueStruct = c_type(self.ftype().value_type)._type_ + c_key = KeyStruct(*idx) + c_value = ValueStruct() + getattr(self.lib.library, self.lib.methods["store"])( + self.map, ctypes.byref(c_key), ctypes.byref(c_value) + ) + + def __str__(self): + return f"hashtable({self.map})" + + def ftype(self): + return HashTableFType(self.key_len, self.value_len) + + +class HashTable(Map): + """ + A Hash Table that maps Z^{in_len} to Z^{out_len} + """ + + def __init__(self, key_len, value_len, map: "dict[tuple,tuple] | None" = None): """ Constructor for the Hash Table, which maps integer tuples to integer tuples. Takes three arguments: in_len: The @@ -80,7 +293,7 @@ def __init__( @property def ftype(self): """ - Returns the ftype of the buffer, which is a NumpyBufferFType. + Returns the finch type of this hash table. """ return HashTableFType(self.key_len, self.value_len) @@ -101,13 +314,14 @@ def __str__(self): return f"hashtable({self.map})" -class HashTableFType(MapFType, CMapFType, CStackFType, NumbaMapFType, NumbaStackFType): +# class HashTableFType(MapFType, CMapFType, CStackFType, NumbaMapFType, NumbaStackFType): +class HashTableFType(CMapFType, NumbaMapFType, CStackFType, NumbaStackFType): """ A ftype for buffers that uses NumPy arrays. This is a concrete implementation of the BufferFType class. """ - def __init__(self, key_len, value_len): + def __init__(self, key_len: int, value_len: int): self.key_len = key_len self.value_len = value_len self._key_type = TupleFType.from_tuple(tuple(int for _ in range(key_len))) @@ -130,22 +344,23 @@ def __repr__(self): @property def key_type(self): """ - Returns the type used for the length of the buffer. + Returns the type of elements used as the keys of the hash table. + (some integer tuple) """ return self._key_type @property - def element_type(self): + def value_type(self): """ - Returns the type of elements stored in the buffer. - This is typically the same as the dtype used to create the buffer. + Returns the type of elements used as the value of the hash table. + (some integer tuple) """ return self._value_type def __hash__(self): """ - Needs to be here because you are going to be using this type as a key - in dictionaries. + This method needs to be here because you are going to be using this + type as a key in dictionaries. """ return hash(("HashTableFType", self.key_len, self.value_len)) @@ -158,11 +373,15 @@ def numba_jitclass_type(self) -> numba.types.Type: value_t = numba.types.UniTuple(numba.types.int64, self.value_len) return numba.types.ListType(numba.types.DictType(key_t, value_t)) - def numba_existsmap(self, ctx: "NumbaContext", map: "Stack", idx: "AssemblyExpression"): + def numba_existsmap( + self, ctx: "NumbaContext", map: "Stack", idx: "AssemblyExpression" + ): assert isinstance(map.obj, NumbaMapFields) return f"({ctx(idx)}) in {map.obj.map}" - def numba_loadmap(self, ctx: "NumbaContext", map: "Stack", idx: "AssemblyExpression"): + def numba_loadmap( + self, ctx: "NumbaContext", map: "Stack", idx: "AssemblyExpression" + ): assert isinstance(map.obj, NumbaMapFields) return f"{map.obj.map}[{ctx(idx)}]" @@ -176,7 +395,9 @@ def numba_storemap( assert isinstance(map.obj, NumbaMapFields) ctx.exec(f"{map.obj.map}[{ctx(idx)}] = {ctx(value)}") - def numba_unpack(self, ctx: "NumbaContext", var_n: str, val: "AssemblyExpression") -> NumbaMapFields: + def numba_unpack( + self, ctx: "NumbaContext", var_n: str, val: "AssemblyExpression" + ) -> NumbaMapFields: """ Unpack the buffer into Numba context. This is part of a step that will create a new slot for var_n @@ -205,7 +426,6 @@ def serialize_to_numba(self, obj: "HashTable"): def deserialize_from_numba(self, obj: "HashTable", numba_map: "list[dict]"): obj.map = numba_map[0] - return def construct_from_numba(self, numba_buffer): """ @@ -219,45 +439,95 @@ def construct_from_numba(self, numba_buffer): """ def c_type(self): - return ctypes.POINTER(CHashMap) + return ctypes.POINTER(CHashMapStruct) def c_existsmap(self, ctx: "CContext", map: "Stack", idx: "AssemblyExpression"): # TODO: call in the methods from the c library. assert isinstance(map.obj, CMapFields) - return "" - - def c_existsmap(self, ctx: "CContext", map: "Stack", idx: "AssemblyExpression"): - # TODO: call in the methods from the c library to do existence check. - assert isinstance(map.obj, CMapFields) - return "" + methods: CHashMethods = ctx.datastructures[self] + return (f"{ctx.feed}{methods['store']}({map.obj.map}, {ctx(idx)})") def c_storemap( self, - ctx: "NumbaContext", + ctx: "CContext", map: "Stack", idx: "AssemblyExpression", value: "AssemblyExpression", ): - # TODO: pull in library code to do this. assert isinstance(map.obj, CMapFields) - ctx.exec("") + methods: CHashMethods = ctx.datastructures[self] + ctx.exec(f"{methods['exists']}({map.obj.map}, {ctx(idx)}, {ctx(value)})") + + def c_loadmap(self, ctx: "CContext", map: "Stack", idx: "AssemblyExpression"): + """ + Get an expression where we can get the value corresponding to a key. + + TODO: Do we want to use pointers to tuples (standard across everything + but requires lifecycle management) + Or do we want to just use tuple values? + + This load is incomplete without this design decision. + """ + assert isinstance(map.obj, CMapFields) + methods: CHashMethods = ctx.datastructures[self] - def serialize_to_c(self, obj): + valuetype_c = ctx.ctype_name(c_type(self.value_type)._type_) + value = ctx.freshen("value") + ctx.exec(f"{ctx.feed}{valuetype_c} {value};") + ctx.exec(f"{ctx.feed}{methods['load']}({map.obj.map}, {ctx(idx)}, &{value})") + return value + + def c_unpack(self, ctx: "CContext", var_n: str, val: AssemblyExpression): + """ + Unpack the map into C context. + """ + assert val.result_format == self + data = ctx.freshen(var_n, "data") + # Add all the stupid header stuff from above. + ctx.add_datastructure( + self, + lambda ctx: CHashTable.gen_code(ctx, self.key_type, self.value_type) + ) + + ctx.exec(f"{ctx.feed}void* {data} = {ctx(val)}->map;") + return CMapFields(data, var_n) + + def c_repack(self, ctx: "CContext", lhs: str, obj: "CMapFields"): + """ + Repack the map out of C context. + """ + ctx.exec(f"{ctx.feed}{lhs}->map = {obj.map}") + + def serialize_to_c(self, obj: CHashTable): """ Serialize the Hash Map to a CHashMap structure. + This datatype will then immediately get turned into a struct. """ - data = ctypes.c_void_p(obj.arr.ctypes.data) - obj._c_buffer = CHashMap(obj._self_obj, data) - return ctypes.pointer(obj._c_buffer) + assert isinstance(obj, CHashTable) + map = ctypes.c_void_p(obj.map) + struct = CHashMapStruct(map, obj) + return ctypes.pointer(struct) - def deserialize_from_c(self, obj, c_buffer): + def deserialize_from_c(self, obj: CHashTable, res): """ Update this buffer based on how the C call modified the CNumpyBuffer structure. """ - # this is handled by the resize callback + assert isinstance(res, ctypes.POINTER(CHashMapStruct)) + assert isinstance(res.contents.obj, CHashTable) + obj.map = res.contents.map def construct_from_c(self, c_map): """ Construct a NumpyBuffer from a C-compatible structure. + + c_map is a pointer to a CHashMapStruct """ - return NumpyBuffer(c_map.contents.map) + raise NotImplementedError + + +if __name__ == "__main__": + table = CHashTable(2, 3, {}) + table.store((2, 3), (3, 2, 3)) + print(table.exists((2, 3))) + print(table.load((2, 3))) + print(table.exists((2, 1))) diff --git a/src/finchlite/codegen/stc/c11/fmt.h b/src/finchlite/codegen/stc/c11/fmt.h new file mode 100644 index 00000000..93f8d234 --- /dev/null +++ b/src/finchlite/codegen/stc/c11/fmt.h @@ -0,0 +1,301 @@ +#ifndef FMT_H_INCLUDED +#define FMT_H_INCLUDED +/* +VER 2.4 API: +void fmt_print(fmt, ...); +void fmt_println(fmt, ...); +void fmt_printd(dst, fmt, ...); +const char* fmt_time(fmt, const struct tm* tm, int MAXLEN); +void fmt_close(fmt_stream* ss); + + dst - destination, one of: + FILE* fp Write to a file + char* strbuf Write to a pre-allocated string buffer + fmt_stream* ss Write to a string-stream (auto allocated). + Set ss->overwrite=1 for overwrite-mode. + Call fmt_close(ss) after usage. + + fmt - format string (const char*) + {} Auto-detected format. If :MOD is not specified, + float will use ".8g" format, and double ".16g". + {:MODS} Format modifiers: '<' left align (replaces '-'). Default for char* and char. + '>' right align. Default for numbers. + Other than that MODS can be regular printf() format modifiers. + {{ }} % Print the '{', '}', and '%' characters. + +* C11 or higher required. +* MAX 12 arguments after fmt string. +* Define FMT_IMPLEMENT, STC_IMPLEMENT or i_implement prior to #include in one translation unit. +* (c) operamint, 2022-2025, MIT License. +----------------------------------------------------------------------------------- +#define i_implement +#include "c11/fmt.h" + +int main(void) { + const double pi = 3.141592653589793; + const size_t x = 1234567890; + const char* string = "Hello world"; + const wchar_t* wstr = L"The whole"; + const char z = 'z'; + _Bool flag = 1; + unsigned char r = 123, g = 214, b = 90, w = 110; + char buffer[64]; + + fmt_print("Color: ({} {} {}), {}\n", r, g, b, flag); + fmt_println("Wide: {}, {}", wstr, L"wide world"); + fmt_println("{:10} {:10} {:10.2f}", 42ull, 43, pi); + fmt_println("{:>10} {:>10} {:>10}", z, z, w); + fmt_printd(stdout, "{:10} {:10} {:10}\n", "Hello", "Mad", "World"); + fmt_printd(stderr, "100%: {:<20} {:.*} {}\n", string, 4, pi, x); + fmt_printd(buffer, "Precision: {} {:.10} {}", string, pi, x); + fmt_println("{}", buffer); + fmt_println("Vector: ({}, {}, {})", 3.2, 3.3, pi); + + fmt_stream ss[1] = {0}; + fmt_printd(ss, "{} {}", "Pi is:", pi); + fmt_print("{}, len={}, cap={}\n", ss->data, ss->len, ss->cap); + fmt_printd(ss, "{} {}", ", Pi squared is:", pi*pi); + fmt_print("{}, len={}, cap={}\n", ss->data, ss->len, ss->cap); + fmt_close(ss); + + time_t now = time(NULL); + struct tm t1 = *localtime(&now), t2 = t1; + t2.tm_hour += 48; + mktime(&t2); + fmt_print("Dates: {} and {}\n", fmt_time("%Y-%m-%d %X %Z", &t1, 63), + fmt_time("%Y-%m-%d %X %Z", &t2, 63)); +} +*/ +#include // IWYU pragma: keep +#include +#include +#include +#if defined _WIN32 + #include +#elif defined __APPLE__ + #include +#else + #include +#endif +#define fmt_OVERLOAD(name, ...) \ + fmt_JOIN(name ## _,fmt_NUMARGS(__VA_ARGS__))(__VA_ARGS__) +#define fmt_JOIN(a, b) _fmt_JOIN0(a, b) +#define fmt_NUMARGS(...) _fmt_APPLY_ARG_N((__VA_ARGS__, _fmt_RSEQ_N)) +#define _fmt_JOIN0(a, b) a ## b +#define _fmt_APPLY_ARG_N(args) _fmt_ARG_N args +#define _fmt_RSEQ_N 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, +#define _fmt_ARG_N(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,N,...) N + +#if defined FMT_NDEBUG || defined STC_NDEBUG || defined NDEBUG +# define fmt_OK(exp) (void)(exp) +#else +# define fmt_OK(exp) assert(exp) +#endif + +typedef struct { + char* data; + ptrdiff_t cap, len; + _Bool overwrite; +} fmt_stream; + +#if defined FMT_STATIC || defined STC_STATIC || defined i_static + #define FMT_API static + #define FMT_DEF static +#elif defined FMT_IMPLEMENT || defined STC_IMPLEMENT || defined i_implement + #define FMT_API extern + #define FMT_DEF +#else + #define FMT_API +#endif + +struct tm; +FMT_API const char* _fmt_time(const char *fmt, const struct tm* tm, char* buf, int bufsize); +FMT_API void fmt_close(fmt_stream* ss); +FMT_API int _fmt_parse(char* p, int nargs, const char *fmt, ...); +FMT_API void _fmt_sprint(fmt_stream*, const char* fmt, ...); +#define _fmt_init(_fs, _fmt, fmt, nargs) \ + const char* _fmt = fmt; \ + char* _fs = (char*)alloca(strlen(_fmt) + nargs*3 + 1) // "{}" => "%.16g" => +3 + +#define fmt_print(...) fmt_printd(stdout, __VA_ARGS__) +#define fmt_println(...) fmt_printd((fmt_stream*)0, __VA_ARGS__) +#define fmt_printd(...) fmt_OVERLOAD(fmt_printd, __VA_ARGS__) +#define fmt_time(fmt, tm, MAXLEN) _fmt_time(fmt, tm, (char[MAXLEN + 1]){0}, MAXLEN + 1) + +#define fmt_sv "{:.*s}" +#define fmt_svarg(sv) (int)(sv).size, (sv).buf + +/* Primary function. */ +#define fmt_printd_2(to, fmt) \ + do { _fmt_init(_fs, _fmt, fmt, 0); int _n = _fmt_parse(_fs, 0, _fmt); \ + fmt_OK(_n == 0); _fmt_fn(to)(to, _fmt); } while (0) +#define fmt_printd_3(to, fmt, a) \ + do { _fmt_init(_fs, _fmt, fmt, 1); int _n = _fmt_parse(_fs, 1, _fmt, _fc(a)); \ + fmt_OK(_n == 1); _fmt_fn(to)(to, _fs, a); } while (0) +#define fmt_printd_4(to, fmt, a, b) \ + do { _fmt_init(_fs, _fmt, fmt, 2); int _n = _fmt_parse(_fs, 2, _fmt, _fc(a), _fc(b)); \ + fmt_OK(_n == 2); _fmt_fn(to)(to, _fs, a, b); } while (0) +#define fmt_printd_5(to, fmt, a, b, c) \ + do { _fmt_init(_fs, _fmt, fmt, 3); int _n = _fmt_parse(_fs, 3, _fmt, _fc(a), _fc(b), _fc(c)); \ + fmt_OK(_n == 3); _fmt_fn(to)(to, _fs, a, b, c); } while (0) +#define fmt_printd_6(to, fmt, a, b, c, d) \ + do { _fmt_init(_fs, _fmt, fmt, 4); int _n = _fmt_parse(_fs, 4, _fmt, _fc(a), _fc(b), _fc(c), _fc(d)); \ + fmt_OK(_n == 4); _fmt_fn(to)(to, _fs, a, b, c, d); } while (0) +#define fmt_printd_7(to, fmt, a, b, c, d, e) \ + do { _fmt_init(_fs, _fmt, fmt, 5); int _n = _fmt_parse(_fs, 5, _fmt, _fc(a), _fc(b), _fc(c), _fc(d), _fc(e)); \ + fmt_OK(_n == 5); _fmt_fn(to)(to, _fs, a, b, c, d, e); } while (0) +#define fmt_printd_8(to, fmt, a, b, c, d, e, f) \ + do { _fmt_init(_fs, _fmt, fmt, 6); int _n = _fmt_parse(_fs, 6, _fmt, _fc(a), _fc(b), _fc(c), _fc(d), _fc(e), _fc(f)); \ + fmt_OK(_n == 6); _fmt_fn(to)(to, _fs, a, b, c, d, e, f); } while (0) +#define fmt_printd_9(to, fmt, a, b, c, d, e, f, g) \ + do { _fmt_init(_fs, _fmt, fmt, 7); int _n = _fmt_parse(_fs, 7, _fmt, _fc(a), _fc(b), _fc(c), _fc(d), _fc(e), _fc(f), _fc(g)); \ + fmt_OK(_n == 7); _fmt_fn(to)(to, _fs, a, b, c, d, e, f, g); } while (0) +#define fmt_printd_10(to, fmt, a, b, c, d, e, f, g, h) \ + do { _fmt_init(_fs, _fmt, fmt, 8); int _n = _fmt_parse(_fs, 8, _fmt, _fc(a), _fc(b), _fc(c), _fc(d), _fc(e), _fc(f), _fc(g), _fc(h)); \ + fmt_OK(_n == 8); _fmt_fn(to)(to, _fs, a, b, c, d, e, f, g, h); } while (0) +#define fmt_printd_11(to, fmt, a, b, c, d, e, f, g, h, i) \ + do { _fmt_init(_fs, _fmt, fmt, 9); int _n = _fmt_parse(_fs, 9, _fmt, _fc(a), _fc(b), _fc(c), _fc(d), _fc(e), _fc(f), _fc(g), _fc(h), \ + _fc(i)); \ + fmt_OK(_n == 9); _fmt_fn(to)(to, _fs, a, b, c, d, e, f, g, h, i); } while (0) +#define fmt_printd_12(to, fmt, a, b, c, d, e, f, g, h, i, j) \ + do { _fmt_init(_fs, _fmt, fmt, 10); int _n = _fmt_parse(_fs, 10, _fmt, _fc(a), _fc(b), _fc(c), _fc(d), _fc(e), _fc(f), _fc(g), _fc(h), \ + _fc(i), _fc(j)); \ + fmt_OK(_n == 10); _fmt_fn(to)(to, _fs, a, b, c, d, e, f, g, h, i, j); } while (0) +#define fmt_printd_13(to, fmt, a, b, c, d, e, f, g, h, i, j, k) \ + do { _fmt_init(_fs, _fmt, fmt, 11); int _n = _fmt_parse(_fs, 11, _fmt, _fc(a), _fc(b), _fc(c), _fc(d), _fc(e), _fc(f), _fc(g), _fc(h), \ + _fc(i), _fc(j), _fc(k)); \ + fmt_OK(_n == 11); _fmt_fn(to)(to, _fs, a, b, c, d, e, f, g, h, i, j, k); } while (0) +#define fmt_printd_14(to, fmt, a, b, c, d, e, f, g, h, i, j, k, l) \ + do { _fmt_init(_fs, _fmt, fmt, 12); int _n = _fmt_parse(_fs, 12, _fmt, _fc(a), _fc(b), _fc(c), _fc(d), _fc(e), _fc(f), _fc(g), _fc(h), \ + _fc(i), _fc(j), _fc(k), _fc(l)); \ + fmt_OK(_n == 12); _fmt_fn(to)(to, _fs, a, b, c, d, e, f, g, h, i, j, k, l); } while (0) + +#define _fmt_fn(x) _Generic ((x), \ + FILE*: fprintf, \ + char*: sprintf, \ + fmt_stream*: _fmt_sprint) + +#if defined(_MSC_VER) && !defined(__clang__) + #define _signed_char_hhd +#else + #define _signed_char_hhd signed char: "c", +#endif + +#if defined(__GNUC__) || defined(__clang__) || defined(__INTEL_LLVM_COMPILER) + #define FMT_UNUSED __attribute__((unused)) +#else + #define FMT_UNUSED +#endif + +#define _fc(x) _Generic (x, \ + _Bool: "d", \ + unsigned char: "hhu", \ + _signed_char_hhd \ + char: "c", \ + short: "hd", \ + unsigned short: "hu", \ + int: "d", \ + unsigned: "u", \ + long: "ld", \ + unsigned long: "lu", \ + long long: "lld", \ + unsigned long long: "llu", \ + float: "g", \ + double: "@g", \ + long double: "@Lg", \ + char*: "s", \ + wchar_t*: "ls", \ + void*: "p", \ + const char*: "s", \ + const wchar_t*: "ls", \ + const void*: "p") + +#if defined FMT_DEF + +#include +#include +#include + +FMT_DEF FMT_UNUSED void fmt_close(fmt_stream* ss) { + free(ss->data); +} + +FMT_DEF FMT_UNUSED +const char* _fmt_time(const char *fmt, const struct tm* tm, char* buf, int bufsize) { + strftime(buf, (size_t)bufsize, fmt, tm); + return buf; +} + +FMT_DEF void _fmt_sprint(fmt_stream* ss, const char* fmt, ...) { + va_list args, args2; + va_start(args, fmt); + if (ss == NULL) { + vprintf(fmt, args); putchar('\n'); + goto done1; + } + va_copy(args2, args); + const int n = vsnprintf(NULL, 0U, fmt, args); + if (n < 0) goto done2; + const ptrdiff_t pos = ss->overwrite ? 0 : ss->len; + ss->len = pos + n; + if (ss->len > ss->cap) { + ss->cap = ss->len + ss->cap/2; + ss->data = (char*)realloc(ss->data, (size_t)ss->cap + 1U); + } + vsnprintf(ss->data + pos, (size_t)n+1, fmt, args2); + done2: va_end(args2); + done1: va_end(args); +} + +FMT_DEF int _fmt_parse(char* p, int nargs, const char *fmt, ...) { + char *arg, *p0, ch; + int n = 0, empty; + va_list args; + va_start(args, fmt); + do { + switch ((ch = *fmt)) { + case '%': + *p++ = '%'; + break; + case '}': + if (*++fmt == '}') break; /* ok */ + n = 99; + continue; + case '{': + if (*++fmt == '{') break; /* ok */ + if (++n > nargs) continue; + if (*fmt != ':' && *fmt != '}') n = 99; + fmt += (*fmt == ':'); + empty = *fmt == '}'; + arg = va_arg(args, char *); + *p++ = '%', p0 = p; + while (1) switch (*fmt) { + case '\0': n = 99; /* FALLTHRU */ + case '}': goto done; + case '<': *p++ = '-', ++fmt; break; + case '>': p0 = NULL; /* FALLTHRU */ + case '-': ++fmt; break; + case '*': if (++n <= nargs) arg = va_arg(args, char *); /* FALLTHRU */ + default: *p++ = *fmt++; + } + done: + switch (*arg) { + case 'g': if (empty) memcpy(p, ".8", 2), p += 2; break; + case '@': ++arg; if (empty) memcpy(p, ".16", 3), p += 3; break; + } + if (strchr("csdioxXufFeEaAgGnp", fmt[-1]) == NULL) + while (*arg) *p++ = *arg++; + if (p0 && (p[-1] == 's' || p[-1] == 'c')) /* left-align str */ + memmove(p0 + 1, p0, (size_t)(p++ - p0)), *p0 = '-'; + fmt += *fmt == '}'; + continue; + } + *p++ = *fmt++; + } while (ch); + va_end(args); + return n; +} +#endif +#endif +#undef i_implement +#undef i_static diff --git a/src/finchlite/codegen/stc/stc/algorithm.h b/src/finchlite/codegen/stc/stc/algorithm.h new file mode 100644 index 00000000..a517a155 --- /dev/null +++ b/src/finchlite/codegen/stc/stc/algorithm.h @@ -0,0 +1,11 @@ +#ifndef STC_ALGORITHM_H_INCLUDED +#define STC_ALGORITHM_H_INCLUDED + +// IWYU pragma: begin_exports +#include "sys/crange.h" +#include "sys/filter.h" +#include "sys/utility.h" +#include "sys/sumtype.h" +// IWYU pragma: end_exports + +#endif // STC_ALGORITHM_H_INCLUDED diff --git a/src/finchlite/codegen/stc/stc/arc.h b/src/finchlite/codegen/stc/stc/arc.h new file mode 100644 index 00000000..f703bb67 --- /dev/null +++ b/src/finchlite/codegen/stc/stc/arc.h @@ -0,0 +1,254 @@ +/* MIT License + * + * Copyright (c) 2025 Tyge Løvset + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +/* arc: atomic reference counted shared_ptr (new implementation) + * + * The difference between arc and arc2 is that arc only takes up one pointer, + * whereas arc2 uses two. arc cannot be constructed from an already allocated pointer, + * which arc2 may. To use arc2, specify the `(c_arc2)` option after the key type, e.g.: + * #define T MyArc, MyType, (c_arc2 | c_no_atomic) + */ +/* +#include + +typedef struct { cstr name, last; } Person; + +Person Person_make(const char* name, const char* last) { + return (Person){.name = cstr_from(name), .last = cstr_from(last)}; +} +Person Person_clone(Person p) { + p.name = cstr_clone(p.name); + p.last = cstr_clone(p.last); + return p; +} +void Person_drop(Person* p) { + printf("drop: %s %s\n", cstr_str(&p->name), cstr_str(&p->last)); + cstr_drop(&p->name); + cstr_drop(&p->last); +} + +#define T ArcPers, Person, (c_keyclass) // clone, drop, cmp, hash +#include + +int main(void) { + ArcPers p = ArcPers_from(Person_make("John", "Smiths")); + ArcPers q = ArcPers_clone(p); // share the pointer + + printf("%s %s. uses: %ld\n", cstr_str(&q.get->name), cstr_str(&q.get->last), ArcPers_use_count(q)); + c_drop(ArcPers, &p, &q); +} +*/ +#include "priv/linkage.h" +#include "types.h" + +#ifndef STC_ARC_H_INCLUDED +#define STC_ARC_H_INCLUDED +#include "common.h" +#include + +#if defined __GNUC__ || defined __clang__ || defined _MSC_VER || defined i_no_atomic + typedef long catomic_long; +#else // try with C11 + typedef _Atomic(long) catomic_long; +#endif +#if defined _MSC_VER + #include + #define c_atomic_inc(v) (void)_InterlockedIncrement(v) + #define c_atomic_dec_and_test(v) !_InterlockedDecrement(v) +#elif defined __GNUC__ || defined __clang__ + #define c_atomic_inc(v) (void)__atomic_add_fetch(v, 1, __ATOMIC_SEQ_CST) + #define c_atomic_dec_and_test(v) !__atomic_sub_fetch(v, 1, __ATOMIC_SEQ_CST) +#else // try with C11 + #include + #define c_atomic_inc(v) (void)atomic_fetch_add(v, 1) + #define c_atomic_dec_and_test(v) (atomic_fetch_sub(v, 1) == 1) +#endif +#endif // STC_ARC_H_INCLUDED + +#ifndef _i_prefix + #define _i_prefix arc_ +#endif +#define _i_is_arc +#include "priv/template.h" +typedef i_keyraw _m_raw; + +#if c_OPTION(c_no_atomic) + #define i_no_atomic +#endif +#if !defined i_no_atomic + #define _i_atomic_inc(v) c_atomic_inc(v) + #define _i_atomic_dec_and_test(v) c_atomic_dec_and_test(v) +#else + #define _i_atomic_inc(v) (void)(++*(v)) + #define _i_atomic_dec_and_test(v) !(--*(v)) +#endif + +#if c_OPTION(c_arc2) + #define i_arc2 +#endif +#if !(defined i_arc2 || defined STC_USE_ARC2) +// ------------ Arc1 size of one pointer (union) ------------- + +#ifndef i_declared +_c_DEFTYPES(declare_arc, Self, i_key); +#endif +struct _c_MEMB(_ctrl) { + _m_value value; + catomic_long counter; +}; + +// c++: std::make_shared<_m_value>(val) +STC_INLINE Self _c_MEMB(_make)(_m_value val) { + Self arc = {.ctrl=_i_new_n(_c_MEMB(_ctrl), 1)}; + arc.ctrl->value = val; + arc.ctrl->counter = 1; + return arc; +} + +STC_INLINE Self _c_MEMB(_toarc)(_m_value* arc_raw) + { Self arc = {.ctrl=(_c_MEMB(_ctrl) *)arc_raw}; return arc; } + +// destructor +STC_INLINE void _c_MEMB(_drop)(const Self* self) { + if (self->ctrl && _i_atomic_dec_and_test(&self->ctrl->counter)) { + i_keydrop(self->get); + i_free(self->ctrl, c_sizeof *self->ctrl); + } +} + +#else // ------------ Arc2 size of two pointers ------------- + +#ifndef i_declared +_c_DEFTYPES(declare_arc2, Self, i_key); +#endif +struct _c_MEMB(_ctrl) { + catomic_long counter; // nb! counter <-> value order is swapped. + _m_value value; +}; +#define ctrl ctrl2 + +// c++: std::make_shared<_m_value>(val) +STC_INLINE Self _c_MEMB(_make)(_m_value val) { + Self out = {.ctrl2=_i_new_n(_c_MEMB(_ctrl), 1)}; + out.ctrl2->counter = 1; + out.get = &out.ctrl2->value; + *out.get = val; + return out; +} + +STC_INLINE Self _c_MEMB(_from_ptr)(_m_value* ptr) { + Self out = {.get=ptr}; + if (ptr) { + enum {OFFSET = offsetof(_c_MEMB(_ctrl), value)}; + // Adds 2 dummy bytes to ensure that the second if-test in _drop() is safe. + catomic_long* _rc = (catomic_long*)i_malloc(OFFSET + 2); + out.ctrl2 = (_c_MEMB(_ctrl)*) _rc; + out.ctrl2->counter = 1; + } + return out; +} + +// destructor +STC_INLINE void _c_MEMB(_drop)(const Self* self) { + if (self->ctrl2 && _i_atomic_dec_and_test(&self->ctrl2->counter)) { + enum {OFFSET = offsetof(_c_MEMB(_ctrl), value)}; + i_keydrop(self->get); + + if ((char*)self->ctrl2 + OFFSET == (char*)self->get) { + i_free((void*)self->ctrl2, c_sizeof *self->ctrl2); // _make() + } else { + i_free((void*)self->ctrl2, OFFSET + 2); // _from_ptr() + i_free(self->get, c_sizeof *self->get); + } + } +} + +// take ownership of pointer p +STC_INLINE void _c_MEMB(_reset_to)(Self* self, _m_value* ptr) { + _c_MEMB(_drop)(self); + *self = _c_MEMB(_from_ptr)(ptr); +} + +#endif // ---------- end Arc2 with two pointers ------------ + +STC_INLINE long _c_MEMB(_use_count)(Self arc) + { return arc.ctrl ? arc.ctrl->counter : 0; } + +STC_INLINE Self _c_MEMB(_init)(void) + { return c_literal(Self){0}; } + +STC_INLINE Self _c_MEMB(_from)(_m_raw raw) + { return _c_MEMB(_make)(i_keyfrom(raw)); } + +STC_INLINE _m_raw _c_MEMB(_toraw)(const Self* self) + { return i_keytoraw(self->get); } + +// move ownership to receiving arc +STC_INLINE Self _c_MEMB(_move)(Self* self) { + Self arc = *self; + *self = (Self){0}; + return arc; // now unowned +} + +// take ownership of unowned arc +STC_INLINE void _c_MEMB(_take)(Self* self, Self unowned) { + _c_MEMB(_drop)(self); + *self = unowned; // now owned +} + +// make shared ownership with owned arc +STC_INLINE void _c_MEMB(_assign)(Self* self, const Self* owned) { + if (owned->ctrl) + _i_atomic_inc(&owned->ctrl->counter); + _c_MEMB(_drop)(self); + *self = *owned; +} + +// clone by sharing. Does not use i_keyclone, so OK to always define. +STC_INLINE Self _c_MEMB(_clone)(Self owned) { + if (owned.ctrl) + _i_atomic_inc(&owned.ctrl->counter); + return owned; +} + +#if defined _i_has_cmp + STC_INLINE int _c_MEMB(_raw_cmp)(const _m_raw* rx, const _m_raw* ry) + { return i_cmp(rx, ry); } +#endif + +#if defined _i_has_eq + STC_INLINE bool _c_MEMB(_raw_eq)(const _m_raw* rx, const _m_raw* ry) + { return i_eq(rx, ry); } +#endif + +#if !defined i_no_hash && defined _i_has_eq + STC_INLINE size_t _c_MEMB(_raw_hash)(const _m_raw* rx) + { return i_hash(rx); } +#endif // i_no_hash + +#undef ctrl +#undef i_no_atomic +#undef i_arc2 +#undef _i_atomic_inc +#undef _i_atomic_dec_and_test +#undef _i_is_arc +#include "sys/finalize.h" diff --git a/src/finchlite/codegen/stc/stc/box.h b/src/finchlite/codegen/stc/stc/box.h new file mode 100644 index 00000000..366bda58 --- /dev/null +++ b/src/finchlite/codegen/stc/stc/box.h @@ -0,0 +1,168 @@ + +/* MIT License + * + * Copyright (c) 2025 Tyge Løvset + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* cbox: heap allocated boxed type +#include + +typedef struct { cstr name, email; } Person; + +Person Person_from(const char* name, const char* email) { + return (Person){.name = cstr_from(name), .email = cstr_from(email)}; +} +Person Person_clone(Person p) { + p.name = cstr_clone(p.name); + p.email = cstr_clone(p.email); + return p; +} +void Person_drop(Person* p) { + printf("drop: %s %s\n", cstr_str(&p->name), cstr_str(&p->email)); + c_drop(cstr, &p->name, &p->email); +} + +#define T PBox, Person, (c_keyclass) // bind Person clone+drop fn's +#include + +int main(void) { + PBox p = PBox_from(Person_from("John Smiths", "josmiths@gmail.com")); + PBox q = PBox_clone(p); + cstr_assign(&q.get->name, "Joe Smiths"); + + printf("%s %s.\n", cstr_str(&p.get->name), cstr_str(&p.get->email)); + printf("%s %s.\n", cstr_str(&q.get->name), cstr_str(&q.get->email)); + + c_drop(PBox, &p, &q); +} +*/ +#include "priv/linkage.h" +#include "types.h" + +#ifndef STC_BOX_H_INCLUDED +#define STC_BOX_H_INCLUDED +#include "common.h" +#include + +#define cbox_null {0} +#endif // STC_BOX_H_INCLUDED + +#ifndef _i_prefix + #define _i_prefix box_ +#endif +#define _i_is_box +#include "priv/template.h" +typedef i_keyraw _m_raw; + +#ifndef i_declared +_c_DEFTYPES(declare_box, Self, i_key); +#endif + +// constructors (take ownership) +STC_INLINE Self _c_MEMB(_init)(void) + { return c_literal(Self){0}; } + +STC_INLINE long _c_MEMB(_use_count)(const Self* self) + { return (long)(self->get != NULL); } + + +// c++: std::make_unique(val) +STC_INLINE Self _c_MEMB(_make)(_m_value val) { + Self box = {_i_new_n(_m_value, 1)}; + *box.get = val; + return box; +} + +STC_INLINE Self _c_MEMB(_from_ptr)(_m_value* p) + { return c_literal(Self){p}; } + +STC_INLINE Self _c_MEMB(_from)(_m_raw raw) + { return _c_MEMB(_make)(i_keyfrom(raw)); } + +STC_INLINE _m_raw _c_MEMB(_toraw)(const Self* self) + { return i_keytoraw(self->get); } + +// destructor +STC_INLINE void _c_MEMB(_drop)(const Self* self) { + if (self->get) { + i_keydrop(self->get); + i_free(self->get, c_sizeof *self->get); + } +} + +// move ownership to receiving box +STC_INLINE Self _c_MEMB(_move)(Self* self) { + Self box = *self; + self->get = NULL; + return box; +} + +// release owned pointer, must be manually freed by receiver +STC_INLINE _m_value* _c_MEMB(_release)(Self* self) + { return _c_MEMB(_move)(self).get; } + +// take ownership of pointer p +STC_INLINE void _c_MEMB(_reset_to)(Self* self, _m_value* p) { + _c_MEMB(_drop)(self); + self->get = p; +} + +// take ownership of unowned box +STC_INLINE void _c_MEMB(_take)(Self* self, Self unowned) { + _c_MEMB(_drop)(self); + *self = unowned; +} + +// transfer ownership from other; set other to NULL +STC_INLINE void _c_MEMB(_assign)(Self* self, Self* owned) { + if (owned->get == self->get) + return; + _c_MEMB(_drop)(self); + *self = *owned; + owned->get = NULL; +} + +#ifndef i_no_clone + STC_INLINE Self _c_MEMB(_clone)(Self other) { + if (other.get == NULL) return other; + Self out = {_i_new_n(_m_value, 1)}; + *out.get = i_keyclone((*other.get)); + return out; + } +#endif // !i_no_clone + + +#if defined _i_has_cmp + STC_INLINE int _c_MEMB(_raw_cmp)(const _m_raw* rx, const _m_raw* ry) + { return i_cmp(rx, ry); } +#endif + +#if defined _i_has_eq + STC_INLINE bool _c_MEMB(_raw_eq)(const _m_raw* rx, const _m_raw* ry) + { return i_eq(rx, ry); } +#endif + +#if !defined i_no_hash && defined _i_has_eq + STC_INLINE size_t _c_MEMB(_raw_hash)(const _m_raw* rx) + { return i_hash(rx); } +#endif // i_no_hash +#undef _i_is_box +#include "sys/finalize.h" diff --git a/src/finchlite/codegen/stc/stc/cbits.h b/src/finchlite/codegen/stc/stc/cbits.h new file mode 100644 index 00000000..ac424ba4 --- /dev/null +++ b/src/finchlite/codegen/stc/stc/cbits.h @@ -0,0 +1,336 @@ +/* MIT License + * + * Copyright (c) 2025 Tyge Løvset + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +/* +Similar to boost::dynamic_bitset / std::bitset + +#include +#include "cbits.h" + +int main(void) { + cbits bset = cbits_with_size(23, true); + cbits_reset(&bset, 9); + cbits_resize(&bset, 43, false); + + printf("%4d: ", (int)cbits_size(&bset)); + for (c_range(i, cbits_size(&bset))) + printf("%d", cbits_at(&bset, i)); + puts(""); + cbits_set(&bset, 28); + cbits_resize(&bset, 77, true); + cbits_resize(&bset, 93, false); + cbits_resize(&bset, 102, true); + cbits_set_value(&bset, 99, false); + + printf("%4d: ", (int)cbits_size(&bset)); + for (c_range(i, cbits_size(&bset))) + printf("%d", cbits_at(&bset, i)); + puts(""); + + cbits_drop(&bset); +} +*/ +#include "priv/linkage.h" +#ifndef STC_CBITS_H_INCLUDED +#define STC_CBITS_H_INCLUDED +#include "common.h" +#include + +#if INTPTR_MAX == INT64_MAX +#define _gnu_popc(x) __builtin_popcountll(x) +#define _msc_popc(x) (int)__popcnt64(x) +#else +#define _gnu_popc(x) __builtin_popcount(x) +#define _msc_popc(x) (int)__popcnt(x) +#endif +#define _cbits_WS c_sizeof(uintptr_t) +#define _cbits_WB (8*_cbits_WS) +#define _cbits_bit(i) ((uintptr_t)1 << ((i) & (_cbits_WB - 1))) +#define _cbits_words(n) (isize)(((n) + (_cbits_WB - 1))/_cbits_WB) +#define _cbits_bytes(n) (_cbits_words(n)*_cbits_WS) + +#if defined _MSC_VER + #include + STC_INLINE int c_popcount(uintptr_t x) { return _msc_popc(x); } +#elif defined __GNUC__ || defined __clang__ + STC_INLINE int c_popcount(uintptr_t x) { return _gnu_popc(x); } +#else + STC_INLINE int c_popcount(uintptr_t x) { /* http://en.wikipedia.org/wiki/Hamming_weight */ + x -= (x >> 1) & (uintptr_t)0x5555555555555555; + x = (x & (uintptr_t)0x3333333333333333) + ((x >> 2) & (uintptr_t)0x3333333333333333); + x = (x + (x >> 4)) & (uintptr_t)0x0f0f0f0f0f0f0f0f; + return (int)((x*(uintptr_t)0x0101010101010101) >> (_cbits_WB - 8)); + } +#endif +#if defined __GNUC__ && !defined __clang__ && !defined __cplusplus +#pragma GCC diagnostic ignored "-Walloc-size-larger-than=" // gcc 11.4 +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" // gcc 11.4 +#endif + +#define cbits_print(...) c_MACRO_OVERLOAD(cbits_print, __VA_ARGS__) +#define cbits_print_1(self) cbits_print_4(self, stdout, 0, -1) +#define cbits_print_2(self, stream) cbits_print_4(self, stream, 0, -1) +#define cbits_print_4(self, stream, start, end) cbits_print_5(cbits, self, stream, start, end) +#define cbits_print_3(SetType, self, stream) cbits_print_5(SetType, self, stream, 0, -1) +#define cbits_print_5(SetType, self, stream, start, end) do { \ + const SetType* _cb_set = self; \ + isize _cb_start = start, _cb_end = end; \ + if (_cb_end == -1) _cb_end = SetType##_size(_cb_set); \ + for (c_range_3(_cb_i, _cb_start, _cb_end)) \ + fputc(SetType##_test(_cb_set, _cb_i) ? '1' : '0', stream); \ +} while (0) + +STC_INLINE isize _cbits_count(const uintptr_t* set, const isize sz) { + const isize n = sz/_cbits_WB; + isize count = 0; + for (isize i = 0; i < n; ++i) + count += c_popcount(set[i]); + if (sz & (_cbits_WB - 1)) + count += c_popcount(set[n] & (_cbits_bit(sz) - 1)); + return count; +} + +STC_INLINE char* _cbits_to_str(const uintptr_t* set, const isize sz, + char* out, isize start, isize stop) { + if (stop > sz) stop = sz; + c_assert(start <= stop); + + c_memset(out, '0', stop - start); + for (isize i = start; i < stop; ++i) + if ((set[i/_cbits_WB] & _cbits_bit(i)) != 0) + out[i - start] = '1'; + out[stop - start] = '\0'; + return out; +} + +#define _cbits_OPR(OPR, VAL) \ + const isize n = sz/_cbits_WB; \ + for (isize i = 0; i < n; ++i) \ + if ((set[i] OPR other[i]) != VAL) \ + return false; \ + if ((sz & (_cbits_WB - 1)) == 0) \ + return true; \ + const uintptr_t i = (uintptr_t)n, m = _cbits_bit(sz) - 1; \ + return ((set[i] OPR other[i]) & m) == (VAL & m) + +STC_INLINE bool _cbits_subset_of(const uintptr_t* set, const uintptr_t* other, const isize sz) + { _cbits_OPR(|, set[i]); } + +STC_INLINE bool _cbits_disjoint(const uintptr_t* set, const uintptr_t* other, const isize sz) + { _cbits_OPR(&, 0); } + +#endif // STC_CBITS_H_INCLUDED + +#if defined T && !defined i_type + #define i_type T +#endif +#if defined i_type + #define Self c_GETARG(1, i_type) + #define _i_length c_GETARG(2, i_type) +#else + #define Self cbits +#endif +#ifndef i_allocator + #define i_allocator c +#endif +#define _i_MEMB(name) c_JOIN(Self, name) + + +#if !defined _i_length // DYNAMIC SIZE BITARRAY + +typedef struct { uintptr_t *buffer; isize _size; } Self; +#define _i_assert(x) c_assert(x) + +STC_INLINE void cbits_drop(cbits* self) { i_free(self->buffer, _cbits_bytes(self->_size)); } +STC_INLINE isize cbits_size(const cbits* self) { return self->_size; } + +STC_INLINE cbits* cbits_take(cbits* self, cbits other) { + if (self->buffer != other.buffer) { + cbits_drop(self); + *self = other; + } + return self; +} + +STC_INLINE cbits cbits_clone(cbits other) { + cbits set = other; + const isize bytes = _cbits_bytes(other._size); + set.buffer = (uintptr_t *)c_safe_memcpy(i_malloc(bytes), other.buffer, bytes); + return set; +} + +STC_INLINE cbits* cbits_copy(cbits* self, const cbits* other) { + if (self->buffer == other->buffer) + return self; + if (self->_size != other->_size) + return cbits_take(self, cbits_clone(*other)); + c_memcpy(self->buffer, other->buffer, _cbits_bytes(other->_size)); + return self; +} + +STC_INLINE bool cbits_resize(cbits* self, const isize size, const bool value) { + const isize new_w = _cbits_words(size), osize = self->_size, old_w = _cbits_words(osize); + uintptr_t* b = (uintptr_t *)i_realloc(self->buffer, old_w*_cbits_WS, new_w*_cbits_WS); + if (b == NULL) return false; + self->buffer = b; self->_size = size; + if (size > osize) { + c_memset(self->buffer + old_w, -(int)value, (new_w - old_w)*_cbits_WS); + if (osize & (_cbits_WB - 1)) { + uintptr_t mask = _cbits_bit(osize) - 1; + if (value) self->buffer[old_w - 1] |= ~mask; + else self->buffer[old_w - 1] &= mask; + } + } + return true; +} + +STC_INLINE void cbits_set_all(cbits *self, const bool value); +STC_INLINE void cbits_set_pattern(cbits *self, const uintptr_t pattern); + +STC_INLINE cbits cbits_move(cbits* self) { + cbits tmp = *self; + self->buffer = NULL, self->_size = 0; + return tmp; +} + +STC_INLINE cbits cbits_with_size(const isize size, const bool value) { + cbits set = {(uintptr_t *)i_malloc(_cbits_bytes(size)), size}; + cbits_set_all(&set, value); + return set; +} + +STC_INLINE cbits cbits_with_pattern(const isize size, const uintptr_t pattern) { + cbits set = {(uintptr_t *)i_malloc(_cbits_bytes(size)), size}; + cbits_set_pattern(&set, pattern); + return set; +} + +#else // _i_length: FIXED SIZE BITARRAY + +#define _i_assert(x) (void)0 + +typedef struct { uintptr_t buffer[(_i_length - 1)/_cbits_WB + 1]; } Self; + +STC_INLINE void _i_MEMB(_drop)(Self* self) { (void)self; } +STC_INLINE isize _i_MEMB(_size)(const Self* self) { (void)self; return _i_length; } +STC_INLINE Self _i_MEMB(_move)(Self* self) { return *self; } +STC_INLINE Self* _i_MEMB(_take)(Self* self, Self other) { *self = other; return self; } +STC_INLINE Self _i_MEMB(_clone)(Self other) { return other; } +STC_INLINE void _i_MEMB(_copy)(Self* self, const Self* other) { *self = *other; } +STC_INLINE void _i_MEMB(_set_all)(Self *self, const bool value); +STC_INLINE void _i_MEMB(_set_pattern)(Self *self, const uintptr_t pattern); + +STC_INLINE Self _i_MEMB(_with_size)(const isize size, const bool value) { + c_assert(size <= _i_length); + Self set; _i_MEMB(_set_all)(&set, value); + return set; +} + +STC_INLINE Self _i_MEMB(_with_pattern)(const isize size, const uintptr_t pattern) { + c_assert(size <= _i_length); + Self set; _i_MEMB(_set_pattern)(&set, pattern); + return set; +} +#endif // _i_length + +// COMMON: + +STC_INLINE void _i_MEMB(_set_all)(Self *self, const bool value) + { c_memset(self->buffer, -(int)value, _cbits_bytes(_i_MEMB(_size)(self))); } + +STC_INLINE void _i_MEMB(_set_pattern)(Self *self, const uintptr_t pattern) { + isize n = _cbits_words(_i_MEMB(_size)(self)); + while (n--) self->buffer[n] = pattern; +} + +STC_INLINE bool _i_MEMB(_test)(const Self* self, const isize i) + { return (self->buffer[i/_cbits_WB] & _cbits_bit(i)) != 0; } + +STC_INLINE bool _i_MEMB(_at)(const Self* self, const isize i) + { c_assert(c_uless(i, _i_MEMB(_size)(self))); return _i_MEMB(_test)(self, i); } + +STC_INLINE void _i_MEMB(_set)(Self *self, const isize i) + { self->buffer[i/_cbits_WB] |= _cbits_bit(i); } + +STC_INLINE void _i_MEMB(_reset)(Self *self, const isize i) + { self->buffer[i/_cbits_WB] &= ~_cbits_bit(i); } + +STC_INLINE void _i_MEMB(_set_value)(Self *self, const isize i, const bool b) { + self->buffer[i/_cbits_WB] ^= ((uintptr_t)-(int)b ^ self->buffer[i/_cbits_WB]) & _cbits_bit(i); +} + +STC_INLINE void _i_MEMB(_flip)(Self *self, const isize i) + { self->buffer[i/_cbits_WB] ^= _cbits_bit(i); } + +STC_INLINE void _i_MEMB(_flip_all)(Self *self) { + isize n = _cbits_words(_i_MEMB(_size)(self)); + while (n--) self->buffer[n] ^= ~(uintptr_t)0; +} + +STC_INLINE Self _i_MEMB(_from)(const char* str) { + isize n = c_strlen(str); + Self set = _i_MEMB(_with_size)(n, false); + while (n--) if (str[n] == '1') _i_MEMB(_set)(&set, n); + return set; +} + +/* Intersection */ +STC_INLINE void _i_MEMB(_intersect)(Self *self, const Self* other) { + _i_assert(self->_size == other->_size); + isize n = _cbits_words(_i_MEMB(_size)(self)); + while (n--) self->buffer[n] &= other->buffer[n]; +} +/* Union */ +STC_INLINE void _i_MEMB(_union)(Self *self, const Self* other) { + _i_assert(self->_size == other->_size); + isize n = _cbits_words(_i_MEMB(_size)(self)); + while (n--) self->buffer[n] |= other->buffer[n]; +} +/* Exclusive disjunction */ +STC_INLINE void _i_MEMB(_xor)(Self *self, const Self* other) { + _i_assert(self->_size == other->_size); + isize n = _cbits_words(_i_MEMB(_size)(self)); + while (n--) self->buffer[n] ^= other->buffer[n]; +} + +STC_INLINE isize _i_MEMB(_count)(const Self* self) + { return _cbits_count(self->buffer, _i_MEMB(_size)(self)); } + +STC_INLINE char* _i_MEMB(_to_str)(const Self* self, char* out, isize start, isize stop) + { return _cbits_to_str(self->buffer, _i_MEMB(_size)(self), out, start, stop); } + +STC_INLINE bool _i_MEMB(_subset_of)(const Self* self, const Self* other) { + _i_assert(self->_size == other->_size); + return _cbits_subset_of(self->buffer, other->buffer, _i_MEMB(_size)(self)); +} + +STC_INLINE bool _i_MEMB(_disjoint)(const Self* self, const Self* other) { + _i_assert(self->_size == other->_size); + return _cbits_disjoint(self->buffer, other->buffer, _i_MEMB(_size)(self)); +} + +#include "priv/linkage2.h" +#undef i_type +#undef _i_length +#undef _i_MEMB +#undef _i_assert +#undef Self diff --git a/src/finchlite/codegen/stc/stc/common.h b/src/finchlite/codegen/stc/stc/common.h new file mode 100644 index 00000000..fce1829d --- /dev/null +++ b/src/finchlite/codegen/stc/stc/common.h @@ -0,0 +1,359 @@ +/* MIT License + * + * Copyright (c) 2025 Tyge Løvset + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef STC_COMMON_H_INCLUDED +#define STC_COMMON_H_INCLUDED + +#ifdef _MSC_VER + #pragma warning(disable: 4116 4996) // unnamed type definition in parentheses +#endif +#include +#include +#include +#include +#include + +typedef ptrdiff_t isize; +#ifndef STC_NO_INT_DEFS + typedef int8_t int8; + typedef uint8_t uint8; + typedef int16_t int16; + typedef uint16_t uint16; + typedef int32_t int32; + typedef uint32_t uint32; + typedef int64_t int64; + typedef uint64_t uint64; +#endif +#if !defined STC_HAS_TYPEOF && (_MSC_FULL_VER >= 193933428 || \ + defined __GNUC__ || defined __clang__ || defined __TINYC__) + #define STC_HAS_TYPEOF 1 +#endif +#if defined __GNUC__ + #define c_GNUATTR(...) __attribute__((__VA_ARGS__)) +#else + #define c_GNUATTR(...) +#endif +#define STC_INLINE static inline c_GNUATTR(unused) +#define c_ZI PRIiPTR +#define c_ZU PRIuPTR +#define c_NPOS INTPTR_MAX + +// Macro overloading feature support +#define c_MACRO_OVERLOAD(name, ...) \ + c_JOIN(name ## _,c_NUMARGS(__VA_ARGS__))(__VA_ARGS__) +#define c_JOIN0(a, b) a ## b +#define c_JOIN(a, b) c_JOIN0(a, b) +#define c_NUMARGS(...) _c_APPLY_ARG_N((__VA_ARGS__, _c_RSEQ_N)) +#define _c_APPLY_ARG_N(args) _c_ARG_N args +#define _c_RSEQ_N 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, +#define _c_ARG_N(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,N,...) N + +// Saturated overloading +// #define foo(...) foo_I(__VA_ARGS__, c_COMMA_N(foo_3), c_COMMA_N(foo_2), c_COMMA_N(foo_1),)(__VA_ARGS__) +// #define foo_I(a,b,c, n, ...) c_TUPLE_AT_1(n, foo_n,) +#define c_TUPLE_AT_1(x,y,...) y +#define c_COMMA_N(x) ,x +#define c_EXPAND(...) __VA_ARGS__ + +// Select arg, e.g. for #define i_type A,B then c_GETARG(2, i_type) is B +#define c_GETARG(N, ...) c_ARG_##N(__VA_ARGS__,) +#define c_ARG_1(a, ...) a +#define c_ARG_2(a, b, ...) b +#define c_ARG_3(a, b, c, ...) c +#define c_ARG_4(a, b, c, d, ...) d + +#define _i_new_n(T, n) ((T*)i_malloc((n)*c_sizeof(T))) +#define _i_new_zeros(T, n) ((T*)i_calloc(n, c_sizeof(T))) +#define _i_realloc_n(ptr, old_n, n) i_realloc(ptr, (old_n)*c_sizeof *(ptr), (n)*c_sizeof *(ptr)) +#define _i_free_n(ptr, n) i_free(ptr, (n)*c_sizeof *(ptr)) + +#ifndef __cplusplus + #define c_new(T, ...) ((T*)c_safe_memcpy(c_malloc(c_sizeof(T)), ((T[]){__VA_ARGS__}), c_sizeof(T))) + #define c_literal(T) (T) + #define c_make_array(T, ...) ((T[])__VA_ARGS__) + #define c_make_array2d(T, N, ...) ((T[][N])__VA_ARGS__) +#else + #include + #define c_new(T, ...) new (c_malloc(c_sizeof(T))) T(__VA_ARGS__) + #define c_literal(T) T + template struct _c_Array { T data[M][N]; }; + #define c_make_array(T, ...) (_c_Array{{__VA_ARGS__}}.data[0]) + #define c_make_array2d(T, N, ...) (_c_Array{__VA_ARGS__}.data) +#endif + +#ifdef STC_ALLOCATOR + #define c_malloc c_JOIN(STC_ALLOCATOR, _malloc) + #define c_calloc c_JOIN(STC_ALLOCATOR, _calloc) + #define c_realloc c_JOIN(STC_ALLOCATOR, _realloc) + #define c_free c_JOIN(STC_ALLOCATOR, _free) +#else + #define c_malloc(sz) malloc(c_i2u_size(sz)) + #define c_calloc(n, sz) calloc(c_i2u_size(n), c_i2u_size(sz)) + #define c_realloc(ptr, old_sz, sz) realloc(ptr, c_i2u_size(1 ? (sz) : (old_sz))) + #define c_free(ptr, sz) ((void)(sz), free(ptr)) +#endif + +#define c_new_n(T, n) ((T*)c_malloc((n)*c_sizeof(T))) +#define c_free_n(ptr, n) c_free(ptr, (n)*c_sizeof *(ptr)) +#define c_realloc_n(ptr, old_n, n) c_realloc(ptr, (old_n)*c_sizeof *(ptr), (n)*c_sizeof *(ptr)) +#define c_delete_n(T, ptr, n) do { \ + T* _tp = ptr; isize _n = n, _i = _n; \ + while (_i--) T##_drop((_tp + _i)); \ + c_free(_tp, _n*c_sizeof(T)); \ +} while (0) + +#define c_static_assert(expr) (void)sizeof(int[(expr) ? 1 : -1]) +#if defined STC_NDEBUG || defined NDEBUG + #define c_assert(expr) (void)sizeof(expr) +#else + #define c_assert(expr) assert(expr) +#endif +#define c_container_of(p, C, m) ((C*)((char*)(1 ? (p) : &((C*)0)->m) - offsetof(C, m))) +#define c_const_cast(Tp, p) ((Tp)(1 ? (p) : (Tp)0)) +#define c_litstrlen(literal) (c_sizeof("" literal) - 1) +#define c_countof(a) (isize)(sizeof(a)/sizeof 0[a]) +#define c_arraylen(a) c_countof(a) // [deprecated]? + +// expect signed ints to/from these (use with gcc -Wconversion) +#define c_sizeof (isize)sizeof +#define c_strlen(s) (isize)strlen(s) +#define c_strncmp(a, b, ilen) strncmp(a, b, c_i2u_size(ilen)) +#define c_memcpy(d, s, ilen) memcpy(d, s, c_i2u_size(ilen)) +#define c_memmove(d, s, ilen) memmove(d, s, c_i2u_size(ilen)) +#define c_memset(d, val, ilen) memset(d, val, c_i2u_size(ilen)) +#define c_memcmp(a, b, ilen) memcmp(a, b, c_i2u_size(ilen)) +// library internal, but may be useful in user code: +#define c_u2i_size(u) (isize)(1 ? (u) : (size_t)1) // warns if u is signed +#define c_i2u_size(i) (size_t)(1 ? (i) : -1) // warns if i is unsigned +#define c_uless(a, b) ((size_t)(a) < (size_t)(b)) +#define c_safe_cast(T, From, x) ((T)(1 ? (x) : (From){0})) + +// x, y are i_keyraw* type, which defaults to i_key*. vp is i_key* type. +#define c_memcmp_eq(x, y) (memcmp(x, y, sizeof *(x)) == 0) +#define c_default_eq(x, y) (*(x) == *(y)) +#define c_default_less(x, y) (*(x) < *(y)) +#define c_default_cmp(x, y) (c_default_less(y, x) - c_default_less(x, y)) +#define c_default_hash(vp) c_hash_n(vp, sizeof *(vp)) +#define c_default_clone(v) (v) +#define c_default_toraw(vp) (*(vp)) +#define c_default_drop(vp) ((void) (vp)) + +// non-owning char pointer +typedef const char* cstr_raw; +#define cstr_raw_cmp(x, y) strcmp(*(x), *(y)) +#define cstr_raw_eq(x, y) (cstr_raw_cmp(x, y) == 0) +#define cstr_raw_hash(vp) c_hash_str(*(vp)) +#define cstr_raw_clone(v) (v) +#define cstr_raw_drop(vp) ((void)vp) + +// Control block macros + +// [deprecated]: +#define c_init(...) c_make(__VA_ARGS__) +#define c_forlist(...) for (c_items(_VA_ARGS__)) +#define c_foritems(...) for (c_items(__VA_ARGS__)) +#define c_foreach(...) for (c_each(__VA_ARGS__)) +#define c_foreach_n(...) for (c_each_n(__VA_ARGS__)) +#define c_foreach_kv(...) for (c_each_kv(__VA_ARGS__)) +#define c_foreach_reverse(...) for (c_each_reverse(__VA_ARGS__)) +#define c_forrange(...) for (c_range(__VA_ARGS__)) +#define c_forrange32(...) for (c_range32(__VA_ARGS__)) + +// New: +#define c_each(...) c_MACRO_OVERLOAD(c_each, __VA_ARGS__) +#define c_each_3(it, C, cnt) \ + C##_iter it = C##_begin(&cnt); it.ref; C##_next(&it) +#define c_each_4(it, C, start, end) \ + _c_each(it, C, start, (end).ref, _) + +#define c_each_item(v, C, cnt) \ + C##_value* v = (C##_value*)&v; v; ) \ + for (C##_iter v##_it = C##_begin(&cnt); (v = v##_it.ref); C##_next(&v##_it) + +#define c_each_n(...) c_MACRO_OVERLOAD(c_each_n, __VA_ARGS__) +#define c_each_n_3(it, C, cnt) c_each_n_4(it, C, cnt, INTPTR_MAX) +#define c_each_n_4(it, C, cnt, n) \ + struct {C##_iter iter; C##_value* ref; isize size, index;} \ + it = {.iter=C##_begin(&cnt), .size=n}; (it.ref = it.iter.ref) && it.index < it.size; C##_next(&it.iter), ++it.index + +#define c_each_reverse(...) c_MACRO_OVERLOAD(c_each_reverse, __VA_ARGS__) +#define c_each_reverse_3(it, C, cnt) /* works for stack, vec, queue, deque */ \ + C##_iter it = C##_rbegin(&cnt); it.ref; C##_rnext(&it) +#define c_each_reverse_4(it, C, start, end) \ + _c_each(it, C, start, (end).ref, _r) + +#define _c_each(it, C, start, endref, rev) /* private */ \ + C##_iter it = (start), *_endref_##it = c_safe_cast(C##_iter*, C##_value*, endref) \ + ; it.ref != (C##_value*)_endref_##it; C##rev##next(&it) + +#define c_each_kv(...) c_MACRO_OVERLOAD(c_each_kv, __VA_ARGS__) +#define c_each_kv_4(key, val, C, cnt) /* structured binding for maps */ \ + _c_each_kv(key, val, C, C##_begin(&cnt), NULL) +#define c_each_kv_5(key, val, C, start, end) \ + _c_each_kv(key, val, C, start, (end).ref) + +#define _c_each_kv(key, val, C, start, endref) /* private */ \ + const C##_key *key = (const C##_key*)&key; key; ) \ + for (C##_mapped *val; key; key = NULL) \ + for (C##_iter _it_##key = start, *_endref_##key = c_safe_cast(C##_iter*, C##_value*, endref); \ + _it_##key.ref != (C##_value*)_endref_##key && (key = &_it_##key.ref->first, val = &_it_##key.ref->second); \ + C##_next(&_it_##key) + +#define c_items(it, T, ...) \ + struct {T* ref; int size, index;} \ + it = {.ref=c_make_array(T, __VA_ARGS__), .size=(int)(sizeof((T[])__VA_ARGS__)/sizeof(T))} \ + ; it.index < it.size ; ++it.ref, ++it.index + +// c_range, c_range32: python-like int range iteration +#define c_range_t(...) c_MACRO_OVERLOAD(c_range_t, __VA_ARGS__) +#define c_range_t_3(T, i, stop) c_range_t_4(T, i, 0, stop) +#define c_range_t_4(T, i, start, stop) \ + T i=start, _c_end_##i=stop; i < _c_end_##i; ++i +#define c_range_t_5(T, i, start, stop, step) \ + T i=start, _c_inc_##i=step, _c_end_##i=(stop) - (_c_inc_##i > 0) \ + ; (_c_inc_##i > 0) == (i <= _c_end_##i) ; i += _c_inc_##i + +#define c_range(...) c_MACRO_OVERLOAD(c_range, __VA_ARGS__) +#define c_range_1(stop) c_range_t_4(isize, _c_i1, 0, stop) +#define c_range_2(i, stop) c_range_t_4(isize, i, 0, stop) +#define c_range_3(i, start, stop) c_range_t_4(isize, i, start, stop) +#define c_range_4(i, start, stop, step) c_range_t_5(isize, i, start, stop, step) + +#define c_range32(...) c_MACRO_OVERLOAD(c_range32, __VA_ARGS__) +#define c_range32_2(i, stop) c_range_t_4(int32_t, i, 0, stop) +#define c_range32_3(i, start, stop) c_range_t_4(int32_t, i, start, stop) +#define c_range32_4(i, start, stop, step) c_range_t_5(int32_t, i, start, stop, step) + +// make container from a literal list +#define c_make(C, ...) \ + C##_from_n(c_make_array(C##_raw, __VA_ARGS__), c_sizeof((C##_raw[])__VA_ARGS__)/c_sizeof(C##_raw)) + +// put multiple raw-type elements from a literal list into a container +#define c_put_items(C, cnt, ...) \ + C##_put_n(cnt, c_make_array(C##_raw, __VA_ARGS__), c_sizeof((C##_raw[])__VA_ARGS__)/c_sizeof(C##_raw)) + +// drop multiple containers of same type +#define c_drop(C, ...) \ + do { for (c_items(_c_i2, C*, {__VA_ARGS__})) C##_drop(*_c_i2.ref); } while(0) + +// RAII scopes +#define c_defer(...) \ + for (int _c_i3 = 0; _c_i3++ == 0; __VA_ARGS__) + +#define c_with(...) c_MACRO_OVERLOAD(c_with, __VA_ARGS__) +#define c_with_2(init, deinit) \ + for (int _c_i4 = 0; _c_i4 == 0; ) for (init; _c_i4++ == 0; deinit) +#define c_with_3(init, condition, deinit) \ + for (int _c_i5 = 0; _c_i5 == 0; ) for (init; _c_i5++ == 0 && (condition); deinit) + +// General functions + +STC_INLINE void* c_safe_memcpy(void* dst, const void* src, isize size) + { return dst ? memcpy(dst, src, (size_t)size) : NULL; } + +#if INTPTR_MAX == INT64_MAX + #define FNV_BASIS 0xcbf29ce484222325 + #define FNV_PRIME 0x00000100000001b3 +#else + #define FNV_BASIS 0x811c9dc5 + #define FNV_PRIME 0x01000193 +#endif + +STC_INLINE size_t c_basehash_n(const void* key, isize len) { + const uint8_t* msg = (const uint8_t*)key; + size_t h = FNV_BASIS, block = 0; + + while (len >= c_sizeof h) { + memcpy(&block, msg, sizeof h); + h ^= block; + h *= FNV_PRIME; + msg += c_sizeof h; + len -= c_sizeof h; + } + while (len--) { + h ^= *(msg++); + h *= FNV_PRIME; + } + return h; +} + +STC_INLINE size_t c_hash_n(const void* key, isize len) { + uint64_t b8; uint32_t b4; + switch (len) { + case 8: memcpy(&b8, key, 8); return (size_t)(b8 * 0xc6a4a7935bd1e99d); + case 4: memcpy(&b4, key, 4); return b4 * FNV_BASIS; + default: return c_basehash_n(key, len); + } +} + +STC_INLINE size_t c_hash_str(const char *str) { + const uint8_t* msg = (const uint8_t*)str; + size_t h = FNV_BASIS; + while (*msg) { + h ^= *(msg++); + h *= FNV_PRIME; + } + return h; +} + +#define c_hash_mix(...) /* non-commutative hash combine */ \ + c_hash_mix_n(c_make_array(size_t, {__VA_ARGS__}), c_sizeof((size_t[]){__VA_ARGS__})/c_sizeof(size_t)) + +STC_INLINE size_t c_hash_mix_n(size_t h[], isize n) { + for (isize i = 1; i < n; ++i) h[0] += h[0] ^ h[i]; + return h[0]; +} + +// generic typesafe swap +#define c_swap(xp, yp) do { \ + (void)sizeof((xp) == (yp)); \ + char _tv[sizeof *(xp)]; \ + void *_xp = xp, *_yp = yp; \ + memcpy(_tv, _xp, sizeof _tv); \ + memcpy(_xp, _yp, sizeof _tv); \ + memcpy(_yp, _tv, sizeof _tv); \ +} while (0) + +// get next power of two +STC_INLINE isize c_next_pow2(isize n) { + n--; + n |= n >> 1, n |= n >> 2; + n |= n >> 4, n |= n >> 8; + n |= n >> 16; + #if INTPTR_MAX == INT64_MAX + n |= n >> 32; + #endif + return n + 1; +} + +STC_INLINE char* c_strnstrn(const char *str, isize slen, const char *needle, isize nlen) { + if (nlen == 0) return (char *)str; + if (nlen > slen) return NULL; + slen -= nlen; + do { + if (*str == *needle && !c_memcmp(str, needle, nlen)) + return (char *)str; + ++str; + } while (slen--); + return NULL; +} +#endif // STC_COMMON_H_INCLUDED diff --git a/src/finchlite/codegen/stc/stc/coption.h b/src/finchlite/codegen/stc/stc/coption.h new file mode 100644 index 00000000..67650645 --- /dev/null +++ b/src/finchlite/codegen/stc/stc/coption.h @@ -0,0 +1,180 @@ +/* MIT License + * + * Copyright (c) 2025 Tyge Løvset + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +/* +Inspired by https://attractivechaos.wordpress.com/2018/08/31/a-survey-of-argument-parsing-libraries-in-c-c +Fixed major bugs with optional arguments (both long and short). +Added arg->optstr output field, more consistent API. + +coption_get() is similar to GNU's getopt_long(). Each call parses one option and +returns the option name. opt->arg points to the option argument if present. +The function returns -1 when all command-line arguments are parsed. In this case, +opt->ind is the index of the first non-option argument. + +#include +#include + +int main(int argc, char *argv[]) +{ + coption_long longopts[] = { + {"foo", coption_no_argument, 'f'}, + {"bar", coption_required_argument, 'b'}, + {"opt", coption_optional_argument, 'o'}, + {0} + }; + const char* optstr = "xy:z::123"; + printf("program -x -y ARG -z [ARG] -1 -2 -3 --foo --bar ARG --opt [ARG] [ARGUMENTS]\n"); + int c; + coption opt = coption_init(); + while ((c = coption_get(&opt, argc, argv, optstr, longopts)) != -1) { + switch (c) { + case '?': printf("error: unknown option: %s\n", opt.optstr); return 1; + case ':': printf("error: missing argument for %s (%c)\n", opt.optstr, opt.opt); return 2; + default: printf("option: %c [%s]\n", opt.opt, opt.arg ? opt.arg : ""); break; + } + } + printf("\nNon-option arguments:"); + for (int i = opt.ind; i < argc; ++i) + printf(" %s", argv[i]); + putchar('\n'); + return 0; +} +*/ +#ifndef STC_COPTION_H_INCLUDED +#define STC_COPTION_H_INCLUDED + +#include +#include + +typedef enum { + coption_no_argument, + coption_required_argument, + coption_optional_argument +} coption_type; + +typedef struct { + const char *name; + coption_type type; + int val; +} coption_long; + +typedef struct { + int ind; /* equivalent to optind */ + int opt; /* equivalent to optopt */ + const char *optstr; /* points to the option string */ + const char *arg; /* equivalent to optarg */ + int _i, _pos, _nargs; + char _optstr[4]; +} coption; + +static inline coption coption_init(void) { + coption opt = {1, 0, NULL, NULL, 1, 0, 0, {'-', '?', '\0'}}; + return opt; +} + +/* move argv[j] over n elements to the left */ +static void coption_permute_(char *argv[], int j, int n) { + int k; + char *p = argv[j]; + for (k = 0; k < n; ++k) + argv[j - k] = argv[j - k - 1]; + argv[j - k] = p; +} + +/* @param opt output; must be initialized to coption_init() on first call + * @return ASCII val for a short option; longopt.val for a long option; + * -1 if argv[] is fully processed; '?' for an unknown option or + * an ambiguous long option; ':' if an option argument is missing + */ +static int coption_get(coption *opt, int argc, char *argv[], + const char *shortopts, const coption_long *longopts) { + int optc = -1, i0, j, posixly_correct = (shortopts && shortopts[0] == '+'); + if (!posixly_correct) { + while (opt->_i < argc && (argv[opt->_i][0] != '-' || argv[opt->_i][1] == '\0')) + ++opt->_i, ++opt->_nargs; + } + opt->opt = 0, opt->optstr = NULL, opt->arg = NULL, i0 = opt->_i; + if (opt->_i >= argc || argv[opt->_i][0] != '-' || argv[opt->_i][1] == '\0') { + opt->ind = opt->_i - opt->_nargs; + return -1; + } + if (argv[opt->_i][0] == '-' && argv[opt->_i][1] == '-') { /* "--" or a long option */ + if (argv[opt->_i][2] == '\0') { /* a bare "--" */ + coption_permute_(argv, opt->_i, opt->_nargs); + ++opt->_i, opt->ind = opt->_i - opt->_nargs; + return -1; + } + optc = '?', opt->_pos = -1; + if (longopts) { /* parse long options */ + int k, n_exact = 0, n_partial = 0; + const coption_long *o = 0, *o_exact = 0, *o_partial = 0; + for (j = 2; argv[opt->_i][j] != '\0' && argv[opt->_i][j] != '='; ++j) {} /* find the end of the option name */ + for (k = 0; longopts[k].name != 0; ++k) + if (strncmp(&argv[opt->_i][2], longopts[k].name, (size_t)(j - 2)) == 0) { + if (longopts[k].name[j - 2] == 0) ++n_exact, o_exact = &longopts[k]; + else ++n_partial, o_partial = &longopts[k]; + } + opt->optstr = argv[opt->_i]; + if (n_exact > 1 || (n_exact == 0 && n_partial > 1)) return '?'; + o = n_exact == 1? o_exact : n_partial == 1? o_partial : 0; + if (o) { + opt->opt = optc = o->val; + if (o->type != coption_no_argument) { + if (argv[opt->_i][j] == '=') + opt->arg = &argv[opt->_i][j + 1]; + else if (argv[opt->_i][j] == '\0' && opt->_i < argc - 1 && (o->type == coption_required_argument || + argv[opt->_i + 1][0] != '-')) + opt->arg = argv[++opt->_i]; + else if (o->type == coption_required_argument) + optc = ':'; /* missing option argument */ + } + } + } + } else if (shortopts) { /* a short option */ + const char *p; + if (opt->_pos == 0) opt->_pos = 1; + optc = opt->opt = argv[opt->_i][opt->_pos++]; + opt->_optstr[1] = optc, opt->optstr = opt->_optstr; + p = strchr(shortopts, optc); + if (p == 0) { + optc = '?'; /* unknown option */ + } else if (p[1] == ':') { + if (argv[opt->_i][opt->_pos] != '\0') + opt->arg = &argv[opt->_i][opt->_pos]; + else if (opt->_i < argc - 1 && (p[2] != ':' || argv[opt->_i + 1][0] != '-')) + opt->arg = argv[++opt->_i]; + else if (p[2] != ':') + optc = ':'; + opt->_pos = -1; + } + } + if (opt->_pos < 0 || argv[opt->_i][opt->_pos] == 0) { + ++opt->_i, opt->_pos = 0; + if (opt->_nargs > 0) /* permute */ + for (j = i0; j < opt->_i; ++j) + coption_permute_(argv, j, opt->_nargs); + } + opt->ind = opt->_i - opt->_nargs; + return optc; +} + +#endif // STC_COPTION_H_INCLUDED diff --git a/src/finchlite/codegen/stc/stc/coroutine.h b/src/finchlite/codegen/stc/stc/coroutine.h new file mode 100644 index 00000000..b7a7163b --- /dev/null +++ b/src/finchlite/codegen/stc/stc/coroutine.h @@ -0,0 +1,584 @@ +/* MIT License + * + * Copyright (c) 2025 Tyge Løvset + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef STC_COROUTINE_H_INCLUDED +#define STC_COROUTINE_H_INCLUDED +/* +#include +#include + +struct iterpair { + cco_base base; // required member + int max_x, max_y; + int x, y; +}; + +int iterpair(struct iterpair* I) { + cco_async (I) { + for (I->x = 0; I->x < I->max_x; I->x++) + for (I->y = 0; I->y < I->max_y; I->y++) + cco_yield; // suspend + } + + puts("done"); + return 0; // CCO_DONE +} + +int main(void) { + struct iterpair it = {.max_x=3, .max_y=3}; + int n = 0; + while (iterpair(&it)) + { + printf("%d %d\n", it.x, it.y); + // example of early stop: + if (++n == 7) cco_stop(&it); // signal to stop/finalize in next + } + return 0; +} +*/ +#include +#include "common.h" + +enum { + CCO_STATE_INIT = 0, + CCO_STATE_DONE = -1, + CCO_STATE_DROP = -2, +}; +enum cco_status { + CCO_DONE = 0, + CCO_YIELD = 1<<12, + CCO_SUSPEND = 1<<13, + CCO_AWAIT = 1<<14, +}; +#define CCO_CANCEL (1U<<30) + +typedef struct { + int spawn_count; + int await_count; +} cco_group; // waitgroup + +#define cco_state_struct(Prefix) \ + struct Prefix##_state { \ + int32_t pos:24; \ + bool drop; \ + struct Prefix##_fiber* fb; \ + cco_group* wg; \ + } + +#define cco_is_initial(co) ((co)->base.state.pos == CCO_STATE_INIT) +#define cco_is_done(co) ((co)->base.state.pos == CCO_STATE_DONE) +#define cco_is_active(co) ((co)->base.state.pos != CCO_STATE_DONE) + +#if defined STC_HAS_TYPEOF && STC_HAS_TYPEOF + #define _cco_state(co) __typeof__((co)->base.state) + #define _cco_validate_task_struct(co) \ + c_static_assert(/* error: co->base not first member in task struct */ \ + sizeof((co)->base) == sizeof(cco_base) || \ + offsetof(__typeof__(*(co)), base) == 0) +#else + #define _cco_state(co) cco_state + #define _cco_validate_task_struct(co) (void)0 +#endif + +#define cco_async(co) \ + if (0) goto _resume_lbl; \ + else for (_cco_state(co)* _state = (_cco_validate_task_struct(co), (_cco_state(co)*) &(co)->base.state) \ + ; _state->pos != CCO_STATE_DONE \ + ; _state->pos = CCO_STATE_DONE, \ + (void)(sizeof((co)->base) > sizeof(cco_base) && _state->wg ? --_state->wg->spawn_count : 0)) \ + _resume_lbl: switch (_state->pos) case CCO_STATE_INIT: // thanks, @liigo! + +#define cco_finalize /* label */ \ + _state->drop = true; /* FALLTHRU */ \ + case CCO_STATE_DROP + +#define cco_drop cco_finalize // [deprecated] +#define cco_cleanup [fix: use cco_finalize:] +#define cco_routine [fix: use cco_async] + +#define cco_stop(co) \ + do { \ + cco_state* _s = (cco_state*)&(co)->base.state; \ + if (!_s->drop) { _s->pos = CCO_STATE_DROP; _s->drop = true; } \ + } while (0) + +#define cco_reset_state(co) \ + do { \ + cco_state* _s = (cco_state*)&(co)->base.state; \ + _s->pos = CCO_STATE_INIT, _s->drop = false; \ + } while (0) + +#define cco_return \ + do { \ + _state->pos = (_state->drop ? CCO_STATE_DONE : CCO_STATE_DROP); \ + _state->drop = true; \ + goto _resume_lbl; \ + } while (0) + +#define cco_exit() \ + do { \ + _state->pos = CCO_STATE_DONE; \ + goto _resume_lbl; \ + } while (0) + +#define cco_yield_v(status) \ + do { \ + _state->pos = __LINE__; return status; \ + case __LINE__:; \ + } while (0) + +#define cco_yield \ + cco_yield_v(CCO_YIELD) + +#define cco_suspend \ + cco_yield_v(CCO_SUSPEND) + +#define cco_await(until) \ + do { \ + _state->pos = __LINE__; /* FALLTHRU */ \ + case __LINE__: if (!(until)) return CCO_AWAIT; \ + } while (0) + +/* cco_await_coroutine(): assumes coroutine returns a status value (int) */ +#define cco_await_coroutine(...) c_MACRO_OVERLOAD(cco_await_coroutine, __VA_ARGS__) +#define cco_await_coroutine_1(corocall) cco_await_coroutine_2(corocall, CCO_DONE) +#define cco_await_coroutine_2(corocall, awaitbits) \ + do { \ + _state->pos = __LINE__; /* FALLTHRU */ \ + case __LINE__: { \ + int _res = corocall; \ + if (_res & ~(awaitbits)) return _res; \ + } \ + } while (0) + +/* cco_run_coroutine(): assumes coroutine returns a status value (int) */ +#define cco_run_coroutine(corocall) \ + while ((1 ? (corocall) : -1) != CCO_DONE) + + +/* + * Tasks and Fibers + */ +struct cco_error { + int32_t code, line; + const char* file; +}; + +#define cco_fiber_struct(Prefix, Env) \ + typedef Env Prefix##_env; \ + struct Prefix##_fiber { \ + struct cco_task* task; \ + Prefix##_env* env; \ + cco_group* wgcurr; \ + struct cco_task* parent_task; \ + struct cco_task_fiber* next; \ + struct cco_task_state recover_state; \ + struct cco_error err; \ + int awaitbits, status; \ + cco_base base; /* is a coroutine object itself */ \ + } + +/* Define a Task struct */ +#define cco_task_struct(...) c_MACRO_OVERLOAD(cco_task_struct, __VA_ARGS__) +#define cco_task_struct_1(Task) \ + cco_task_struct_2(Task, void) + +#define cco_task_struct_2(Task, Env) \ + cco_fiber_struct(Task, Env); \ + cco_state_struct(Task); \ + _cco_task_struct(Task) + +#define _cco_task_struct(Task) \ + struct Task; \ + typedef struct { \ + int (*func)(struct Task*); \ + int awaitbits; \ + struct Task##_state state; \ + struct cco_task* parent_task; \ + } Task##_base; \ + struct Task + +/* Base cco_task type */ +typedef cco_state_struct(cco_task) cco_state; +typedef struct { cco_state state; } cco_base; +cco_fiber_struct(cco_task, void); +_cco_task_struct(cco_task) { cco_task_base base; }; +typedef struct cco_task_fiber cco_fiber; +typedef struct cco_task cco_task; + +#define cco_err() (&_state->fb->err) +#define cco_status() (_state->fb->status + 0) +#define cco_fb(task) ((cco_fiber*)(task)->base.state.fb + 0) +#define cco_env(task) (task)->base.state.fb->env +#define cco_set_env(task, the_env) ((task)->base.state.fb->env = the_env) + +#define cco_cast_task(...) \ + ((void)sizeof((__VA_ARGS__)->base.func(__VA_ARGS__)), (cco_task *)(__VA_ARGS__)) + +/* Return with error and unwind await stack; must be recovered in cco_finalize section */ +#define cco_throw(error_code) \ + do { \ + cco_fiber* _fb = (cco_fiber*)_state->fb; \ + _fb->err.code = error_code; \ + _fb->err.line = __LINE__; \ + _fb->err.file = __FILE__; \ + cco_return; \ + } while (0) + +#define cco_cancel_fiber(a_fiber) \ + do { \ + cco_fiber* _fb1 = a_fiber; \ + _fb1->err.code = CCO_CANCEL; \ + _fb1->err.line = __LINE__; \ + _fb1->err.file = __FILE__; \ + cco_stop(_fb1->task); \ + } while (0) + +/* Cancel job/task and unwind await stack; MAY be stopped (recovered) in cco_finalize section */ +/* Equals cco_throw(CCO_CANCEL) if a_task is in current fiber. */ +#define cco_cancel_task(a_task) \ + do { \ + cco_task* _tsk1 = cco_cast_task(a_task); \ + cco_cancel_fiber(_tsk1->base.state.fb); \ + cco_stop(_tsk1); \ + if (_tsk1 == _state->fb->task) goto _resume_lbl; \ + } while (0) + +#define cco_await_cancel_task(a_task) do { \ + cco_cancel_task(a_task); \ + cco_await_task(a_task); \ +} while (0) + + +#define cco_cancel_group(waitgroup) \ + _cco_cancel_group((cco_fiber*)_state->fb, waitgroup) + +#define cco_cancel_all() \ + for (cco_fiber *_fbi = _state->fb->next; _fbi != (cco_fiber*)_state->fb; _fbi = _fbi->next) \ + cco_cancel_fiber(_fbi) \ + +/* Recover the thrown error; to be used in cco_finalize section upon handling cco_err()->code */ +#define cco_recover \ + do { \ + cco_fiber* _fb = (cco_fiber*)_state->fb; \ + c_assert(_fb->err.code); \ + _fb->task->base.state = _fb->recover_state; \ + _fb->err.code = 0; \ + goto _resume_lbl; \ + } while (0) + +/* Asymmetric coroutine await/call */ +#define cco_await_task(...) c_MACRO_OVERLOAD(cco_await_task, __VA_ARGS__) +#define cco_await_task_1(a_task) cco_await_task_2(a_task, CCO_DONE) +#define cco_await_task_2(a_task, _awaitbits) do { \ + { cco_task* _await_task = cco_cast_task(a_task); \ + (void)sizeof(cco_env(a_task) == _state->fb->env); \ + cco_fiber* _fb = (cco_fiber*)_state->fb; \ + _await_task->base.awaitbits = (_awaitbits); \ + _await_task->base.parent_task = _fb->task; \ + _fb->task = _await_task; \ + _await_task->base.state.fb = _fb; \ + } \ + cco_suspend; \ +} while (0) + +/* Symmetric coroutine flow of control transfer */ +#define cco_yield_to(a_task) do { \ + { cco_task* _to_task = cco_cast_task(a_task); \ + (void)sizeof(cco_env(a_task) == _state->fb->env); \ + cco_fiber* _fb = (cco_fiber*)_state->fb; \ + _to_task->base.awaitbits = _fb->task->base.awaitbits; \ + _to_task->base.parent_task = NULL; \ + _fb->task = _to_task; \ + _to_task->base.state.fb = _fb; \ + } \ + cco_suspend; \ +} while (0) + +#define cco_resume(a_task) \ + _cco_resume_task(cco_cast_task(a_task)) + +static inline int _cco_resume_task(cco_task* task) + { return task->base.func(task); } + +/* + * cco_run_fiber()/cco_run_task(): Run fibers/tasks in parallel + */ +#define cco_new_fiber(...) c_MACRO_OVERLOAD(cco_new_fiber, __VA_ARGS__) +#define cco_new_fiber_1(task) \ + _cco_new_fiber(cco_cast_task(task), NULL, NULL) +#define cco_new_fiber_2(task, env) \ + _cco_new_fiber(cco_cast_task(task), ((void)sizeof((env) == cco_env(task)), env), NULL) + +#define cco_reset_group(wg) ((wg)->spawn_count = 0) +#define cco_spawn(...) c_MACRO_OVERLOAD(cco_spawn, __VA_ARGS__) +#define cco_spawn_1(task) cco_spawn_4(task, NULL, NULL, _state->fb) +#define cco_spawn_2(task, wg) cco_spawn_4(task, wg, NULL, _state->fb) +#define cco_spawn_3(task, wg, env) cco_spawn_4(task, wg, env, _state->fb) +#define cco_spawn_4(task, wg, env, fiber) \ + _cco_spawn(cco_cast_task(task), wg, ((void)sizeof((env) == cco_env(task)), env), \ + (cco_fiber*)((void)sizeof((fiber)->parent_task), fiber)) + +#define cco_await_group(waitgroup) do { \ + cco_task* top = _state->fb->task; \ + while (top->base.parent_task) \ + top = top->base.parent_task; \ + _state->fb->wgcurr = waitgroup; \ + /* wait until spawn_count = 1 to not wait for itself to finish, else until it is 0 */ \ + _state->fb->wgcurr->await_count = (top->base.state.wg == _state->fb->wgcurr); \ + cco_await(_state->fb->wgcurr->spawn_count == _state->fb->wgcurr->await_count); \ +} while (0) + +#define cco_await_n(waitgroup, n) do { \ + _state->fb->wgcurr = waitgroup; \ + _state->fb->wgcurr->await_count = _state->fb->wgcurr->spawn_count - (n); \ + cco_await(_state->fb->wgcurr->spawn_count == _state->fb->wgcurr->await_count); \ +} while (0) + +#define cco_await_cancel_group(waitgroup) do { \ + cco_group* wg = waitgroup; \ + cco_cancel_group(wg); \ + cco_await_group(wg); \ +} while (0) + +#define cco_await_any(waitgroup) do { \ + cco_await_n(waitgroup, 1); \ + cco_cancel_group(_state->fb->wgcurr); \ + cco_await_group(_state->fb->wgcurr); \ +} while (0) + +#define cco_run_fiber(...) c_MACRO_OVERLOAD(cco_run_fiber, __VA_ARGS__) +#define cco_run_fiber_1(fiber_ref) \ + for (cco_fiber** _it_ref = (cco_fiber**)((void)sizeof((fiber_ref)[0]->env), fiber_ref) \ + ; (*_it_ref = cco_execute_next(*_it_ref)) != NULL; ) +#define cco_run_fiber_2(it, fiber) \ + for (cco_fiber* it = (cco_fiber*)((void)sizeof((fiber)->env), fiber) \ + ; (it = cco_execute_next(it)) != NULL; ) + +#define cco_run_task(...) c_MACRO_OVERLOAD(cco_run_task, __VA_ARGS__) +#define cco_run_task_1(task) cco_run_fiber_2(_it_fb, cco_new_fiber_1(task)) +#define cco_run_task_2(task, env) cco_run_fiber_2(_it_fb, cco_new_fiber_2(task, env)) +#define cco_run_task_3(it, task, env) cco_run_fiber_2(it, cco_new_fiber_2(task, env)) + +#define cco_joined() \ + ((cco_fiber*)_state->fb == _state->fb->next) + +extern int cco_execute(cco_fiber* fb); // is a coroutine itself +extern cco_fiber* cco_execute_next(cco_fiber* fb); // resume and return the next fiber + +extern cco_fiber* _cco_new_fiber(cco_task* task, void* env, cco_group* wg); +extern cco_fiber* _cco_spawn(cco_task* task, cco_group* wg, void* env, cco_fiber* fb); +extern void _cco_cancel_group(cco_fiber* fb, cco_group* waitgroup); + +/* + * Iterate containers with already defined iterator (prefer to use in coroutines only): + */ +#define cco_each(existing_it, C, cnt) \ + existing_it = C##_begin(&cnt); (existing_it).ref; C##_next(&existing_it) + +#define cco_each_reverse(existing_it, C, cnt) \ + existing_it = C##_rbegin(&cnt); (existing_it).ref; C##_rnext(&existing_it) + +/* + * Using c_filter with coroutine iterators: + */ +#define cco_flt_take(n) \ + (c_flt_take(n), fltbase.done ? (_it.base.state.pos = CCO_STATE_DROP, _it.base.state.drop = 1) : 1) + +#define cco_flt_takewhile(pred) \ + (c_flt_takewhile(pred), fltbase.done ? (_it.base.state.pos = CCO_STATE_DROP, _it.base.state.drop = 1) : 1) + + +/* + * Semaphore + */ + +typedef struct { ptrdiff_t acq_count; } cco_semaphore; + +#define cco_make_semaphore(value) (c_literal(cco_semaphore){value}) +#define cco_set_semaphore(sem, value) ((sem)->acq_count = value) +#define cco_acquire_semaphore(sem) (--(sem)->acq_count) +#define cco_release_semaphore(sem) (++(sem)->acq_count) + +#define cco_await_semaphore(sem) \ + do { \ + cco_await((sem)->acq_count > 0); \ + cco_acquire_semaphore(sem); \ + } while (0) + + +/* + * Timer + */ + +#ifdef _WIN32 + #ifdef __cplusplus + #define _c_LINKC extern "C" __declspec(dllimport) + #else + #define _c_LINKC __declspec(dllimport) + #endif + #ifndef _WINDOWS_ // windows.h + typedef long long LARGE_INTEGER; + _c_LINKC int __stdcall QueryPerformanceCounter(LARGE_INTEGER*); + //_c_LINKC int __stdcall QueryPerformanceFrequency(LARGE_INTEGER*); + #endif + #define cco_timer_freq() 10000000LL /* 1/10th microseconds */ + //static inline long long cco_timer_freq(void) { + // long long quad; + // QueryPerformanceFrequency((LARGE_INTEGER*)&quad); + // return quad; + //} + + static inline long long cco_timer_ticks(void) { + long long quad; + QueryPerformanceCounter((LARGE_INTEGER*)&quad); + return quad; + } +#else + #include + #define cco_timer_freq() 1000000LL + + static inline long long cco_timer_ticks(void) { /* microseconds */ + struct timeval tv; + gettimeofday(&tv, NULL); + return tv.tv_sec*cco_timer_freq() + tv.tv_usec; + } +#endif + +typedef struct { double duration; long long start_time; } cco_timer; + +static inline cco_timer cco_make_timer(double sec) { + cco_timer tm = {.duration=sec, .start_time=cco_timer_ticks()}; + return tm; +} + +static inline void cco_start_timer(cco_timer* tm, double sec) { + tm->duration = sec; + tm->start_time = cco_timer_ticks(); +} + +static inline void cco_restart_timer(cco_timer* tm) { + tm->start_time = cco_timer_ticks(); +} + +static inline double cco_timer_elapsed(cco_timer* tm) { + return (double)(cco_timer_ticks() - tm->start_time)*(1.0/cco_timer_freq()); +} + +static inline bool cco_timer_expired(cco_timer* tm) { + return cco_timer_elapsed(tm) >= tm->duration; +} + +static inline double cco_timer_remaining(cco_timer* tm) { + return tm->duration - cco_timer_elapsed(tm); +} + +#define cco_await_timer(tm, sec) \ + do { \ + cco_start_timer(tm, sec); \ + cco_await(cco_timer_expired(tm)); \ + } while (0) + +#endif // STC_COROUTINE_H_INCLUDED + +/* -------------------------- IMPLEMENTATION ------------------------- */ +#if (defined i_implement || defined STC_IMPLEMENT) && !defined STC_COROUTINE_IMPLEMENT +#define STC_COROUTINE_IMPLEMENT +#include + +cco_fiber* _cco_spawn(cco_task* _task, cco_group* wg, void* env, cco_fiber* fb) { + cco_fiber* new_fb; + new_fb = fb->next = (fb->next ? c_new(cco_fiber, {.next=fb->next}) : fb); + new_fb->task = _task; + new_fb->env = (env ? env : fb->env); + _task->base.state.fb = new_fb; + if (wg) wg->spawn_count += 1; + _task->base.state.wg = wg; + return new_fb; +} + +cco_fiber* _cco_new_fiber(cco_task* _task, void* env, cco_group* wg) { + cco_fiber* new_fb = c_new(cco_fiber, {.task=_task, .env=env}); + _task->base.state.fb = new_fb; + _task->base.state.wg = wg; + return (new_fb->next = new_fb); +} + +void _cco_cancel_group(cco_fiber* fb, cco_group* waitgroup) { + for (cco_fiber *fbi = fb->next; fbi != fb; fbi = fbi->next) { + cco_task* top = fbi->task; + while (top->base.parent_task) + top = top->base.parent_task; + if (top->base.state.wg == waitgroup) + cco_cancel_fiber(fbi); + } +} + +cco_fiber* cco_execute_next(cco_fiber* fb) { + cco_fiber *_next = fb->next, *unlinked; + int ret = cco_execute(_next); + + if (ret == CCO_DONE) { + unlinked = _next; + _next = (_next == fb ? NULL : _next->next); + fb->next = _next; + c_free_n(unlinked, 1); + } + return _next; +} + +int cco_execute(cco_fiber* fb) { + cco_async (fb) { + while (1) { + fb->parent_task = fb->task->base.parent_task; + fb->awaitbits = fb->task->base.awaitbits; + fb->status = cco_resume(fb->task); + if (fb->err.code) { + // Note: if fb->status == CCO_DONE, fb->task may already be destructed. + if (fb->status == CCO_DONE) { + fb->task = fb->parent_task; + if (fb->task == NULL) + break; + fb->recover_state = fb->task->base.state; + } + cco_stop(fb->task); + cco_suspend; + continue; + } + if (!((fb->status & ~fb->awaitbits) || (fb->task = fb->parent_task) != NULL)) + break; + cco_suspend; + } + } + + if ((uint32_t)fb->err.code & ~CCO_CANCEL) { // Allow CCO_CANCEL not to trigger error. + fprintf(stderr, __FILE__ ": error: unhandled coroutine error '%d'\n" + "%s:%d: cco_throw(%d);\n", + fb->err.code, fb->err.file, fb->err.line, fb->err.code); + exit(fb->err.code); + } + return CCO_DONE; +} +#endif // IMPLEMENT +#undef i_implement +#undef i_static +#undef i_header diff --git a/src/finchlite/codegen/stc/stc/cregex.h b/src/finchlite/codegen/stc/stc/cregex.h new file mode 100644 index 00000000..0a1ab03f --- /dev/null +++ b/src/finchlite/codegen/stc/stc/cregex.h @@ -0,0 +1,168 @@ +/* +This is a Unix port of the Plan 9 regular expression library, by Rob Pike. + +Copyright © 2021 Plan 9 Foundation +Copyright © 2022 Tyge Løvset, for additions made in 2022. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +#ifndef STC_CREGEX_H_INCLUDED +#define STC_CREGEX_H_INCLUDED +/* + * cregex.h + * + * This is a extended version of regexp9, supporting UTF8 input, common + * shorthand character classes, ++. + */ +#include "common.h" +#include "types.h" // csview, cstr types + +enum { + CREG_DEFAULT = 0, + + /* compile-flags */ + CREG_DOTALL = 1<<0, /* dot matches newline too */ + CREG_ICASE = 1<<1, /* ignore case */ + + /* match-flags */ + CREG_FULLMATCH = 1<<2, /* like start-, end-of-line anchors were in pattern: "^ ... $" */ + CREG_NEXT = 1<<3, /* use end of previous match[0] as start of input */ + + /* replace-flags */ + CREG_STRIP = 1<<5, /* only keep the matched strings, strip rest */ + + /* limits */ + CREG_MAX_CLASSES = 16, + CREG_MAX_CAPTURES = 32, +}; + +typedef enum { + CREG_OK = 0, + CREG_NOMATCH = -1, + CREG_MATCHERROR = -2, + CREG_OUTOFMEMORY = -3, + CREG_UNMATCHEDLEFTPARENTHESIS = -4, + CREG_UNMATCHEDRIGHTPARENTHESIS = -5, + CREG_TOOMANYSUBEXPRESSIONS = -6, + CREG_TOOMANYCHARACTERCLASSES = -7, + CREG_MALFORMEDCHARACTERCLASS = -8, + CREG_MISSINGOPERAND = -9, + CREG_UNKNOWNOPERATOR = -10, + CREG_OPERANDSTACKOVERFLOW = -11, + CREG_OPERATORSTACKOVERFLOW = -12, + CREG_OPERATORSTACKUNDERFLOW = -13, +} cregex_result; + +typedef struct { + struct _Reprog* prog; + int error; +} cregex; + +typedef struct { + const cregex* regex; + csview input; + csview match[CREG_MAX_CAPTURES]; +} cregex_iter; + +#define c_match(it, re, str) \ + cregex_iter it = {.regex=re, .input={str}, .match={{0}}}; \ + cregex_match(it.regex, it.input.buf, it.match, CREG_NEXT) == CREG_OK && it.match[0].size; + +#define c_match_sv(it, re, strview) \ + cregex_iter it = {.regex=re, .input=strview, .match={{0}}}; \ + cregex_match_sv(it.regex, it.input, it.match, CREG_NEXT) == CREG_OK && it.match[0].size; + +/* compile a regex from a pattern. return CREG_OK, or negative error code on failure. */ +extern int cregex_compile_pro(cregex *re, const char* pattern, int cflags); + +#define cregex_compile(...) \ + c_ARG_4(__VA_ARGS__, cregex_compile_pro(__VA_ARGS__), cregex_compile_pro(__VA_ARGS__, CREG_DEFAULT), _too_few_args_) + +/* construct and return a regex from a pattern. return CREG_OK, or negative error code on failure. */ +STC_INLINE cregex cregex_make(const char* pattern, int cflags) { + cregex re = {0}; + cregex_compile_pro(&re, pattern, cflags); + return re; +} +STC_INLINE cregex cregex_from(const char* pattern) + { return cregex_make(pattern, CREG_DEFAULT); } + +/* destroy regex */ +extern void cregex_drop(cregex* re); + +/* number of capture groups in a regex pattern, excluding the full match capture (0) */ +extern int cregex_captures(const cregex* re); + +/* ----- Private ----- */ + +struct cregex_match_opt { csview* match; int flags; int _dummy; }; +struct cregex_replace_opt { int count; bool(*xform)(int group, csview match, cstr* out); int flags; int _dummy; }; + +extern int cregex_match_opt(const cregex* re, const char* input, const char* input_end, struct cregex_match_opt opt); +extern int cregex_match_aio_opt(const char* pattern, const char* input, const char* input_end, struct cregex_match_opt opt); +extern cstr cregex_replace_opt(const cregex* re, const char* input, const char* input_end, const char* replace, struct cregex_replace_opt opt); +extern cstr cregex_replace_aio_opt(const char* pattern, const char* input, const char* input_end, const char* replace, struct cregex_replace_opt opt); + +static inline int cregex_match_sv_opt(const cregex* re, csview sv, struct cregex_match_opt opt) + { return cregex_match_opt(re, sv.buf, sv.buf+sv.size, opt); } +static inline int cregex_match_aio_sv_opt(const char* pattern, csview sv, struct cregex_match_opt opt) + { return cregex_match_aio_opt(pattern, sv.buf, sv.buf+sv.size, opt); } +static inline cstr cregex_replace_sv_opt(const cregex* re, csview sv, const char* replace, struct cregex_replace_opt opt) + { return cregex_replace_opt(re, sv.buf, sv.buf+sv.size, replace, opt); } +static inline cstr cregex_replace_aio_sv_opt(const char* pattern, csview sv, const char* replace, struct cregex_replace_opt opt) + { return cregex_replace_aio_opt(pattern, sv.buf, sv.buf+sv.size, replace, opt); } + +/* match: return CREG_OK, CREG_NOMATCH or CREG_MATCHERROR. */ +#define _cregex_match(re, str, ...) cregex_match_opt(re, str, NULL, (struct cregex_match_opt){__VA_ARGS__}) +#define _cregex_match_sv(re, sv, ...) cregex_match_sv_opt(re, sv, (struct cregex_match_opt){__VA_ARGS__}) +/* all-in-one: compile RE pattern + match + free */ +#define _cregex_match_aio(pattern, str, ...) cregex_match_aio_opt(pattern, str, NULL, (struct cregex_match_opt){__VA_ARGS__}) +#define _cregex_match_aio_sv(pattern, sv, ...) cregex_match_aio_sv_opt(pattern, sv, (struct cregex_match_opt){__VA_ARGS__}) + +/* replace input with a string using regular expression */ +#define _cregex_replace(re, str, replace, ...) cregex_replace_opt(re, str, NULL, replace, (struct cregex_replace_opt){__VA_ARGS__}) +#define _cregex_replace_sv(re, sv, replace, ...) cregex_replace_sv_opt(re, sv, replace, (struct cregex_replace_opt){__VA_ARGS__}) +/* all-in-one: compile RE string pattern + match + replace + free */ +#define _cregex_replace_aio(pattern, str, replace, ...) cregex_replace_aio_opt(pattern, str, NULL, replace, (struct cregex_replace_opt){__VA_ARGS__}) +#define _cregex_replace_aio_sv(pattern, sv, replace, ...) cregex_replace_aio_sv_opt(pattern, sv, replace, (struct cregex_replace_opt){__VA_ARGS__}) + +/* ----- API functions ---- */ + +#define cregex_match(...) _cregex_match(__VA_ARGS__, ._dummy=0) +#define cregex_match_sv(...) _cregex_match_sv(__VA_ARGS__, ._dummy=0) +#define cregex_match_aio(...) _cregex_match_aio(__VA_ARGS__, ._dummy=0) +#define cregex_match_aio_sv(...) _cregex_match_aio_sv(__VA_ARGS__, ._dummy=0) +#define cregex_is_match(re, str) (_cregex_match(re, str, 0) == CREG_OK) + +#define cregex_replace(...) _cregex_replace(__VA_ARGS__, ._dummy=0) +#define cregex_replace_sv(...) _cregex_replace_sv(__VA_ARGS__, ._dummy=0) +#define cregex_replace_aio(...) _cregex_replace_aio(__VA_ARGS__, ._dummy=0) +#define cregex_replace_aio_sv(...) _cregex_replace_aio_sv(__VA_ARGS__, ._dummy=0) + +#endif // STC_CREGEX_H_INCLUDED + +#if defined STC_IMPLEMENT || defined i_implement || defined i_import + #include "priv/linkage.h" + #include "priv/cregex_prv.c" + #if defined i_import + #include "priv/utf8_prv.c" + #include "priv/cstr_prv.c" + #endif + #include "priv/linkage2.h" +#endif diff --git a/src/finchlite/codegen/stc/stc/cspan.h b/src/finchlite/codegen/stc/stc/cspan.h new file mode 100644 index 00000000..6f5f16c0 --- /dev/null +++ b/src/finchlite/codegen/stc/stc/cspan.h @@ -0,0 +1,480 @@ +/* + MIT License + * + * Copyright (c) 2025 Tyge Løvset + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +/* +#include +#include +#include +use_cspan(Span2f, float, 2); +use_cspan(Intspan, int); + +int demo1() { + float raw[4*5]; + Span2f ms = cspan_md(raw, 4, 5); + + for (int i=0; ishape[0]==0 ? NULL : self->data, ._s=self}; \ + } \ + STC_INLINE Self##_iter Self##_end(const Self* self) { \ + (void)self; \ + return c_literal(Self##_iter){0}; \ + } \ + STC_INLINE void Self##_next(Self##_iter* it) { \ + isize off = it->_s->stride.d[RANK - 1]; \ + bool done = _cspan_next##RANK(it->pos, it->_s->shape, it->_s->stride.d, RANK, &off); \ + if (done) it->ref = NULL; else it->ref += off; \ + } \ + STC_INLINE isize Self##_size(const Self* self) \ + { return cspan_size(self); } \ + STC_INLINE Self Self##_transposed(Self sp) \ + { _cspan_transpose(sp.shape, sp.stride.d, cspan_rank(&sp)); return sp; } \ + STC_INLINE Self Self##_swapped_axes(Self sp, int ax1, int ax2) \ + { _cspan_swap_axes(sp.shape, sp.stride.d, cspan_rank(&sp), ax1, ax2); return sp; } \ + struct stc_nostruct + +#define use_cspan_with_eq_4(Self, T, i_eq, RANK) \ + use_cspan_3(Self, T, RANK); \ + STC_INLINE bool Self##_eq(const Self* x, const Self* y) { \ + if (memcmp(x->shape, y->shape, sizeof x->shape) != 0) \ + return false; \ + for (Self##_iter _i = Self##_begin(x), _j = Self##_begin(y); \ + _i.ref != NULL; Self##_next(&_i), Self##_next(&_j)) \ + { if (!(i_eq(_i.ref, _j.ref))) return false; } \ + return true; \ + } \ + STC_INLINE bool Self##_equals(Self sp1, Self sp2) \ + { return Self##_eq(&sp1, &sp2); } \ + struct stc_nostruct + +#define use_cspan2(Self, T) use_cspan_2(Self, T); use_cspan_3(Self##2, T, 2) +#define use_cspan3(Self, T) use_cspan2(Self, T); use_cspan_3(Self##3, T, 3) +#define use_cspan2_with_eq(Self, T, eq) use_cspan_with_eq_3(Self, T, eq); \ + use_cspan_with_eq_4(Self##2, T, eq, 2) +#define use_cspan3_with_eq(Self, T, eq) use_cspan2_with_eq(Self, T, eq); \ + use_cspan_with_eq_4(Self##3, T, eq, 3) +#define use_cspan_tuple(N) typedef struct { _istride d[N]; } cspan_tuple##N +use_cspan_tuple(1); use_cspan_tuple(2); +use_cspan_tuple(3); use_cspan_tuple(4); +use_cspan_tuple(5); use_cspan_tuple(6); +use_cspan_tuple(7); use_cspan_tuple(8); + + +// Construct a cspan from a pointer+size +#define cspan_from_n(dataptr, n) \ + {.data=dataptr, \ + .shape={(_istride)(n)}, \ + .stride=c_literal(cspan_tuple1){.d={1}}} + +// Create a 1d-span in the local lexical scope. N must be a compile-time constant. +#define cspan_by_copy(dataptr, N) \ + cspan_from_n(memcpy((char[(N)*sizeof *(dataptr)]){0}, dataptr, (N)*sizeof *(dataptr)), N) + +// Create a zeroed out 1d-span in the local lexical scope. N must be a compile-time constant. +#define cspan_zeros(Span, N) \ + ((Span)cspan_from_n((Span##_value[N]){0}, N)) + +// Create a global scope 1d-span from constant initializer list, otherwise like c_make(Span, ...). +#define cspan_make(Span, ...) \ + ((Span)cspan_from_n(c_make_array(Span##_value, __VA_ARGS__), \ + sizeof((Span##_value[])__VA_ARGS__)/sizeof(Span##_value))) + +// Make 1d-span from a c-array. +#define cspan_from_array(array) \ + cspan_from_n(array, c_arraylen(array)) + +// Make 1d-span from a vec or stack container. +#define cspan_from_vec(container) \ + cspan_from_n((container)->data, (container)->size) + +// Make a 1d-sub-span from a 1d-span +#define cspan_subspan(self, offset, count) \ + {.data=cspan_at(self, offset), \ + .shape={(_istride)(count)}, \ + .stride=(self)->stride} + +// Accessors +// +#define cspan_size(self) _cspan_size((self)->shape, cspan_rank(self)) +#define cspan_rank(self) c_arraylen((self)->shape) // constexpr +#define cspan_at(self, ...) ((self)->data + cspan_index(self, __VA_ARGS__)) +#define cspan_front(self) ((self)->data) +#define cspan_back(self) ((self)->data + cspan_size(self) - 1) + +#define cspan_index(...) cspan_index_fn(__VA_ARGS__, c_COMMA_N(cspan_index_3d), c_COMMA_N(cspan_index_2d), \ + c_COMMA_N(cspan_index_1d),)(__VA_ARGS__) +#define cspan_index_fn(self, i,j,k,n, ...) c_TUPLE_AT_1(n, cspan_index_nd,) +#define cspan_index_1d(self, i) (c_static_assert(cspan_rank(self) == 1), \ + c_assert((i) < (self)->shape[0]), \ + (i)*(self)->stride.d[0]) +#define cspan_index_2d(self, i,j) (c_static_assert(cspan_rank(self) == 2), \ + c_assert((i) < (self)->shape[0] && (j) < (self)->shape[1]), \ + (i)*(self)->stride.d[0] + (j)*(self)->stride.d[1]) +#define cspan_index_3d(self, i,j,k) (c_static_assert(cspan_rank(self) == 3), \ + c_assert((i) < (self)->shape[0] && (j) < (self)->shape[1] && (k) < (self)->shape[2]), \ + (i)*(self)->stride.d[0] + (j)*(self)->stride.d[1] + (k)*(self)->stride.d[2]) +#define cspan_index_nd(self, ...) _cspan_index((self)->shape, (self)->stride.d, c_make_array(isize, {__VA_ARGS__}), \ + (c_static_assert(cspan_rank(self) == c_NUMARGS(__VA_ARGS__)), cspan_rank(self))) + + +// Multi-dimensional span constructors +// +typedef enum {c_ROWMAJOR, c_COLMAJOR, c_STRIDED} cspan_layout; + +#define cspan_is_colmajor(self) \ + _cspan_is_layout(c_COLMAJOR, (self)->shape, (self)->stride.d, cspan_rank(self)) +#define cspan_is_rowmajor(self) \ + _cspan_is_layout(c_ROWMAJOR, (self)->shape, (self)->stride.d, cspan_rank(self)) +#define cspan_get_layout(self) \ + (cspan_is_rowmajor(self) ? c_ROWMAJOR : cspan_is_colmajor(self) ? c_COLMAJOR : c_STRIDED) + +#define cspan_md(dataptr, ...) \ + cspan_md_layout(c_ROWMAJOR, dataptr, __VA_ARGS__) + +// Span2 sp1 = cspan_md(data, 30, 50); +// Span2 sp2 = {data, cspan_shape(15, 25), cspan_strides(50*2, 2)}; // every second in each dim +#define cspan_shape(...) {__VA_ARGS__} +#define cspan_strides(...) {.d={__VA_ARGS__}} + +#define cspan_md_layout(layout, dataptr, ...) \ + {.data=dataptr, \ + .shape={__VA_ARGS__}, \ + .stride=*(c_JOIN(cspan_tuple,c_NUMARGS(__VA_ARGS__))*) \ + _cspan_shape2stride(layout, c_make_array(_istride, {__VA_ARGS__}), c_NUMARGS(__VA_ARGS__))} + +// Transpose matrix +#define cspan_transpose(self) \ + _cspan_transpose((self)->shape, (self)->stride.d, cspan_rank(self)) + +// Swap two matrix axes +#define cspan_swap_axes(self, ax1, ax2) \ + _cspan_swap_axes((self)->shape, (self)->stride.d, cspan_rank(self), ax1, ax2) + +// Set all span elements to value. +#define cspan_set_all(Span, self, value) do { \ + Span##_value _v = value; \ + for (c_each_3(_it, Span, *(self))) *_it.ref = _v; \ +} while (0) + +// General slicing function. +// +#define c_END (_istride)(((size_t)1 << (sizeof(_istride)*8 - 1)) - 1) +#define c_ALL 0,c_END + +#define cspan_slice(self, Outspan, ...) \ + Outspan##_slice_((self)->data, (self)->shape, (self)->stride.d, \ + c_make_array2d(const isize, 3, {__VA_ARGS__}), \ + (c_static_assert(cspan_rank(self) == sizeof((isize[][3]){__VA_ARGS__})/sizeof(isize[3])), cspan_rank(self))) + +// submd#(): Reduces rank, fully typesafe + range checked by default +// int ms3[N1][N2][N3]; +// int (*ms2)[N3] = ms3[1]; // traditional, lose range test/info. VLA. +// Span3 ms3 = cspan_md(data, N1,N2,N3); // Uses cspan_md instead. +// *cspan_at(&ms3, 1,1,1) = 42; +// Span2 ms2 = cspan_slice(&ms3, Span2, {1}, {c_ALL}, {c_ALL}); +// Span2 ms2 = cspan_submd3(&ms3, 1); // Same as line above, optimized. +#define cspan_submd2(self, x) \ + {.data=cspan_at(self, x, 0), \ + .shape={(self)->shape[1]}, \ + .stride=c_literal(cspan_tuple1){.d={(self)->stride.d[1]}}} + +#define cspan_submd3(...) c_MACRO_OVERLOAD(cspan_submd3, __VA_ARGS__) +#define cspan_submd3_2(self, x) \ + {.data=cspan_at(self, x, 0, 0), \ + .shape={(self)->shape[1], (self)->shape[2]}, \ + .stride=c_literal(cspan_tuple2){.d={(self)->stride.d[1], (self)->stride.d[2]}}} +#define cspan_submd3_3(self, x, y) \ + {.data=cspan_at(self, x, y, 0), \ + .shape={(self)->shape[2]}, \ + .stride=c_literal(cspan_tuple1){.d={(self)->stride.d[2]}}} + +#define cspan_submd4(...) c_MACRO_OVERLOAD(cspan_submd4, __VA_ARGS__) +#define cspan_submd4_2(self, x) \ + {.data=cspan_at(self, x, 0, 0, 0), \ + .shape={(self)->shape[1], (self)->shape[2], (self)->shape[3]}, \ + .stride=c_literal(cspan_tuple3){.d={(self)->stride.d[1], (self)->stride.d[2], (self)->stride.d[3]}}} +#define cspan_submd4_3(self, x, y) \ + {.data=cspan_at(self, x, y, 0, 0), \ + .shape={(self)->shape[2], (self)->shape[3]}, \ + .stride=c_literal(cspan_tuple2){.d={(self)->stride.d[2], (self)->stride.d[3]}}} +#define cspan_submd4_4(self, x, y, z) \ + {.data=cspan_at(self, x, y, z, 0), \ + .shape={(self)->shape[3]}, \ + .stride=c_literal(cspan_tuple1){.d={(self)->stride.d[3]}}} + +#define cspan_print(...) c_MACRO_OVERLOAD(cspan_print, __VA_ARGS__) +#define cspan_print_3(Span, fmt, span) \ + cspan_print_4(Span, fmt, span, stdout) +#define cspan_print_4(Span, fmt, span, fp) \ + cspan_print_5(Span, fmt, span, fp, "[]") +#define cspan_print_5(Span, fmt, span, fp, brackets) \ + cspan_print_6(Span, fmt, span, fp, brackets, c_EXPAND) +#define cspan_print_complex(Span, prec, span, fp) \ + cspan_print_6(Span, "%." #prec "f%+." #prec "fi", span, fp, "[]", cspan_CMPLX_FLD) +#define cspan_CMPLX_FLD(x) creal(x), cimag(x) + +#define cspan_print_6(Span, fmt, span, fp, brackets, field) do { \ + const Span _s = span; \ + const char *_f = fmt, *_b = brackets; \ + FILE* _fp = fp; \ + int _w, _max = 0; \ + char _res[2][20], _fld[64]; \ + for (c_each_3(_it, Span, _s)) { \ + _w = snprintf(NULL, 0ULL, _f, field(_it.ref[0])); \ + if (_w > _max) _max = _w; \ + } \ + for (c_each_3(_it, Span, _s)) { \ + _cspan_print_assist(_it.pos, _s.shape, cspan_rank(&_s), _b, _res); \ + _w = _max + (_it.pos[cspan_rank(&_s) - 1] > 0); \ + snprintf(_fld, sizeof _fld, _f, field(_it.ref[0])); \ + fprintf(_fp, "%s%*s%s", _res[0], _w, _fld, _res[1]); \ + } \ +} while (0) + +/* ----- PRIVATE ----- */ + +STC_INLINE isize _cspan_size(const _istride shape[], int rank) { + isize size = shape[0]; + while (--rank) size *= shape[rank]; + return size; +} + +STC_INLINE void _cspan_swap_axes(_istride shape[], _istride stride[], + int rank, int ax1, int ax2) { + (void)rank; + c_assert(c_uless(ax1, rank) & c_uless(ax2, rank)); + c_swap(shape + ax1, shape + ax2); + c_swap(stride + ax1, stride + ax2); +} + +STC_INLINE void _cspan_transpose(_istride shape[], _istride stride[], int rank) { + for (int i = 0; i < --rank; ++i) { + c_swap(shape + i, shape + rank); + c_swap(stride + i, stride + rank); + } +} + +STC_INLINE isize _cspan_index(const _istride shape[], const _istride stride[], + const isize args[], int rank) { + isize off = 0; + (void)shape; + while (rank-- != 0) { + c_assert(args[rank] < shape[rank]); + off += args[rank]*stride[rank]; + } + return off; +} + +STC_API void _cspan_print_assist(_istride pos[], const _istride shape[], const int rank, + const char* brackets, char result[2][20]); + +STC_API bool _cspan_nextN(_istride pos[], const _istride shape[], const _istride stride[], + int rank, isize* off); +#define _cspan_next1(pos, shape, stride, rank, off) (++pos[0] == shape[0]) +#define _cspan_next2(pos, shape, stride, rank, off) (++pos[1] == shape[1] && \ + (pos[1] = 0, *off += stride[0] - (isize)shape[1]*stride[1], ++pos[0] == shape[0])) +#define _cspan_next3(pos, shape, stride, rank, off) (++pos[2] == shape[2] && \ + (pos[2] = 0, *off += stride[1] - (isize)shape[2]*stride[2], ++pos[1] == shape[1]) && \ + (pos[1] = 0, *off += stride[0] - (isize)shape[1]*stride[1], ++pos[0] == shape[0])) +#define _cspan_next4 _cspan_nextN +#define _cspan_next5 _cspan_nextN +#define _cspan_next6 _cspan_nextN +#define _cspan_next7 _cspan_nextN +#define _cspan_next8 _cspan_nextN + +STC_API isize _cspan_slice(_istride oshape[], _istride ostride[], int* orank, + const _istride shape[], const _istride stride[], + const isize args[][3], int rank); +STC_API _istride* _cspan_shape2stride(cspan_layout layout, _istride shape[], int rank); +STC_API bool _cspan_is_layout(cspan_layout layout, const _istride shape[], const _istride strides[], int rank); + +#endif // STC_CSPAN_H_INCLUDED + +/* --------------------- IMPLEMENTATION --------------------- */ +#if defined i_implement && !defined STC_CSPAN_IMPLEMENT +#define STC_CSPAN_IMPLEMENT + +STC_DEF bool _cspan_is_layout(cspan_layout layout, const _istride shape[], const _istride strides[], int rank) { + _istride tmpshape[16]; // 16 = "max" rank + size_t sz = (size_t)rank*sizeof(_istride); + memcpy(tmpshape, shape, sz); + return memcmp(strides, _cspan_shape2stride(layout, tmpshape, rank), sz) == 0; +} + +STC_DEF void _cspan_print_assist(_istride pos[], const _istride shape[], const int rank, + const char* brackets, char result[2][20]) { + int n = 0, j = 0, r = rank - 1; + memset(result, 0, 32); + + // left braces: + while (n <= r && pos[r - n] == 0) + ++n; + if (n) for (; j < rank; ++j) + result[0][j] = j < rank - n ? ' ' : brackets[0]; + + // right braces: + for (j = 0; r >= 0 && pos[r] + 1 == shape[r]; --r, ++j) + result[1][j] = brackets[1]; + + // comma and newlines: + n = (j > 0) + ((j > 1) & (j < rank)); + if (brackets[2] && j < rank) + result[1][j++] = brackets[2]; // comma + while (n--) + result[1][j++] = '\n'; +} + +STC_DEF bool _cspan_nextN(_istride pos[], const _istride shape[], const _istride stride[], + int rank, isize* off) { + ++pos[--rank]; + for (; rank && pos[rank] == shape[rank]; --rank) { + pos[rank] = 0; ++pos[rank - 1]; + *off += stride[rank - 1] - (isize)shape[rank]*stride[rank]; + } + return pos[rank] == shape[rank]; +} + +STC_DEF _istride* _cspan_shape2stride(cspan_layout layout, _istride shpstri[], int rank) { + int i, inc; + if (layout == c_COLMAJOR) i = 0, inc = 1; + else i = rank - 1, inc = -1; + _istride k = 1, s1 = shpstri[i], s2; + + shpstri[i] = 1; + while (--rank) { + i += inc; + s2 = shpstri[i]; + shpstri[i] = (k *= s1); + s1 = s2; + } + return shpstri; +} + +STC_DEF isize _cspan_slice(_istride oshape[], _istride ostride[], int* orank, + const _istride shape[], const _istride stride[], + const isize args[][3], int rank) { + isize end, off = 0; + int i = 0, oi = 0; + + for (; i < rank; ++i) { + off += args[i][0]*stride[i]; + switch (args[i][1]) { + case 0: c_assert(c_uless(args[i][0], shape[i])); continue; + case c_END: end = shape[i]; break; + default: end = args[i][1]; + } + oshape[oi] = (_istride)(end - args[i][0]); + ostride[oi] = stride[i]; + c_assert((oshape[oi] > 0) & !c_uless(shape[i], end)); + if (args[i][2] > 0) { + ostride[oi] *= (_istride)args[i][2]; + oshape[oi] = (oshape[oi] - 1)/(_istride)args[i][2] + 1; + } + ++oi; + } + *orank = oi; + return off; +} +#endif // IMPLEMENT +#include "priv/linkage2.h" diff --git a/src/finchlite/codegen/stc/stc/cstr.h b/src/finchlite/codegen/stc/stc/cstr.h new file mode 100644 index 00000000..d7c3556d --- /dev/null +++ b/src/finchlite/codegen/stc/stc/cstr.h @@ -0,0 +1,51 @@ +/* MIT License + * + * Copyright (c) 2025 Tyge Løvset + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* A string type with short string optimization in C99. + * Stores up to a 22 bytes long string inside a 24 bytes string representation (x64). + */ +#define i_header // external linkage by default. override with i_static. +#include "priv/linkage.h" + +#ifndef STC_CSTR_H_INCLUDED +#define STC_CSTR_H_INCLUDED + +#include "common.h" +#include "types.h" +#include "priv/utf8_prv.h" +#include "priv/cstr_prv.h" + +#endif // STC_CSTR_H_INCLUDED + +#if defined i_implement || \ + defined STC_CSTR_CORE || \ + defined STC_CSTR_IO || \ + defined STC_CSTR_UTF8 + #include "priv/cstr_prv.c" +#endif // i_implement + +#if defined i_import || defined STC_CSTR_UTF8 + #include "priv/utf8_prv.c" +#endif + +#include "priv/linkage2.h" diff --git a/src/finchlite/codegen/stc/stc/csview.h b/src/finchlite/codegen/stc/stc/csview.h new file mode 100644 index 00000000..c9e71df7 --- /dev/null +++ b/src/finchlite/codegen/stc/stc/csview.h @@ -0,0 +1,243 @@ +/* MIT License + * + * Copyright (c) 2025 Tyge Løvset + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +// csview is a non-zero-terminated string view. + +#ifndef STC_CSVIEW_H_INCLUDED +#define STC_CSVIEW_H_INCLUDED + +#include "common.h" +#include "types.h" +#include "priv/utf8_prv.h" + +#define csview_init() c_sv_1("") +#define csview_drop(p) c_default_drop(p) +#define csview_clone(sv) c_default_clone(sv) + +csview_iter csview_advance(csview_iter it, isize u8pos); +csview csview_subview_pro(csview sv, isize pos, isize n); +csview csview_token(csview sv, const char* sep, isize* pos); +csview csview_u8_subview(csview sv, isize u8pos, isize u8len); +csview csview_u8_tail(csview sv, isize u8len); +csview_iter csview_u8_at(csview sv, isize u8pos); + +STC_INLINE csview csview_from(const char* str) + { return c_literal(csview){str, c_strlen(str)}; } +STC_INLINE csview csview_from_n(const char* str, isize n) + { return c_literal(csview){str, n}; } + +STC_INLINE void csview_clear(csview* self) { *self = csview_init(); } +STC_INLINE isize csview_size(csview sv) { return sv.size; } +STC_INLINE bool csview_is_empty(csview sv) { return sv.size == 0; } + +STC_INLINE bool csview_equals_sv(csview sv1, csview sv2) + { return sv1.size == sv2.size && !c_memcmp(sv1.buf, sv2.buf, sv1.size); } + +STC_INLINE bool csview_equals(csview sv, const char* str) + { return csview_equals_sv(sv, c_sv_2(str, c_strlen(str))); } + +STC_INLINE size_t csview_hash(const csview *self) + { return c_basehash_n(self->buf, self->size); } + +STC_INLINE isize csview_find_sv(csview sv, csview search) { + char* res = c_strnstrn(sv.buf, sv.size, search.buf, search.size); + return res ? (res - sv.buf) : c_NPOS; +} + +STC_INLINE isize csview_find(csview sv, const char* str) + { return csview_find_sv(sv, c_sv_2(str, c_strlen(str))); } + +STC_INLINE bool csview_contains(csview sv, const char* str) + { return csview_find(sv, str) != c_NPOS; } + +STC_INLINE bool csview_starts_with(csview sv, const char* str) { + isize n = c_strlen(str); + return n <= sv.size && !c_memcmp(sv.buf, str, n); +} + +STC_INLINE bool csview_ends_with(csview sv, const char* str) { + isize n = c_strlen(str); + return n <= sv.size && !c_memcmp(sv.buf + sv.size - n, str, n); +} + +STC_INLINE csview csview_subview(csview sv, isize pos, isize len) { + c_assert(((size_t)pos <= (size_t)sv.size) & (len >= 0)); + if (pos + len > sv.size) len = sv.size - pos; + sv.buf += pos, sv.size = len; + return sv; +} + +STC_INLINE csview csview_slice(csview sv, isize p1, isize p2) { + c_assert(((size_t)p1 <= (size_t)p2) & ((size_t)p1 <= (size_t)sv.size)); + if (p2 > sv.size) p2 = sv.size; + sv.buf += p1, sv.size = p2 - p1; + return sv; +} + +STC_INLINE csview csview_trim_start(csview sv) + { while (sv.size && *sv.buf <= ' ') ++sv.buf, --sv.size; return sv; } + +STC_INLINE csview csview_trim_end(csview sv) + { while (sv.size && sv.buf[sv.size - 1] <= ' ') --sv.size; return sv; } + +STC_INLINE csview csview_trim(csview sv) + { return csview_trim_end(csview_trim_start(sv)); } + +STC_INLINE csview csview_tail(csview sv, isize len) + { return csview_subview(sv, sv.size - len, len); } + +/* utf8 iterator */ +STC_INLINE csview_iter csview_begin(const csview* self) { + csview_iter it = {.u8 = {{self->buf, utf8_chr_size(self->buf)}, + self->buf + self->size}}; + return it; +} +STC_INLINE csview_iter csview_end(const csview* self) { + (void)self; csview_iter it = {0}; return it; +} +STC_INLINE void csview_next(csview_iter* it) { + it->ref += it->chr.size; + it->chr.size = utf8_chr_size(it->ref); + if (it->ref == it->u8.end) it->ref = NULL; +} + +/* utf8 */ +STC_INLINE csview csview_u8_from(const char* str, isize u8pos, isize u8len) + { return utf8_subview(str, u8pos, u8len); } + +STC_INLINE isize csview_u8_size(csview sv) + { return utf8_count_n(sv.buf, sv.size); } + +STC_INLINE bool csview_u8_valid(csview sv) // requires linking with utf8 symbols + { return utf8_valid_n(sv.buf, sv.size); } + +#define c_fortoken(...) for (c_token(__VA_ARGS__)) // [deprecated] + +#define c_token_sv(it, separator, sv) \ + struct { csview input, token; const char* sep; isize pos; } \ + it = {.input=sv, .sep=separator} ; \ + it.pos <= it.input.size && (it.token = csview_token(it.input, it.sep, &it.pos)).buf ; + +#define c_token(it, separator, str) \ + c_token_sv(it, separator, csview_from(str)) + +/* ---- Container helper functions ---- */ + +STC_INLINE int csview_cmp(const csview* x, const csview* y) { + isize n = x->size < y->size ? x->size : y->size; + int c = c_memcmp(x->buf, y->buf, n); + return c ? c : c_default_cmp(&x->size, &y->size); +} + +STC_INLINE bool csview_eq(const csview* x, const csview* y) + { return x->size == y->size && !c_memcmp(x->buf, y->buf, x->size); } + +/* ---- case insensitive ---- */ + +STC_INLINE bool csview_iequals_sv(csview sv1, csview sv2) + { return sv1.size == sv2.size && !utf8_icompare(sv1, sv2); } + +STC_INLINE bool csview_iequals(csview sv, const char* str) + { return csview_iequals_sv(sv, c_sv(str, c_strlen(str))); } + +STC_INLINE bool csview_ieq(const csview* x, const csview* y) + { return csview_iequals_sv(*x, *y); } + +STC_INLINE int csview_icmp(const csview* x, const csview* y) + { return utf8_icompare(*x, *y); } + +STC_INLINE bool csview_istarts_with(csview sv, const char* str) { + isize n = c_strlen(str); + return n <= sv.size && !utf8_icompare(sv, c_sv(str, n)); +} + +STC_INLINE bool csview_iends_with(csview sv, const char* str) { + isize n = c_strlen(str); + return n <= sv.size && !utf8_icmp(sv.buf + sv.size - n, str); +} +#endif // STC_CSVIEW_H_INCLUDED + +/* -------------------------- IMPLEMENTATION ------------------------- */ +#if (defined STC_IMPLEMENT || defined i_implement) && !defined STC_CSVIEW_IMPLEMENT +#define STC_CSVIEW_IMPLEMENT + +csview_iter csview_advance(csview_iter it, isize u8pos) { + int inc = 1; + if (u8pos < 0) u8pos = -u8pos, inc = -1; + while (u8pos && it.ref != it.u8.end) + u8pos -= (*(it.ref += inc) & 0xC0) != 0x80; + if (it.ref == it.u8.end) it.ref = NULL; + else it.chr.size = utf8_chr_size(it.ref); + return it; +} + +csview csview_subview_pro(csview sv, isize pos, isize len) { + if (pos < 0) { + pos += sv.size; + if (pos < 0) pos = 0; + } + if (pos > sv.size) pos = sv.size; + if (pos + len > sv.size) len = sv.size - pos; + sv.buf += pos, sv.size = len; + return sv; +} + +csview csview_token(csview sv, const char* sep, isize* pos) { + isize sep_size = c_strlen(sep); + csview slice = {sv.buf + *pos, sv.size - *pos}; + const char* res = c_strnstrn(slice.buf, slice.size, sep, sep_size); + csview tok = {slice.buf, res ? (res - slice.buf) : slice.size}; + *pos += tok.size + sep_size; + return tok; +} + +csview csview_u8_subview(csview sv, isize u8pos, isize u8len) { + const char* s, *end = &sv.buf[sv.size]; + while ((u8pos > 0) & (sv.buf != end)) + u8pos -= (*++sv.buf & 0xC0) != 0x80; + s = sv.buf; + while ((u8len > 0) & (s != end)) + u8len -= (*++s & 0xC0) != 0x80; + sv.size = s - sv.buf; return sv; +} + +csview csview_u8_tail(csview sv, isize u8len) { + const char* p = &sv.buf[sv.size]; + while (u8len && p != sv.buf) + u8len -= (*--p & 0xC0) != 0x80; + sv.size -= p - sv.buf, sv.buf = p; + return sv; +} + +csview_iter csview_u8_at(csview sv, isize u8pos) { + const char *end = &sv.buf[sv.size]; + while ((u8pos > 0) & (sv.buf != end)) + u8pos -= (*++sv.buf & 0xC0) != 0x80; + sv.size = utf8_chr_size(sv.buf); + c_assert(sv.buf != end); + return c_literal(csview_iter){.u8 = {sv, end}}; +} +#endif // IMPLEMENT + +#if defined i_import +#include "priv/utf8_prv.c" +#endif diff --git a/src/finchlite/codegen/stc/stc/ctest.h b/src/finchlite/codegen/stc/stc/ctest.h new file mode 100644 index 00000000..8ffbdf7a --- /dev/null +++ b/src/finchlite/codegen/stc/stc/ctest.h @@ -0,0 +1,639 @@ +/* Copyright 2011-2023 Bas van den Berg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CTEST_H +#define CTEST_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __GNUC__ +#define CTEST_IMPL_FORMAT_PRINTF(a, b) __attribute__ ((format(printf, a, b))) +#else +#define CTEST_IMPL_FORMAT_PRINTF(a, b) +#endif + +#include /* intmax_t, uintmax_t, PRI* */ +#include /* bool, true, false */ +#include /* size_t */ + +typedef void (*ctest_nullary_run_func)(void); +typedef void (*ctest_unary_run_func)(void*); +typedef void (*ctest_setup_func)(void*); +typedef void (*ctest_teardown_func)(void*); + +union ctest_run_func_union { + ctest_nullary_run_func nullary; + ctest_unary_run_func unary; +}; + +#define CTEST_IMPL_PRAGMA(x) _Pragma (#x) +#define CTEST_CONTAINER_OF(p, T, m) \ + ((T*)((char*)(p) + 0*sizeof((p) == &((T*)0)->m) - offsetof(T, m))) + +#if defined(__GNUC__) +#if defined(__clang__) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) +/* the GCC argument will work for both gcc and clang */ +#define CTEST_IMPL_DIAG_PUSH_IGNORED(w) \ + CTEST_IMPL_PRAGMA(GCC diagnostic push) \ + CTEST_IMPL_PRAGMA(GCC diagnostic ignored "-W" #w) +#define CTEST_IMPL_DIAG_POP() \ + CTEST_IMPL_PRAGMA(GCC diagnostic pop) +#else +/* the push/pop functionality wasn't in gcc until 4.6, fallback to "ignored" */ +#define CTEST_IMPL_DIAG_PUSH_IGNORED(w) \ + CTEST_IMPL_PRAGMA(GCC diagnostic ignored "-W" #w) +#define CTEST_IMPL_DIAG_POP() +#endif +#else +/* leave them out entirely for non-GNUC compilers */ +#define CTEST_IMPL_DIAG_PUSH_IGNORED(w) +#define CTEST_IMPL_DIAG_POP() +#endif + +struct ctest { + uint32_t magic0, padding; + + const char* ssname; // suite name + const char* ttname; // test name + union ctest_run_func_union run; + + void* data; + ctest_setup_func* setup; + ctest_teardown_func* teardown; + + int32_t skip; + uint32_t magic1; +}; + +#define CTEST_IMPL_NAME(name) ctest_##name +#define CTEST_IMPL_FNAME(sname, tname) CTEST_IMPL_NAME(sname##_##tname##_run) +#define CTEST_IMPL_TNAME(sname, tname) CTEST_IMPL_NAME(sname##_##tname) +#define CTEST_IMPL_DATA_SNAME(sname) CTEST_IMPL_NAME(sname##_data) +#define CTEST_IMPL_DATA_TNAME(sname, tname) CTEST_IMPL_NAME(sname##_##tname##_data) +#define CTEST_IMPL_SETUP_FNAME(sname) CTEST_IMPL_NAME(sname##_setup) +#define CTEST_IMPL_SETUP_FPNAME(sname) CTEST_IMPL_NAME(sname##_setup_ptr) +#define CTEST_IMPL_SETUP_TPNAME(sname, tname) CTEST_IMPL_NAME(sname##_##tname##_setup_ptr) +#define CTEST_IMPL_TEARDOWN_FNAME(sname) CTEST_IMPL_NAME(sname##_teardown) +#define CTEST_IMPL_TEARDOWN_FPNAME(sname) CTEST_IMPL_NAME(sname##_teardown_ptr) +#define CTEST_IMPL_TEARDOWN_TPNAME(sname, tname) CTEST_IMPL_NAME(sname##_##tname##_teardown_ptr) + +#ifdef __APPLE__ +#ifdef __arm64__ +#define CTEST_IMPL_SECTION __attribute__ ((used, section ("__DATA, .ctest"), aligned(8))) +#else +#define CTEST_IMPL_SECTION __attribute__ ((used, section ("__DATA, .ctest"), aligned(1))) +#endif +#elif !defined _MSC_VER +#define CTEST_IMPL_SECTION __attribute__ ((used, section (".ctest"), aligned(1))) +#else +#pragma section(".ctest", read) +#define CTEST_IMPL_SECTION __declspec(allocate(".ctest")) __declspec(align(1)) +#endif + +#define CTEST_IMPL_STRUCT(sname, tname, tskip, tdata, tsetup, tteardown) \ + static CTEST_IMPL_SECTION struct ctest CTEST_IMPL_TNAME(sname, tname) = { \ + 0xBADCAFE0, 0, \ + #sname, \ + #tname, \ + { (ctest_nullary_run_func) CTEST_IMPL_FNAME(sname, tname) }, \ + tdata, \ + (ctest_setup_func*) tsetup, \ + (ctest_teardown_func*) tteardown, \ + tskip, \ + 0xBADCAFE1, \ + } + +#ifdef __cplusplus + +#define CTEST_SETUP(sname, fixt) \ + template <> void CTEST_IMPL_SETUP_FNAME(sname)(struct CTEST_IMPL_DATA_SNAME(sname)* fixt) + +#define CTEST_TEARDOWN(sname, fixt) \ + template <> void CTEST_IMPL_TEARDOWN_FNAME(sname)(struct CTEST_IMPL_DATA_SNAME(sname)* fixt) + +#define CTEST_FIXTURE(sname) \ + template void CTEST_IMPL_SETUP_FNAME(sname)(T* self) { } \ + template void CTEST_IMPL_TEARDOWN_FNAME(sname)(T* self) { } \ + struct CTEST_IMPL_DATA_SNAME(sname) + +#define CTEST_IMPL_CTEST(sname, tname, tskip) \ + static void CTEST_IMPL_FNAME(sname, tname)(void); \ + CTEST_IMPL_STRUCT(sname, tname, tskip, NULL, NULL, NULL); \ + static void CTEST_IMPL_FNAME(sname, tname)(void) + +#define CTEST_IMPL_CTEST_F(sname, tname, tskip, fixt) \ + static struct CTEST_IMPL_DATA_SNAME(sname) CTEST_IMPL_DATA_TNAME(sname, tname); \ + static void CTEST_IMPL_FNAME(sname, tname)(struct CTEST_IMPL_DATA_SNAME(sname)* fixt); \ + static void (*CTEST_IMPL_SETUP_TPNAME(sname, tname))(struct CTEST_IMPL_DATA_SNAME(sname)*) = &CTEST_IMPL_SETUP_FNAME(sname); \ + static void (*CTEST_IMPL_TEARDOWN_TPNAME(sname, tname))(struct CTEST_IMPL_DATA_SNAME(sname)*) = &CTEST_IMPL_TEARDOWN_FNAME(sname); \ + CTEST_IMPL_STRUCT(sname, tname, tskip, &CTEST_IMPL_DATA_TNAME(sname, tname), &CTEST_IMPL_SETUP_TPNAME(sname, tname), &CTEST_IMPL_TEARDOWN_TPNAME(sname, tname)); \ + static void CTEST_IMPL_FNAME(sname, tname)(struct CTEST_IMPL_DATA_SNAME(sname)* fixt) + +#else + +#define CTEST_SETUP(sname, fixt) \ + static void CTEST_IMPL_SETUP_FNAME(sname)(struct CTEST_IMPL_DATA_SNAME(sname)* fixt); \ + static void (*CTEST_IMPL_SETUP_FPNAME(sname))(struct CTEST_IMPL_DATA_SNAME(sname)*) = &CTEST_IMPL_SETUP_FNAME(sname); \ + static void CTEST_IMPL_SETUP_FNAME(sname)(struct CTEST_IMPL_DATA_SNAME(sname)* fixt) + +#define CTEST_TEARDOWN(sname, fixt) \ + static void CTEST_IMPL_TEARDOWN_FNAME(sname)(struct CTEST_IMPL_DATA_SNAME(sname)* fixt); \ + static void (*CTEST_IMPL_TEARDOWN_FPNAME(sname))(struct CTEST_IMPL_DATA_SNAME(sname)*) = &CTEST_IMPL_TEARDOWN_FNAME(sname); \ + static void CTEST_IMPL_TEARDOWN_FNAME(sname)(struct CTEST_IMPL_DATA_SNAME(sname)* fixt) + +#define CTEST_FIXTURE(sname) \ + struct CTEST_IMPL_DATA_SNAME(sname); \ + static void (*CTEST_IMPL_SETUP_FPNAME(sname))(struct CTEST_IMPL_DATA_SNAME(sname)*); \ + static void (*CTEST_IMPL_TEARDOWN_FPNAME(sname))(struct CTEST_IMPL_DATA_SNAME(sname)*); \ + struct CTEST_IMPL_DATA_SNAME(sname) + +#define CTEST_IMPL_CTEST(sname, tname, tskip) \ + static void CTEST_IMPL_FNAME(sname, tname)(void); \ + CTEST_IMPL_STRUCT(sname, tname, tskip, NULL, NULL, NULL); \ + static void CTEST_IMPL_FNAME(sname, tname)(void) + +#define CTEST_IMPL_CTEST_F(sname, tname, tskip, fixt) \ + static struct CTEST_IMPL_DATA_SNAME(sname) CTEST_IMPL_DATA_TNAME(sname, tname); \ + static void CTEST_IMPL_FNAME(sname, tname)(struct CTEST_IMPL_DATA_SNAME(sname)* fixt); \ + CTEST_IMPL_STRUCT(sname, tname, tskip, &CTEST_IMPL_DATA_TNAME(sname, tname), &CTEST_IMPL_SETUP_FPNAME(sname), &CTEST_IMPL_TEARDOWN_FPNAME(sname)); \ + static void CTEST_IMPL_FNAME(sname, tname)(struct CTEST_IMPL_DATA_SNAME(sname)* fixt) + +#endif + +void assert_str(int diag, const char* cmp, const char* exp, const char* real, const char* caller, int line); +void assert_wstr(int diag, const char* cmp, const wchar_t *exp, const wchar_t *real, const char* caller, int line); +void assert_compare(int diag, const char* cmp, intmax_t exp, intmax_t real, const char* caller, int line); +void assert_interval(int diag, intmax_t low, intmax_t high, intmax_t real, const char* caller, int line); +void assert_pointers(int diag, const char* cmp, const void* exp, const void* real, const char* caller, int line); +void assert_bool(int diag, bool exp, bool real, const char* caller, int line); +void assert_dbl_compare(int diag, const char* cmp, double exp, double real, double tol, const char* caller, int line); +void assert_fail(const char* caller, int line); + +#define CTEST_FLT_EPSILON 1e-5 +#define CTEST_DBL_EPSILON 1e-12 + +#define CTEST(sname, tname) CTEST_IMPL_CTEST(sname, tname, 0) +#define CTEST_F(sname, tname, fixt) CTEST_IMPL_CTEST_F(sname, tname, 0, fixt) +#define CTEST_SKIP(sname, tname) CTEST_IMPL_CTEST(sname, tname, 1) +#define CTEST_F_SKIP(sname, tname, fixt) CTEST_IMPL_CTEST_F(sname, tname, 1, fixt) + +// Aliases, more like gtest: +#define TEST(sname, tname) CTEST(sname, tname) +#define TEST_SKIP(sname, tname) CTEST_SKIP(sname, tname) + +#define TEST_FIXTURE(sname) CTEST_FIXTURE(sname) +#define TEST_SETUP(sname, fixt) CTEST_SETUP(sname, fixt) +#define TEST_TEARDOWN(sname, fixt) CTEST_TEARDOWN(sname, fixt) +#define TEST_F(sname, tname, fixt) CTEST_F(sname, tname, fixt) +#define TEST_F_SKIP(sname, tname, fixt) CTEST_F_SKIP(sname, tname, fixt) + +// private +#define _CHECK_STREQ(diag, exp, real) assert_str(diag, "==", exp, real, __FILE__, __LINE__) +#define _CHECK_STRNE(diag, exp, real) assert_str(diag, "!=", exp, real, __FILE__, __LINE__) +#define _CHECK_SUBSTR(diag, substr, real) assert_str(diag, "=~", substr, real, __FILE__, __LINE__) +#define _CHECK_NOT_SUBSTR(diag, substr, real) assert_str(diag, "!~", substr, real, __FILE__, __LINE__) +#define _CHECK_WSTREQ(diag, exp, real) assert_wstr(diag, "==", exp, real, __FILE__, __LINE__) +#define _CHECK_WSTRNE(diag, exp, real) assert_wstr(diag, "!=", exp, real, __FILE__, __LINE__) +#define _CHECK_EQ(diag, v1, v2) assert_compare(diag, "==", v1, v2, __FILE__, __LINE__) +#define _CHECK_NE(diag, v1, v2) assert_compare(diag, "!=", v1, v2, __FILE__, __LINE__) +#define _CHECK_LT(diag, v1, v2) assert_compare(diag, "<", v1, v2, __FILE__, __LINE__) +#define _CHECK_LE(diag, v1, v2) assert_compare(diag, "<=", v1, v2, __FILE__, __LINE__) +#define _CHECK_GT(diag, v1, v2) assert_compare(diag, ">", v1, v2, __FILE__, __LINE__) +#define _CHECK_GE(diag, v1, v2) assert_compare(diag, ">=", v1, v2, __FILE__, __LINE__) +#define _CHECK_INTERVAL(diag, low, high, real) assert_interval(diag, low, high, real, __FILE__, __LINE__) +#define _CHECK_PTR_EQ(diag, exp, real) ((void)sizeof((exp) == (real)), assert_pointers(diag, "==", exp, real, __FILE__, __LINE__)) +#define _CHECK_PTR_NE(diag, exp, real) ((void)sizeof((exp) != (real)), assert_pointers(diag, "!=", exp, real, __FILE__, __LINE__)) +#define _CHECK_BOOL(diag, exp, real) assert_bool(diag, exp, real, __FILE__, __LINE__) +#define _CHECK_NEAR(diag, exp, real, tol) assert_dbl_compare(diag, "==", exp, real, tol, __FILE__, __LINE__) +#define _CHECK_NOT_NEAR(diag, exp, real, tol) assert_dbl_compare(diag, "!=", exp, real, tol, __FILE__, __LINE__) +#define _CHECK_DOUBLE_EQ(diag, exp, real) assert_dbl_compare(diag, "==", exp, real, -CTEST_DBL_EPSILON, __FILE__, __LINE__) +#define _CHECK_DOUBLE_NE(diag, exp, real) assert_dbl_compare(diag, "!=", exp, real, -CTEST_DBL_EPSILON, __FILE__, __LINE__) +#define _CHECK_DOUBLE_LT(diag, v1, v2) assert_dbl_compare(diag, "<", v1, v2, 0.0, __FILE__, __LINE__) +#define _CHECK_DOUBLE_GT(diag, v1, v2) assert_dbl_compare(diag, ">", v1, v2, 0.0, __FILE__, __LINE__) +#define _CHECK_FLOAT_EQ(diag, v1, v2) assert_dbl_compare(diag, "==", v1, v2, -CTEST_FLT_EPSILON, __FILE__, __LINE__) +#define _CHECK_FLOAT_NE(diag, v1, v2) assert_dbl_compare(diag, "!=", v1, v2, -CTEST_FLT_EPSILON, __FILE__, __LINE__) +#define _CHECK_FLOAT_LT(diag, v1, v2) assert_dbl_compare(diag, "<", (float)(v1), (float)(v2), 0.0, __FILE__, __LINE__) +#define _CHECK_FLOAT_GT(diag, v1, v2) assert_dbl_compare(diag, ">", (float)(v1), (float)(v2), 0.0, __FILE__, __LINE__) + +// EXPECT ===================================== + +#define EXPECT_STREQ(exp, real) _CHECK_STREQ(1, exp, real) +#define EXPECT_STRNE(exp, real) _CHECK_STRNE(1, exp, real) +#define EXPECT_SUBSTR(substr, real) _CHECK_SUBSTR(1, substr, real) +#define EXPECT_NOT_SUBSTR(substr, real) _CHECK_NOT_SUBSTR(1, substr, real) +#define EXPECT_WSTREQ(exp, real) _CHECK_WSTREQ(1, exp, real) +#define EXPECT_WSTRNE(exp, real) _CHECK_WSTRNE(1, exp, real) +#define EXPECT_EQ(v1, v2) _CHECK_EQ(1, v1, v2) +#define EXPECT_NE(v1, v2) _CHECK_NE(1, v1, v2) +#define EXPECT_LT(v1, v2) _CHECK_LT(1, v1, v2) +#define EXPECT_LE(v1, v2) _CHECK_LE(1, v1, v2) +#define EXPECT_GT(v1, v2) _CHECK_GT(1, v1, v2) +#define EXPECT_GE(v1, v2) _CHECK_GE(1, v1, v2) +#define EXPECT_INTERVAL(low, high, real) _CHECK_INTERVAL(1, low, high, real) +#define EXPECT_PTR_EQ(exp, real) _CHECK_PTR_EQ(1, exp, real) +#define EXPECT_PTR_NE(exp, real) _CHECK_PTR_NE(1, exp, real) +#define EXPECT_NULL(real) _CHECK_PTR_EQ(1, NULL, real) +#define EXPECT_NOT_NULL(real) _CHECK_PTR_NE(1, NULL, real) +#define EXPECT_TRUE(real) _CHECK_BOOL(1, true, real) +#define EXPECT_FALSE(real) _CHECK_BOOL(1, false, real) +#define EXPECT_NEAR(exp, real, tol) _CHECK_NEAR(1, exp, real, tol) +#define EXPECT_NOT_NEAR(exp, real, tol) _CHECK_NOT_NEAR(1, exp, real, tol) +#define EXPECT_DOUBLE_EQ(exp, real) _CHECK_DOUBLE_EQ(1, exp, real) +#define EXPECT_DOUBLE_NE(exp, real) _CHECK_DOUBLE_NE(1, exp, real) +#define EXPECT_DOUBLE_LT(v1, v2) _CHECK_DOUBLE_LT(1, v1, v2) +#define EXPECT_DOUBLE_GT(v1, v2) _CHECK_DOUBLE_GT(1, v1, v2) +#define EXPECT_FLOAT_EQ(v1, v2) _CHECK_FLOAT_EQ(1, v1, v2) +#define EXPECT_FLOAT_NE(v1, v2) _CHECK_FLOAT_NE(1, v1, v2) +#define EXPECT_FLOAT_LT(v1, v2) _CHECK_FLOAT_LT(1, v1, v2) +#define EXPECT_FLOAT_GT(v1, v2) _CHECK_FLOAT_GT(1, v1, v2) + +// ASSERT ===================================== + +#define ASSERT_STREQ(exp, real) _CHECK_STREQ(2, exp, real) +#define ASSERT_STRNE(exp, real) _CHECK_STRNE(2, exp, real) +#define ASSERT_SUBSTR(substr, real) _CHECK_SUBSTR(2, substr, real) +#define ASSERT_NOT_SUBSTR(substr, real) _CHECK_NOT_SUBSTR(2, substr, real) +#define ASSERT_WSTREQ(exp, real) _CHECK_WSTREQ(2, exp, real) +#define ASSERT_WSTRNE(exp, real) _CHECK_WSTRNE(2, exp, real) +#define ASSERT_EQ(v1, v2) _CHECK_EQ(2, v1, v2) +#define ASSERT_NE(v1, v2) _CHECK_NE(2, v1, v2) +#define ASSERT_LT(v1, v2) _CHECK_LT(2, v1, v2) +#define ASSERT_LE(v1, v2) _CHECK_LE(2, v1, v2) +#define ASSERT_GT(v1, v2) _CHECK_GT(2, v1, v2) +#define ASSERT_GE(v1, v2) _CHECK_GE(2, v1, v2) +#define ASSERT_INTERVAL(low, high, real) _CHECK_INTERVAL(2, low, high, real) +#define ASSERT_PTR_EQ(exp, real) _CHECK_PTR_EQ(2, exp, real) +#define ASSERT_PTR_NE(exp, real) _CHECK_PTR_NE(2, exp, real) +#define ASSERT_NULL(real) _CHECK_PTR_EQ(2, NULL, real) +#define ASSERT_NOT_NULL(real) _CHECK_PTR_NE(2, NULL, real) +#define ASSERT_TRUE(real) _CHECK_BOOL(2, true, real) +#define ASSERT_FALSE(real) _CHECK_BOOL(2, false, real) +#define ASSERT_NEAR(exp, real, tol) _CHECK_NEAR(2, exp, real, tol) +#define ASSERT_NOT_NEAR(exp, real, tol) _CHECK_NOT_NEAR(2, exp, real, tol) +#define ASSERT_DOUBLE_EQ(exp, real) _CHECK_DOUBLE_EQ(2, exp, real) +#define ASSERT_DOUBLE_NE(exp, real) _CHECK_DOUBLE_NE(2, exp, real) +#define ASSERT_DOUBLE_LT(v1, v2) _CHECK_DOUBLE_LT(2, v1, v2) +#define ASSERT_DOUBLE_GT(v1, v2) _CHECK_DOUBLE_GT(2, v1, v2) +#define ASSERT_FLOAT_EQ(v1, v2) _CHECK_FLOAT_EQ(2, v1, v2) +#define ASSERT_FLOAT_NE(v1, v2) _CHECK_FLOAT_NE(2, v1, v2) +#define ASSERT_FLOAT_LT(v1, v2) _CHECK_FLOAT_LT(2, v1, v2) +#define ASSERT_FLOAT_GT(v1, v2) _CHECK_FLOAT_GT(2, v1, v2) +#define ASSERT_FAIL() assert_fail(__FILE__, __LINE__) + +#ifdef CTEST_MAIN + +#include +#include +#include +#include +#include +#if !defined(_WIN32) || defined(__GNUC__) +#include +#elif defined(_WIN32) +#include +#endif +#include +#include +#include + +static int ctest_failed; +static size_t ctest_errorsize; +static char* ctest_errormsg; +#define MSG_SIZE 4096 +static char ctest_errorbuffer[MSG_SIZE]; +static jmp_buf ctest_err; +static int color_output = 1; +static const char* suite_name; + +typedef int (*ctest_filter_func)(struct ctest*); + +#define ANSI_BLACK "\033[0;30m" +#define ANSI_RED "\033[0;31m" +#define ANSI_GREEN "\033[0;32m" +#define ANSI_YELLOW "\033[0;33m" +#define ANSI_BLUE "\033[0;34m" +#define ANSI_MAGENTA "\033[0;35m" +#define ANSI_CYAN "\033[0;36m" +#define ANSI_GREY "\033[0;37m" +#define ANSI_DARKGREY "\033[01;30m" +#define ANSI_BRED "\033[01;31m" +#define ANSI_BGREEN "\033[01;32m" +#define ANSI_BYELLOW "\033[01;33m" +#define ANSI_BBLUE "\033[01;34m" +#define ANSI_BMAGENTA "\033[01;35m" +#define ANSI_BCYAN "\033[01;36m" +#define ANSI_WHITE "\033[01;37m" +#define ANSI_NORMAL "\033[0m" + +CTEST(suite, test) { } + +static void vprint_errormsg(const char* const fmt, va_list ap) CTEST_IMPL_FORMAT_PRINTF(1, 0); +static void print_errormsg(const char* const fmt, ...) CTEST_IMPL_FORMAT_PRINTF(1, 2); + +static void vprint_errormsg(const char* const fmt, va_list ap) { + // (v)snprintf returns the number that would have been written + const int ret = vsnprintf(ctest_errormsg, ctest_errorsize, fmt, ap); + if (ret < 0) { + ctest_errormsg[0] = 0x00; + } else { + const size_t size = (size_t) ret; + const size_t s = (ctest_errorsize <= size ? size -ctest_errorsize : size); + // ctest_errorsize may overflow at this point + ctest_errorsize -= s; + ctest_errormsg += s; + } +} + +static void print_errormsg(const char* const fmt, ...) { + va_list argp; + va_start(argp, fmt); + vprint_errormsg(fmt, argp); + va_end(argp); +} + +static void msg_start(const char* color, const char* title) { + if (color_output) { + print_errormsg("%s", color); + } + print_errormsg(" %s: ", title); +} + +static void msg_end(void) { + if (color_output) { + print_errormsg(ANSI_NORMAL); + } + print_errormsg("\n"); +} + +CTEST_IMPL_DIAG_PUSH_IGNORED(missing-noreturn) + +static void ctest_print(int diag, const char* fmt, ...) CTEST_IMPL_FORMAT_PRINTF(2, 3); // may not return +static void ctest_print(int diag, const char* fmt, ...) +{ + va_list argp; + switch (diag) { + case 0: msg_start(ANSI_BLUE, "LOG"); break; + case 1: msg_start(ANSI_YELLOW, "ERR"); break; + case 2: msg_start(ANSI_BYELLOW, "ERR"); break; + } + va_start(argp, fmt); + vprint_errormsg(fmt, argp); + va_end(argp); + + msg_end(); + + if (diag == 2) + longjmp(ctest_err, 1); + ctest_failed = 1; +} + +CTEST_IMPL_DIAG_POP() + +void assert_str(int diag, const char* cmp, const char* exp, const char* real, const char* caller, int line) { + if ((!exp ^ !real) || (exp && ( + (cmp[1] == '=' && ((cmp[0] == '=') ^ (strcmp(real, exp) == 0))) || + (cmp[1] == '~' && ((cmp[0] == '=') ^ (strstr(real, exp) != NULL))) + ))) { + ctest_print(diag, "%s:%d assertion failed, '%s' %s '%s'", caller, line, exp, cmp, real); + } +} + +void assert_wstr(int diag, const char* cmp, const wchar_t *exp, const wchar_t *real, const char* caller, int line) { + if ((!exp ^ !real) || (exp && ( + (cmp[1] == '=' && ((cmp[0] == '=') ^ (wcscmp(real, exp) == 0))) || + (cmp[1] == '~' && ((cmp[0] == '=') ^ (wcsstr(real, exp) != NULL))) + ))) { + ctest_print(diag, "%s:%d assertion failed, '%ls' %s '%ls'", caller, line, exp, cmp, real); + } +} + +static bool get_compare_result(const char* cmp, int c3, bool eq) { + if (cmp[0] == '<') + return c3 < 0 || ((cmp[1] == '=') & eq); + if (cmp[0] == '>') + return c3 > 0 || ((cmp[1] == '=') & eq); + return (cmp[0] == '=') == eq; +} + +void assert_compare(int diag, const char* cmp, intmax_t exp, intmax_t real, const char* caller, int line) { + int c3 = (real < exp) - (exp < real); + + if (!get_compare_result(cmp, c3, c3 == 0)) { + ctest_print(diag, "%s:%d assertion failed, %" PRIdMAX " %s %" PRIdMAX "", caller, line, exp, cmp, real); + } +} + +void assert_interval(int diag, intmax_t low, intmax_t high, intmax_t real, const char* caller, int line) { + if (real < low || real > high) { + ctest_print(diag, "%s:%d expected %" PRIdMAX "-%" PRIdMAX ", got %" PRIdMAX, caller, line, low, high, real); + } +} + +static bool approximately_equal(double a, double b, double epsilon) { + double d = a - b; + if (d < 0) d = -d; + if (a < 0) a = -a; + if (b < 0) b = -b; + return d <= (a > b ? a : b)*epsilon; /* D.Knuth */ +} + +/* tol < 0 means it is an epsilon, else absolute error */ +void assert_dbl_compare(int diag, const char* cmp, double exp, double real, double tol, const char* caller, int line) { + double diff = exp - real; + double absdiff = diff < 0 ? -diff : diff; + int c3 = (real < exp) - (exp < real); + bool eq = tol < 0 ? approximately_equal(exp, real, -tol) : absdiff <= tol; + + if (!get_compare_result(cmp, c3, eq)) { + const char* tolstr = "tol"; + if (tol < 0) { + tolstr = "eps"; + tol = -tol; + } + ctest_print(diag, "%s:%d assertion failed, %.8g %s %.8g (diff %.4g, %s %.4g)", caller, line, exp, cmp, real, diff, tolstr, tol); + } +} + +void assert_pointers(int diag, const char* cmp, const void* exp, const void* real, const char* caller, int line) { + if ((exp == real) != (cmp[0] == '=')) { + ctest_print(diag, "%s:%d assertion failed (0x%02llx) %s (0x%02llx)", caller, line, + (unsigned long long)(uintptr_t)exp , cmp, (unsigned long long)(uintptr_t)real); + } +} + +void assert_bool(int diag, bool exp, bool real, const char* caller, int line) { + if (exp != real) { + ctest_print(diag, "%s:%d should be %s", caller, line, exp ? "true" : "false"); + } +} + +void assert_fail(const char* caller, int line) { + ctest_print(2, "%s:%d shouldn't come here", caller, line); +} + + +static int suite_all(struct ctest* t) { + (void) t; // avoid unused parameter warning + return 1; +} + +static int suite_filter(struct ctest* t) { + return strncmp(suite_name, t->ssname, strlen(suite_name)) == 0; +} + +static void color_print(const char* color, const char* text) { + if (color_output) + printf("%s%s" ANSI_NORMAL "\n", color, text); + else + printf("%s\n", text); +} + +#ifndef CTEST_NO_SEGFAULT +#include +static void sighandler(int signum) +{ + const char msg_color[] = ANSI_BRED "[SIGSEGV: Segmentation fault]" ANSI_NORMAL "\n"; + const char msg_nocolor[] = "[SIGSEGV: Segmentation fault]\n"; + + const char* msg = color_output ? msg_color : msg_nocolor; + intptr_t n = write(1, msg, (unsigned int)strlen(msg)); + (void)n; + /* "Unregister" the signal handler and send the signal back to the process + * so it can terminate as expected */ + signal(signum, SIG_DFL); +#if !defined(_WIN32) || defined(__CYGWIN__) + //kill(getpid(), signum); +#endif +} +#endif + +int ctest_main(int argc, const char *argv[]); +#ifdef __GNUC__ +__attribute__((no_sanitize_address)) +#endif +int ctest_main(int argc, const char *argv[]) +{ + static int total = 0; + static int num_ok = 0; + static int num_fail = 0; + static int num_skip = 0; + static int idx = 1; + static ctest_filter_func filter = suite_all; + +#ifndef CTEST_NO_SEGFAULT + signal(SIGSEGV, sighandler); +#endif + + if (argc == 2) { + suite_name = argv[1]; + filter = suite_filter; + } +#ifdef CTEST_NO_COLORS + color_output = 0; +#else + color_output = isatty(1); +#endif + clock_t t1 = clock(); + + uint32_t* magic_begin = &CTEST_IMPL_TNAME(suite, test).magic1; + uint32_t* magic_end = &CTEST_IMPL_TNAME(suite, test).magic0, *m; + ptrdiff_t num_ints = sizeof(struct ctest)/sizeof *m; + +#if (defined __TINYC__ && defined __unix__) + #define CTEST_IMPL_MAGIC_SEEK 10 /* search 4*(1+10) bytes outside ctest entry bounds */ +#else + #define CTEST_IMPL_MAGIC_SEEK 0 /* access only 4 bytes outside outer ctest entry bounds */ +#endif + for (m = magic_begin; magic_begin - m <= num_ints + CTEST_IMPL_MAGIC_SEEK; --m) { + if (*m == 0xBADCAFE1) { + magic_begin = m; + m -= num_ints - 1; + } + } + for (m = magic_end; m - magic_end <= num_ints; ++m) { + if (*m == 0xBADCAFE0) { + magic_end = m; + m += num_ints - 1; + } + } + magic_begin = &CTEST_CONTAINER_OF(magic_begin, struct ctest, magic1)->magic0; + + static struct ctest* test; + for (m = magic_begin; m <= magic_end; m += num_ints) { + while (*m != 0xBADCAFE0) ++m; + test = CTEST_CONTAINER_OF(m, struct ctest, magic0); + if (test == &CTEST_IMPL_TNAME(suite, test)) continue; + if (filter(test)) total++; + } + + for (m = magic_begin; m <= magic_end; m += num_ints) { + while (*m != 0xBADCAFE0) ++m; + test = CTEST_CONTAINER_OF(m, struct ctest, magic0); + if (test == &CTEST_IMPL_TNAME(suite, test)) continue; + if (filter(test)) { + ctest_failed = 0; + ctest_errorbuffer[0] = 0; + ctest_errorsize = MSG_SIZE-1; + ctest_errormsg = ctest_errorbuffer; + printf("TEST %d/%d %s:%s ", idx, total, test->ssname, test->ttname); + fflush(stdout); + if (test->skip) { + color_print(ANSI_BYELLOW, "[SKIPPED]"); + num_skip++; + } else { + int result = setjmp(ctest_err); + if (result == 0) { + if (test->setup && *test->setup) (*test->setup)(test->data); + if (test->data) + test->run.unary(test->data); + else + test->run.nullary(); + if (test->teardown && *test->teardown) (*test->teardown)(test->data); + // if we got here it's ok + if (ctest_failed) longjmp(ctest_err, 1); +#ifdef CTEST_NO_COLOR_OK + printf("[OK]\n"); +#else + color_print(ANSI_BGREEN, "[OK]"); +#endif + num_ok++; + } else { + color_print(ANSI_BRED, "[FAIL]"); + num_fail++; + } + if (ctest_errorsize != MSG_SIZE-1) printf("%s", ctest_errorbuffer); + } + idx++; + } + } + clock_t t2 = clock(); + + const char* color = (num_fail) ? ANSI_BRED : ANSI_GREEN; + char results[80]; + snprintf(results, sizeof(results), "RESULTS: %d tests (%d ok, %d failed, %d skipped) ran in %.1f ms", + total, num_ok, num_fail, num_skip, (double)(t2 - t1)*1000.0/CLOCKS_PER_SEC); + color_print(color, results); + return num_fail; +} + +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/finchlite/codegen/stc/stc/deque.h b/src/finchlite/codegen/stc/stc/deque.h new file mode 100644 index 00000000..dd00206f --- /dev/null +++ b/src/finchlite/codegen/stc/stc/deque.h @@ -0,0 +1,205 @@ +/* MIT License + * + * Copyright (c) 2025 Tyge Løvset + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +// Deque - double ended queue. Implemented as a ring buffer, extension of queue. + +#include "priv/linkage.h" +#include "types.h" + +#ifndef STC_DEQUE_H_INCLUDED +#define STC_DEQUE_H_INCLUDED +#include "common.h" +#include +#endif // STC_DEQUE_H_INCLUDED + +#ifndef _i_prefix + #define _i_prefix deque_ +#endif +#define _pop _pop_front +#define _pull _pull_front +#include "priv/template.h" +#include "priv/queue_prv.h" +#undef _pop +#undef _pull + +STC_API _m_value* _c_MEMB(_push_front)(Self* self, _m_value value); +STC_API _m_iter _c_MEMB(_insert_n)(Self* self, isize idx, const _m_value* arr, isize n); +STC_API _m_iter _c_MEMB(_insert_uninit)(Self* self, isize idx, isize n); +STC_API void _c_MEMB(_erase_n)(Self* self, isize idx, isize n); + +STC_INLINE const _m_value* +_c_MEMB(_at)(const Self* self, isize idx) { + c_assert(c_uless(idx, _c_MEMB(_size)(self))); + return self->cbuf + _cbuf_topos(self, idx); +} + +STC_INLINE _m_value* +_c_MEMB(_at_mut)(Self* self, isize idx) + { return (_m_value*)_c_MEMB(_at)(self, idx); } + +STC_INLINE _m_value* +_c_MEMB(_push_back)(Self* self, _m_value val) + { return _c_MEMB(_push)(self, val); } + +STC_INLINE void +_c_MEMB(_pop_back)(Self* self) { + c_assert(!_c_MEMB(_is_empty)(self)); + self->end = (self->end - 1) & self->capmask; + i_keydrop((self->cbuf + self->end)); +} + +STC_INLINE _m_value _c_MEMB(_pull_back)(Self* self) { // move back out of deque + c_assert(!_c_MEMB(_is_empty)(self)); + self->end = (self->end - 1) & self->capmask; + return self->cbuf[self->end]; +} + +STC_INLINE _m_iter +_c_MEMB(_insert_at)(Self* self, _m_iter it, const _m_value val) { + isize idx = _cbuf_toidx(self, it.pos); + return _c_MEMB(_insert_n)(self, idx, &val, 1); +} + +STC_INLINE _m_iter +_c_MEMB(_erase_at)(Self* self, _m_iter it) { + _c_MEMB(_erase_n)(self, _cbuf_toidx(self, it.pos), 1); + if (it.pos == self->end) it.ref = NULL; + return it; +} + +STC_INLINE _m_iter +_c_MEMB(_erase_range)(Self* self, _m_iter it1, _m_iter it2) { + isize idx1 = _cbuf_toidx(self, it1.pos); + isize idx2 = _cbuf_toidx(self, it2.pos); + _c_MEMB(_erase_n)(self, idx1, idx2 - idx1); + if (it1.pos == self->end) it1.ref = NULL; + return it1; +} + +#ifndef i_no_emplace +STC_API _m_iter +_c_MEMB(_emplace_n)(Self* self, isize idx, const _m_raw* raw, isize n); + +STC_INLINE _m_value* +_c_MEMB(_emplace_front)(Self* self, const _m_raw raw) + { return _c_MEMB(_push_front)(self, i_keyfrom(raw)); } + +STC_INLINE _m_value* +_c_MEMB(_emplace_back)(Self* self, const _m_raw raw) + { return _c_MEMB(_push)(self, i_keyfrom(raw)); } + +STC_INLINE _m_iter +_c_MEMB(_emplace_at)(Self* self, _m_iter it, const _m_raw raw) + { return _c_MEMB(_insert_at)(self, it, i_keyfrom(raw)); } +#endif + +#if defined _i_has_eq +STC_API _m_iter _c_MEMB(_find_in)(const Self* self, _m_iter p1, _m_iter p2, _m_raw raw); + +STC_INLINE _m_iter +_c_MEMB(_find)(const Self* self, _m_raw raw) { + return _c_MEMB(_find_in)(self, _c_MEMB(_begin)(self), _c_MEMB(_end)(self), raw); +} +#endif // _i_has_eq + +#if defined _i_has_cmp +#include "priv/sort_prv.h" +#endif // _i_has_cmp + +/* -------------------------- IMPLEMENTATION ------------------------- */ +#if defined i_implement + +STC_DEF _m_value* +_c_MEMB(_push_front)(Self* self, _m_value value) { + isize start = (self->start - 1) & self->capmask; + if (start == self->end) { // full + if (!_c_MEMB(_reserve)(self, self->capmask + 3)) // => 2x expand + return NULL; + start = (self->start - 1) & self->capmask; + } + _m_value *v = self->cbuf + start; + self->start = start; + *v = value; + return v; +} + +STC_DEF void +_c_MEMB(_erase_n)(Self* self, const isize idx, const isize n) { + const isize len = _c_MEMB(_size)(self); + c_assert(idx + n <= len); + for (isize i = idx + n - 1; i >= idx; --i) + i_keydrop(_c_MEMB(_at_mut)(self, i)); + for (isize i = idx, j = i + n; j < len; ++i, ++j) + *_c_MEMB(_at_mut)(self, i) = *_c_MEMB(_at)(self, j); + self->end = (self->end - n) & self->capmask; +} + +STC_DEF _m_iter +_c_MEMB(_insert_uninit)(Self* self, const isize idx, const isize n) { + const isize len = _c_MEMB(_size)(self); + _m_iter it = {._s=self}; + if (len + n >= self->capmask) + if (!_c_MEMB(_reserve)(self, len + n)) // minimum 2x expand + return it; + it.pos = _cbuf_topos(self, idx); + it.ref = self->cbuf + it.pos; + self->end = (self->end + n) & self->capmask; + + if (it.pos < self->end) // common case because of reserve policy + c_memmove(it.ref + n, it.ref, (len - idx)*c_sizeof *it.ref); + else for (isize i = len - 1, j = i + n; i >= idx; --i, --j) + *_c_MEMB(_at_mut)(self, j) = *_c_MEMB(_at)(self, i); + return it; +} + +STC_DEF _m_iter +_c_MEMB(_insert_n)(Self* self, const isize idx, const _m_value* arr, const isize n) { + _m_iter it = _c_MEMB(_insert_uninit)(self, idx, n); + for (isize i = idx, j = 0; j < n; ++i, ++j) + *_c_MEMB(_at_mut)(self, i) = arr[j]; + return it; +} + +#ifndef i_no_emplace +STC_DEF _m_iter +_c_MEMB(_emplace_n)(Self* self, const isize idx, const _m_raw* raw, const isize n) { + _m_iter it = _c_MEMB(_insert_uninit)(self, idx, n); + for (isize i = idx, j = 0; j < n; ++i, ++j) + *_c_MEMB(_at_mut)(self, i) = i_keyfrom(raw[j]); + return it; +} +#endif + +#if defined _i_has_eq +STC_DEF _m_iter +_c_MEMB(_find_in)(const Self* self, _m_iter i1, _m_iter i2, _m_raw raw) { + (void)self; + for (; i1.ref != i2.ref; _c_MEMB(_next)(&i1)) { + const _m_raw r = i_keytoraw(i1.ref); + if (i_eq((&raw), (&r))) + break; + } + return i1; +} +#endif +#endif // IMPLEMENTATION +#include "sys/finalize.h" diff --git a/src/finchlite/codegen/stc/stc/hashmap.h b/src/finchlite/codegen/stc/stc/hashmap.h new file mode 100644 index 00000000..5eaaf9df --- /dev/null +++ b/src/finchlite/codegen/stc/stc/hashmap.h @@ -0,0 +1,43 @@ +/* MIT License + * + * Copyright (c) 2025 Tyge Løvmap + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +// Unordered map - implemented with the robin-hood hashing scheme. +/* +#define T IMap, int, int +#include +#include + +int main(void) { + IMap map = c_make(IMap, {{12, 32}, {42, 54}}); + IMap_insert(&map, 5, 15); + IMap_insert(&map, 8, 18); + + for (c_each_kv(k, v, IMap, map)) + printf(" %d -> %d\n", *k, *v); + + IMap_drop(&map); +} +*/ + +#define _i_prefix hmap_ +#include "hmap.h" diff --git a/src/finchlite/codegen/stc/stc/hashset.h b/src/finchlite/codegen/stc/stc/hashset.h new file mode 100644 index 00000000..76a858d7 --- /dev/null +++ b/src/finchlite/codegen/stc/stc/hashset.h @@ -0,0 +1,44 @@ +/* MIT License + * + * Copyright (c) 2025 Tyge Løvset + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +// Unordered set - implemented with the robin-hood hashing scheme. +/* +#define T ISet, int +#include +#include + +int main(void) { + ISet set = {0}; + ISet_insert(&set, 5); + ISet_insert(&set, 8); + + for (c_each(i, ISet, set)) + printf(" %d\n", *i.ref); + + ISet_drop(&set); +} +*/ + +#define _i_prefix hset_ +#define _i_is_set +#include "hmap.h" diff --git a/src/finchlite/codegen/stc/stc/hmap.h b/src/finchlite/codegen/stc/stc/hmap.h new file mode 100644 index 00000000..b6988c32 --- /dev/null +++ b/src/finchlite/codegen/stc/stc/hmap.h @@ -0,0 +1,517 @@ +/* MIT License + * + * Copyright (c) 2025 Tyge Løvset + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +// Unordered set/map - implemented with the robin-hood hashing scheme. +/* +#include + +#define T icmap, int, char +#include + +int main(void) { + icmap m = {0}; + icmap_emplace(&m, 5, 'a'); + icmap_emplace(&m, 8, 'b'); + icmap_emplace(&m, 12, 'c'); + + icmap_value* v = icmap_get(&m, 10); // NULL + char val = *icmap_at(&m, 5); // 'a' + icmap_emplace_or_assign(&m, 5, 'd'); // update + icmap_erase(&m, 8); + + for (c_each(i, icmap, m)) + printf("map %d: %c\n", i.ref->first, i.ref->second); + + icmap_drop(&m); +} +*/ +#include "priv/linkage.h" +#include "types.h" + +#ifndef STC_HMAP_H_INCLUDED +#define STC_HMAP_H_INCLUDED +#include "common.h" +#include +#define _hashmask 0x3fU +#define _distmask 0x3ffU +struct hmap_meta { uint16_t hashx:6, dist:10; }; // dist: 0=empty, 1=PSL 0, 2=PSL 1, ... +#endif // STC_HMAP_H_INCLUDED + +#ifndef _i_prefix + #define _i_prefix hmap_ +#endif +#ifndef _i_is_set + #define _i_is_map + #define _i_MAP_ONLY c_true + #define _i_SET_ONLY c_false + #define _i_keyref(vp) (&(vp)->first) +#else + #define _i_MAP_ONLY c_false + #define _i_SET_ONLY c_true + #define _i_keyref(vp) (vp) +#endif +#define _i_is_hash +#include "priv/template.h" +#ifndef i_declared + _c_DEFTYPES(_declare_htable, Self, i_key, i_val, _i_MAP_ONLY, _i_SET_ONLY, _i_aux_def); +#endif + +_i_MAP_ONLY( struct _m_value { + _m_key first; + _m_mapped second; +}; ) + +typedef i_keyraw _m_keyraw; +typedef i_valraw _m_rmapped; +typedef _i_SET_ONLY( i_keyraw ) + _i_MAP_ONLY( struct { _m_keyraw first; + _m_rmapped second; } ) +_m_raw; + +#ifndef i_no_clone +STC_API Self _c_MEMB(_clone)(Self map); +#endif +STC_API void _c_MEMB(_drop)(const Self* cself); +STC_API void _c_MEMB(_clear)(Self* self); +STC_API bool _c_MEMB(_reserve)(Self* self, isize capacity); +STC_API void _c_MEMB(_erase_entry)(Self* self, _m_value* val); +STC_API float _c_MEMB(_max_load_factor)(const Self* self); +STC_API isize _c_MEMB(_capacity)(const Self* map); +STC_API _m_result _c_MEMB(_bucket_lookup_)(const Self* self, const _m_keyraw* rkeyptr); +STC_API _m_result _c_MEMB(_bucket_insert_)(const Self* self, const _m_keyraw* rkeyptr); + +STC_INLINE bool _c_MEMB(_is_empty)(const Self* map) { return !map->size; } +STC_INLINE isize _c_MEMB(_size)(const Self* map) { return (isize)map->size; } +STC_INLINE isize _c_MEMB(_bucket_count)(Self* map) { return map->bucket_count; } +STC_INLINE bool _c_MEMB(_contains)(const Self* self, _m_keyraw rkey) + { return self->size && _c_MEMB(_bucket_lookup_)(self, &rkey).ref; } +STC_INLINE void _c_MEMB(_shrink_to_fit)(Self* self) + { _c_MEMB(_reserve)(self, (isize)self->size); } + +#ifndef i_max_load_factor + #define i_max_load_factor 0.80f +#endif + +STC_INLINE _m_result +_c_MEMB(_insert_entry_)(Self* self, _m_keyraw rkey) { + if (self->size >= (isize)((float)self->bucket_count * (i_max_load_factor))) + if (!_c_MEMB(_reserve)(self, (isize)(self->size*3/2 + 2))) + return c_literal(_m_result){0}; + + _m_result res = _c_MEMB(_bucket_insert_)(self, &rkey); + self->size += res.inserted; + return res; +} + +#ifdef _i_is_map + STC_API _m_result _c_MEMB(_insert_or_assign)(Self* self, _m_key key, _m_mapped mapped); + #ifndef i_no_emplace + STC_API _m_result _c_MEMB(_emplace_or_assign)(Self* self, _m_keyraw rkey, _m_rmapped rmapped); + #endif + + STC_INLINE const _m_mapped* _c_MEMB(_at)(const Self* self, _m_keyraw rkey) { + _m_result res = _c_MEMB(_bucket_lookup_)(self, &rkey); + c_assert(res.ref); + return &res.ref->second; + } + + STC_INLINE _m_mapped* _c_MEMB(_at_mut)(Self* self, _m_keyraw rkey) + { return (_m_mapped*)_c_MEMB(_at)(self, rkey); } +#endif // _i_is_map + +#ifndef i_no_clone + STC_INLINE void _c_MEMB(_copy)(Self *self, const Self* other) { + if (self == other) + return; + _c_MEMB(_drop)(self); + *self = _c_MEMB(_clone)(*other); + } + + STC_INLINE _m_value _c_MEMB(_value_clone)(const Self* self, _m_value _val) { + (void)self; + *_i_keyref(&_val) = i_keyclone((*_i_keyref(&_val))); + _i_MAP_ONLY( _val.second = i_valclone(_val.second); ) + return _val; + } +#endif // !i_no_clone + +#ifndef i_no_emplace + STC_INLINE _m_result + _c_MEMB(_emplace)(Self* self, _m_keyraw rkey _i_MAP_ONLY(, _m_rmapped rmapped)) { + _m_result _res = _c_MEMB(_insert_entry_)(self, rkey); + if (_res.inserted) { + *_i_keyref(_res.ref) = i_keyfrom(rkey); + _i_MAP_ONLY( _res.ref->second = i_valfrom(rmapped); ) + } + return _res; + } +#endif // !i_no_emplace + +STC_INLINE _m_raw _c_MEMB(_value_toraw)(const _m_value* val) { + return _i_SET_ONLY( i_keytoraw(val) ) + _i_MAP_ONLY( c_literal(_m_raw){i_keytoraw((&val->first)), i_valtoraw((&val->second))} ); +} + +STC_INLINE void _c_MEMB(_value_drop)(const Self* self, _m_value* _val) { + (void)self; + i_keydrop(_i_keyref(_val)); + _i_MAP_ONLY( i_valdrop((&_val->second)); ) +} + +STC_INLINE Self _c_MEMB(_move)(Self *self) { + Self m = *self; + self->bucket_count = self->size = 0; + self->meta = NULL; self->table = NULL; + return m; +} + +STC_INLINE void _c_MEMB(_take)(Self *self, Self unowned) { + _c_MEMB(_drop)(self); + *self = unowned; +} + +STC_INLINE _m_result +_c_MEMB(_insert)(Self* self, _m_key _key _i_MAP_ONLY(, _m_mapped _mapped)) { + _m_result _res = _c_MEMB(_insert_entry_)(self, i_keytoraw((&_key))); + if (_res.inserted) + { *_i_keyref(_res.ref) = _key; _i_MAP_ONLY( _res.ref->second = _mapped; )} + else + { i_keydrop((&_key)); _i_MAP_ONLY( i_valdrop((&_mapped)); )} + return _res; +} + +STC_INLINE _m_value* _c_MEMB(_push)(Self* self, _m_value _val) { + _m_result _res = _c_MEMB(_insert_entry_)(self, i_keytoraw(_i_keyref(&_val))); + if (_res.inserted) + *_res.ref = _val; + else + _c_MEMB(_value_drop)(self, &_val); + return _res.ref; +} + +#if defined _i_is_map && !defined _i_no_put +STC_INLINE _m_result _c_MEMB(_put)(Self* self, _m_keyraw rkey, _m_rmapped rmapped) { + #ifdef i_no_emplace + return _c_MEMB(_insert_or_assign)(self, rkey, rmapped); + #else + return _c_MEMB(_emplace_or_assign)(self, rkey, rmapped); + #endif +} +#endif + +#ifndef _i_no_put +STC_INLINE void _c_MEMB(_put_n)(Self* self, const _m_raw* raw, isize n) { + while (n--) + #if defined _i_is_set && defined i_no_emplace + _c_MEMB(_insert)(self, *raw++); + #elif defined _i_is_set + _c_MEMB(_emplace)(self, *raw++); + #else + _c_MEMB(_put)(self, raw->first, raw->second), ++raw; + #endif +} +#endif + +#ifndef _i_aux_alloc +STC_INLINE Self _c_MEMB(_init)(void) + { Self cx = {0}; return cx; } + +#ifndef _i_no_put +STC_INLINE Self _c_MEMB(_from_n)(const _m_raw* raw, isize n) + { Self cx = {0}; _c_MEMB(_put_n)(&cx, raw, n); return cx; } +#endif + +STC_INLINE Self _c_MEMB(_with_capacity)(const isize cap) + { Self cx = {0}; _c_MEMB(_reserve)(&cx, cap); return cx; } +#endif + +STC_API _m_iter _c_MEMB(_begin)(const Self* self); + +STC_INLINE _m_iter _c_MEMB(_end)(const Self* self) + { (void)self; return c_literal(_m_iter){0}; } + +STC_INLINE void _c_MEMB(_next)(_m_iter* it) { + while ((++it->ref, (++it->_mref)->dist == 0)) ; + if (it->ref == it->_end) it->ref = NULL; +} + +STC_INLINE _m_iter _c_MEMB(_advance)(_m_iter it, size_t n) { + while (n-- && it.ref) _c_MEMB(_next)(&it); + return it; +} + +STC_INLINE _m_iter +_c_MEMB(_find)(const Self* self, _m_keyraw rkey) { + _m_value* ref; + if (self->size != 0 && (ref = _c_MEMB(_bucket_lookup_)(self, &rkey).ref) != NULL) + return c_literal(_m_iter){ref, + &self->table[self->bucket_count], + &self->meta[ref - self->table]}; + return _c_MEMB(_end)(self); +} + +STC_INLINE const _m_value* +_c_MEMB(_get)(const Self* self, _m_keyraw rkey) { + return self->size ? _c_MEMB(_bucket_lookup_)(self, &rkey).ref : NULL; +} + +STC_INLINE _m_value* +_c_MEMB(_get_mut)(Self* self, _m_keyraw rkey) + { return (_m_value*)_c_MEMB(_get)(self, rkey); } + +STC_INLINE int +_c_MEMB(_erase)(Self* self, _m_keyraw rkey) { + _m_value* ref; + if (self->size != 0 && (ref = _c_MEMB(_bucket_lookup_)(self, &rkey).ref) != NULL) + { _c_MEMB(_erase_entry)(self, ref); return 1; } + return 0; +} + +STC_INLINE _m_iter +_c_MEMB(_erase_at)(Self* self, _m_iter it) { + _c_MEMB(_erase_entry)(self, it.ref); + if (it._mref->dist == 0) + _c_MEMB(_next)(&it); + return it; +} + +STC_INLINE bool +_c_MEMB(_eq)(const Self* self, const Self* other) { + if (_c_MEMB(_size)(self) != _c_MEMB(_size)(other)) return false; + for (_m_iter i = _c_MEMB(_begin)(self); i.ref; _c_MEMB(_next)(&i)) { + const _m_keyraw _raw = i_keytoraw(_i_keyref(i.ref)); + if (!_c_MEMB(_contains)(other, _raw)) return false; + } + return true; +} + +/* -------------------------- IMPLEMENTATION ------------------------- */ +#if defined i_implement + +STC_DEF _m_iter _c_MEMB(_begin)(const Self* self) { + _m_iter it = {self->table, self->table, self->meta}; + if (it.ref == NULL) return it; + it._end += self->bucket_count; + while (it._mref->dist == 0) + ++it.ref, ++it._mref; + if (it.ref == it._end) it.ref = NULL; + return it; +} + +STC_DEF float _c_MEMB(_max_load_factor)(const Self* self) { + (void)self; return (float)(i_max_load_factor); +} + +STC_DEF isize _c_MEMB(_capacity)(const Self* map) { + return (isize)((float)map->bucket_count * (i_max_load_factor)); +} + +static void _c_MEMB(_wipe_)(Self* self) { + if (self->size == 0) + return; + _m_value* d = self->table, *_end = &d[self->bucket_count]; + struct hmap_meta* m = self->meta; + for (; d != _end; ++d) + if ((m++)->dist) + _c_MEMB(_value_drop)(self, d); +} + +STC_DEF void _c_MEMB(_drop)(const Self* cself) { + Self* self = (Self*)cself; + if (self->bucket_count > 0) { + _c_MEMB(_wipe_)(self); + _i_free_n(self->meta, self->bucket_count + 1); + _i_free_n(self->table, self->bucket_count); + } +} + +STC_DEF void _c_MEMB(_clear)(Self* self) { + _c_MEMB(_wipe_)(self); + self->size = 0; + c_memset(self->meta, 0, c_sizeof(struct hmap_meta)*self->bucket_count); +} + +#ifdef _i_is_map + STC_DEF _m_result + _c_MEMB(_insert_or_assign)(Self* self, _m_key _key, _m_mapped _mapped) { + _m_result _res = _c_MEMB(_insert_entry_)(self, i_keytoraw((&_key))); + _m_mapped* _mp = _res.ref ? &_res.ref->second : &_mapped; + if (_res.inserted) + _res.ref->first = _key; + else + { i_keydrop((&_key)); i_valdrop(_mp); } + *_mp = _mapped; + return _res; + } + + #ifndef i_no_emplace + STC_DEF _m_result + _c_MEMB(_emplace_or_assign)(Self* self, _m_keyraw rkey, _m_rmapped rmapped) { + _m_result _res = _c_MEMB(_insert_entry_)(self, rkey); + if (_res.inserted) + _res.ref->first = i_keyfrom(rkey); + else { + if (_res.ref == NULL) return _res; + i_valdrop((&_res.ref->second)); + } + _res.ref->second = i_valfrom(rmapped); + return _res; + } + #endif // !i_no_emplace +#endif // _i_is_map + +STC_DEF _m_result +_c_MEMB(_bucket_lookup_)(const Self* self, const _m_keyraw* rkeyptr) { + const size_t _hash = i_hash(rkeyptr); + const size_t _idxmask = (size_t)self->bucket_count - 1; + _m_result _res = {.idx=_hash & _idxmask, .hashx=(uint8_t)((_hash >> 24) & _hashmask), .dist=1}; + + while (_res.dist <= self->meta[_res.idx].dist) { + if (self->meta[_res.idx].hashx == _res.hashx) { + const _m_keyraw _raw = i_keytoraw(_i_keyref(&self->table[_res.idx])); + if (i_eq((&_raw), rkeyptr)) { + _res.ref = &self->table[_res.idx]; + break; + } + } + _res.idx = (_res.idx + 1) & _idxmask; + ++_res.dist; + } + return _res; +} + +STC_DEF _m_result +_c_MEMB(_bucket_insert_)(const Self* self, const _m_keyraw* rkeyptr) { + _m_result res = _c_MEMB(_bucket_lookup_)(self, rkeyptr); + if (res.ref) // bucket exists + return res; + res.ref = &self->table[res.idx]; + res.inserted = true; + struct hmap_meta mnew = {.hashx=(uint16_t)(res.hashx & _hashmask), + .dist=(uint16_t)(res.dist & _distmask)}; + struct hmap_meta mcur = self->meta[res.idx]; + self->meta[res.idx] = mnew; + + if (mcur.dist != 0) { // collision, reorder buckets + size_t mask = (size_t)self->bucket_count - 1; + _m_value dcur = *res.ref; + for (;;) { + res.idx = (res.idx + 1) & mask; + ++mcur.dist; + if (self->meta[res.idx].dist == 0) + break; + if (self->meta[res.idx].dist < mcur.dist) { + c_swap(&mcur, &self->meta[res.idx]); + c_swap(&dcur, &self->table[res.idx]); + } + } + self->meta[res.idx] = mcur; + self->table[res.idx] = dcur; + } + return res; +} + + +#ifndef i_no_clone + STC_DEF Self + _c_MEMB(_clone)(Self map) { + if (map.bucket_count == 0) + return c_literal(Self){0}; + Self out = map, *self = &out; // _i_new_n may refer self via i_aux + const isize _mbytes = (map.bucket_count + 1)*c_sizeof *map.meta; + out.table = (_m_value *)i_malloc(map.bucket_count*c_sizeof *out.table); + out.meta = (struct hmap_meta *)i_malloc(_mbytes); + + if (out.table && out.meta) { + c_memcpy(out.meta, map.meta, _mbytes); + for (isize i = 0; i < map.bucket_count; ++i) + if (map.meta[i].dist) + out.table[i] = _c_MEMB(_value_clone)(self, map.table[i]); + return out; + } else { + if (out.meta) i_free(out.meta, _mbytes); + if (out.table) _i_free_n(out.table, map.bucket_count); + return c_literal(Self){0}; + } + } +#endif + +STC_DEF bool +_c_MEMB(_reserve)(Self* _self, const isize _newcap) { + isize _newbucks = (isize)((float)_newcap / (i_max_load_factor)) + 4; + _newbucks = c_next_pow2(_newbucks); + + if (_newcap < _self->size || _newbucks == _self->bucket_count) + return true; + Self map = *_self, *self = ↦ (void)self; + map.table = _i_new_n(_m_value, _newbucks); + map.meta = _i_new_zeros(struct hmap_meta, _newbucks + 1); + map.bucket_count = _newbucks; + + bool ok = map.table && map.meta; + if (ok) { // Rehash: + map.meta[_newbucks].dist = _distmask; // end-mark for iter + const _m_value* d = _self->table; + const struct hmap_meta* m = _self->meta; + + for (isize i = 0; i < _self->bucket_count; ++i, ++d) if (m[i].dist != 0) { + _m_keyraw r = i_keytoraw(_i_keyref(d)); + *_c_MEMB(_bucket_insert_)(&map, &r).ref = *d; // move element + } + c_swap(_self, &map); + } + _i_free_n(map.meta, map.bucket_count + (int)(map.meta != NULL)); + _i_free_n(map.table, map.bucket_count); + return ok; +} + +STC_DEF void +_c_MEMB(_erase_entry)(Self* self, _m_value* _val) { + _m_value* d = self->table; + struct hmap_meta *m = self->meta; + size_t i = (size_t)(_val - d), j = i; + size_t mask = (size_t)self->bucket_count - 1; + + _c_MEMB(_value_drop)(self, _val); + for (;;) { + j = (j + 1) & mask; + if (m[j].dist < 2) // 0 => empty, 1 => PSL 0 + break; + d[i] = d[j]; + m[i] = m[j]; + --m[i].dist; + i = j; + } + m[i].dist = 0; + --self->size; +} + +#endif // i_implement +#undef i_max_load_factor +#undef _i_is_set +#undef _i_is_map +#undef _i_is_hash +#undef _i_keyref +#undef _i_MAP_ONLY +#undef _i_SET_ONLY +#include "sys/finalize.h" diff --git a/src/finchlite/codegen/stc/stc/hset.h b/src/finchlite/codegen/stc/stc/hset.h new file mode 100644 index 00000000..d8a7d990 --- /dev/null +++ b/src/finchlite/codegen/stc/stc/hset.h @@ -0,0 +1,43 @@ +/* MIT License + * + * Copyright (c) 2025 Tyge Løvset + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +// Unordered set - implemented with the robin-hood hashing scheme. +/* +#define T iset, int +#include +#include + +int main(void) { + iset set = {0}; + iset_insert(&set, 5); + iset_insert(&set, 8); + + for (c_each(i, iset, set)) + printf("set %d\n", *i.ref); + iset_drop(&set); +} +*/ + +#define _i_prefix hset_ +#define _i_is_set +#include "hmap.h" diff --git a/src/finchlite/codegen/stc/stc/list.h b/src/finchlite/codegen/stc/stc/list.h new file mode 100644 index 00000000..bf85b3c5 --- /dev/null +++ b/src/finchlite/codegen/stc/stc/list.h @@ -0,0 +1,431 @@ +/* MIT License + * + * Copyright (c) 2025 Tyge Løvset + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* Circular Singly-linked Lists. + This implements a std::forward_list-like class in C. Because it is circular, + it also support both push_back() and push_front(), unlike std::forward_list: + + #include + #include + + #define T List, long, (c_use_cmp) // enable sorting, uses default *x < *y. + #include + + int main(void) + { + List list = {0}; + + for (int i = 0; i < 5000000; ++i) // five million + List_push_back(&list, crand64_uint() & (1<<24) - 1; + + int n = 0; + for (c_each(i, List, list)) + if (++n % 100000 == 0) printf("%8d: %10zu\n", n, *i.ref); + + // Sort them... + List_sort(&list); // sort.h quicksort + + n = 0; + puts("sorted"); + for (c_each(i, List, list)) + if (++n % 100000 == 0) printf("%8d: %10zu\n", n, *i.ref); + + List_drop(&list); + } +*/ +#include "priv/linkage.h" +#include "types.h" + +#ifndef STC_LIST_H_INCLUDED +#define STC_LIST_H_INCLUDED +#include "common.h" +#include + +#define _c_list_complete_types(SELF, dummy) \ + struct SELF##_node { \ + SELF##_value value; /* must be first! */ \ + struct SELF##_node *next; \ + } + +#define _clist_tonode(vp) c_safe_cast(_m_node*, _m_value*, vp) + +#define _c_list_insert_entry_after(ref, val) \ + _m_node *entry = _i_new_n(_m_node, 1); entry->value = val; \ + _c_list_insert_after_node(ref, entry) + +#define _c_list_insert_after_node(ref, entry) \ + if (ref) entry->next = ref->next, ref->next = entry; \ + else entry->next = entry + // +: set self->last based on node + +#endif // STC_LIST_H_INCLUDED + +#ifndef _i_prefix + #define _i_prefix list_ +#endif +#include "priv/template.h" + +#define _i_is_list +#ifndef i_declared + _c_DEFTYPES(_declare_list, Self, i_key, _i_aux_def); +#endif +_c_DEFTYPES(_c_list_complete_types, Self, dummy); +typedef i_keyraw _m_raw; + +STC_API void _c_MEMB(_drop)(const Self* cself); +STC_API _m_value* _c_MEMB(_push_back)(Self* self, _m_value value); +STC_API _m_value* _c_MEMB(_push_front)(Self* self, _m_value value); +STC_API _m_iter _c_MEMB(_insert_at)(Self* self, _m_iter it, _m_value value); +STC_API _m_iter _c_MEMB(_erase_at)(Self* self, _m_iter it); +STC_API _m_iter _c_MEMB(_erase_range)(Self* self, _m_iter it1, _m_iter it2); +#if defined _i_has_eq +STC_API _m_iter _c_MEMB(_find_in)(const Self* self, _m_iter it1, _m_iter it2, _m_raw val); +STC_API isize _c_MEMB(_remove)(Self* self, _m_raw val); +#endif +#if defined _i_has_cmp +STC_API bool _c_MEMB(_sort)(Self* self); +#endif +STC_API void _c_MEMB(_reverse)(Self* self); +STC_API _m_iter _c_MEMB(_splice)(Self* self, _m_iter it, Self* other); +STC_API Self _c_MEMB(_split_off)(Self* self, _m_iter it1, _m_iter it2); +STC_API _m_value* _c_MEMB(_push_back_node)(Self* self, _m_node* node); +STC_API _m_value* _c_MEMB(_insert_after_node)(Self* self, _m_node* ref, _m_node* node); +STC_API _m_node* _c_MEMB(_unlink_after_node)(Self* self, _m_node* ref); +STC_API void _c_MEMB(_erase_after_node)(Self* self, _m_node* ref); +STC_INLINE _m_node* _c_MEMB(_get_node)(_m_value* pval) { return _clist_tonode(pval); } +STC_INLINE _m_node* _c_MEMB(_unlink_front_node)(Self* self) + { return _c_MEMB(_unlink_after_node)(self, self->last); } +#ifndef i_no_clone +STC_API Self _c_MEMB(_clone)(Self cx); +STC_INLINE _m_value _c_MEMB(_value_clone)(const Self* self, _m_value val) + { (void)self; return i_keyclone(val); } + +STC_INLINE void +_c_MEMB(_copy)(Self *self, const Self* other) { + if (self->last == other->last) return; + _c_MEMB(_drop)(self); + *self = _c_MEMB(_clone)(*other); +} +#endif // !i_no_clone + +#ifndef i_no_emplace +STC_INLINE _m_value* _c_MEMB(_emplace_back)(Self* self, _m_raw raw) + { return _c_MEMB(_push_back)(self, i_keyfrom(raw)); } +STC_INLINE _m_value* _c_MEMB(_emplace_front)(Self* self, _m_raw raw) + { return _c_MEMB(_push_front)(self, i_keyfrom(raw)); } +STC_INLINE _m_iter _c_MEMB(_emplace_at)(Self* self, _m_iter it, _m_raw raw) + { return _c_MEMB(_insert_at)(self, it, i_keyfrom(raw)); } +STC_INLINE _m_value* _c_MEMB(_emplace)(Self* self, _m_raw raw) + { return _c_MEMB(_push_back)(self, i_keyfrom(raw)); } +#endif // !i_no_emplace + +#ifndef _i_no_put +STC_INLINE void _c_MEMB(_put_n)(Self* self, const _m_raw* raw, isize n) + { while (n--) _c_MEMB(_push_back)(self, i_keyfrom((*raw))), ++raw; } +#endif + +#ifndef _i_aux_alloc + STC_INLINE Self _c_MEMB(_init)(void) { return c_literal(Self){0}; } + #ifndef _i_no_put + STC_INLINE Self _c_MEMB(_from_n)(const _m_raw* raw, isize n) + { Self cx = {0}; _c_MEMB(_put_n)(&cx, raw, n); return cx; } + #endif +#endif + +STC_INLINE bool _c_MEMB(_reserve)(Self* self, isize n) { (void)(self + n); return true; } +STC_INLINE bool _c_MEMB(_is_empty)(const Self* self) { return self->last == NULL; } +STC_INLINE void _c_MEMB(_clear)(Self* self) { _c_MEMB(_drop)(self); } +STC_INLINE _m_value* _c_MEMB(_push)(Self* self, _m_value value) + { return _c_MEMB(_push_back)(self, value); } +STC_INLINE void _c_MEMB(_pop_front)(Self* self) + { c_assert(!_c_MEMB(_is_empty)(self)); _c_MEMB(_erase_after_node)(self, self->last); } +STC_INLINE const _m_value* _c_MEMB(_front)(const Self* self) { return &self->last->next->value; } +STC_INLINE _m_value* _c_MEMB(_front_mut)(Self* self) { return &self->last->next->value; } +STC_INLINE const _m_value* _c_MEMB(_back)(const Self* self) { return &self->last->value; } +STC_INLINE _m_value* _c_MEMB(_back_mut)(Self* self) { return &self->last->value; } +STC_INLINE _m_raw _c_MEMB(_value_toraw)(const _m_value* pval) { return i_keytoraw(pval); } +STC_INLINE void _c_MEMB(_value_drop)(const Self* self, _m_value* pval) { (void)self; i_keydrop(pval); } + +STC_INLINE Self _c_MEMB(_move)(Self *self) { + Self m = *self; + self->last = NULL; + return m; +} + +STC_INLINE void _c_MEMB(_take)(Self *self, Self unowned) { + _c_MEMB(_drop)(self); + *self = unowned; +} + +STC_INLINE isize +_c_MEMB(_count)(const Self* self) { + isize n = 1; const _m_node *node = self->last; + if (node == NULL) return 0; + while ((node = node->next) != self->last) ++n; + return n; +} + +STC_INLINE _m_iter +_c_MEMB(_begin)(const Self* self) { + _m_value* head = self->last ? &self->last->next->value : NULL; + return c_literal(_m_iter){head, &self->last, self->last}; +} + +STC_INLINE _m_iter +_c_MEMB(_end)(const Self* self) + { (void)self; return c_literal(_m_iter){0}; } + +STC_INLINE void +_c_MEMB(_next)(_m_iter* it) { + _m_node* node = it->prev = _clist_tonode(it->ref); + it->ref = (node == *it->_last ? NULL : &node->next->value); +} + +STC_INLINE _m_iter +_c_MEMB(_advance)(_m_iter it, size_t n) { + while (n-- && it.ref) _c_MEMB(_next)(&it); + return it; +} + +STC_INLINE _m_iter +_c_MEMB(_splice_range)(Self* self, _m_iter it, + Self* other, _m_iter it1, _m_iter it2) { + Self tmp = _c_MEMB(_split_off)(other, it1, it2); + return _c_MEMB(_splice)(self, it, &tmp); +} + +#if defined _i_has_eq +STC_INLINE _m_iter +_c_MEMB(_find)(const Self* self, _m_raw val) { + return _c_MEMB(_find_in)(self, _c_MEMB(_begin)(self), _c_MEMB(_end)(self), val); +} + +STC_INLINE bool _c_MEMB(_eq)(const Self* self, const Self* other) { + _m_iter i = _c_MEMB(_begin)(self), j = _c_MEMB(_begin)(other); + for (; i.ref && j.ref; _c_MEMB(_next)(&i), _c_MEMB(_next)(&j)) { + const _m_raw _rx = i_keytoraw(i.ref), _ry = i_keytoraw(j.ref); + if (!(i_eq((&_rx), (&_ry)))) return false; + } + return !(i.ref || j.ref); +} +#endif + +// -------------------------- IMPLEMENTATION ------------------------- +#if defined i_implement + +#ifndef i_no_clone +STC_DEF Self +_c_MEMB(_clone)(Self lst) { + Self out = lst, *self = &out; (void)self; // may be used by i_keyclone via i_aux + out.last = NULL; + for (c_each(it, Self, lst)) + _c_MEMB(_push_back)(&out, i_keyclone((*it.ref))); + return out; +} +#endif + +STC_DEF void +_c_MEMB(_drop)(const Self* cself) { + Self* self = (Self*)cself; + while (self->last) + _c_MEMB(_erase_after_node)(self, self->last); +} + +STC_DEF _m_value* +_c_MEMB(_push_back)(Self* self, _m_value value) { + _c_list_insert_entry_after(self->last, value); + self->last = entry; + return &entry->value; +} + +STC_DEF _m_value* +_c_MEMB(_push_front)(Self* self, _m_value value) { + _c_list_insert_entry_after(self->last, value); + if (self->last == NULL) + self->last = entry; + return &entry->value; +} + +STC_DEF _m_value* +_c_MEMB(_push_back_node)(Self* self, _m_node* node) { + _c_list_insert_after_node(self->last, node); + self->last = node; + return &node->value; +} + +STC_DEF _m_value* +_c_MEMB(_insert_after_node)(Self* self, _m_node* ref, _m_node* node) { + _c_list_insert_after_node(ref, node); + if (self->last == NULL) + self->last = node; + return &node->value; +} + +STC_DEF _m_iter +_c_MEMB(_insert_at)(Self* self, _m_iter it, _m_value value) { + _m_node* node = it.ref ? it.prev : self->last; + _c_list_insert_entry_after(node, value); + if (self->last == NULL || it.ref == NULL) { + it.prev = self->last ? self->last : entry; + self->last = entry; + } + it.ref = &entry->value; + return it; +} + +STC_DEF _m_iter +_c_MEMB(_erase_at)(Self* self, _m_iter it) { + _m_node *node = _clist_tonode(it.ref); + it.ref = (node == self->last) ? NULL : &node->next->value; + _c_MEMB(_erase_after_node)(self, it.prev); + return it; +} + +STC_DEF _m_iter +_c_MEMB(_erase_range)(Self* self, _m_iter it1, _m_iter it2) { + _m_node *end = it2.ref ? _clist_tonode(it2.ref) : self->last->next; + if (it1.ref != it2.ref) do { + _c_MEMB(_erase_after_node)(self, it1.prev); + if (self->last == NULL) break; + } while (it1.prev->next != end); + return it2; +} + +STC_DEF void +_c_MEMB(_erase_after_node)(Self* self, _m_node* ref) { + _m_node* node = _c_MEMB(_unlink_after_node)(self, ref); + i_keydrop((&node->value)); + _i_free_n(node, 1); +} + +STC_DEF _m_node* +_c_MEMB(_unlink_after_node)(Self* self, _m_node* ref) { + _m_node* node = ref->next, *next = node->next; + ref->next = next; + if (node == next) + self->last = NULL; + else if (node == self->last) + self->last = ref; + return node; +} + +STC_DEF void +_c_MEMB(_reverse)(Self* self) { + Self rev = *self; + rev.last = NULL; + while (self->last) { + _m_node* node = _c_MEMB(_unlink_after_node)(self, self->last); + _c_MEMB(_insert_after_node)(&rev, rev.last, node); + } + *self = rev; +} + +STC_DEF _m_iter +_c_MEMB(_splice)(Self* self, _m_iter it, Self* other) { + if (self->last == NULL) + self->last = other->last; + else if (other->last) { + _m_node *p = it.ref ? it.prev : self->last, *next = p->next; + it.prev = other->last; + p->next = it.prev->next; + it.prev->next = next; + if (it.ref == NULL) self->last = it.prev; + } + other->last = NULL; + return it; +} + +STC_DEF Self +_c_MEMB(_split_off)(Self* self, _m_iter it1, _m_iter it2) { + Self lst = *self; + lst.last = NULL; + if (it1.ref == it2.ref) + return lst; + _m_node *p1 = it1.prev, + *p2 = it2.ref ? it2.prev : self->last; + p1->next = p2->next; + p2->next = _clist_tonode(it1.ref); + if (self->last == p2) + self->last = (p1 == p2) ? NULL : p1; + lst.last = p2; + return lst; +} + +#if defined _i_has_eq +STC_DEF _m_iter +_c_MEMB(_find_in)(const Self* self, _m_iter it1, _m_iter it2, _m_raw val) { + (void)self; + for (c_each(it, Self, it1, it2)) { + _m_raw r = i_keytoraw(it.ref); + if (i_eq((&r), (&val))) + return it; + } + it2.ref = NULL; return it2; +} + +STC_DEF isize +_c_MEMB(_remove)(Self* self, _m_raw val) { + isize n = 0; + _m_node *prev = self->last, *node; + if (prev) do { + node = prev->next; + _m_raw r = i_keytoraw((&node->value)); + if (i_eq((&r), (&val))) { + _c_MEMB(_erase_after_node)(self, prev), ++n; + if (self->last == NULL) break; + } else + prev = node; + } while (node != self->last); + return n; +} +#endif + +#if defined _i_has_cmp +#include "priv/sort_prv.h" + +STC_DEF bool _c_MEMB(_sort)(Self* self) { + isize len = 0, cap = 0; + _m_value *arr = NULL, *p = NULL; + _m_node* keep; + for (c_each(i, Self, *self)) { + if (len == cap) { + isize cap_n = cap + cap/2 + 8; + if ((p = (_m_value *)_i_realloc_n(arr, cap, cap_n)) == NULL) + goto done; + arr = p, cap = cap_n; + } + arr[len++] = *i.ref; + } + keep = self->last; + self->last = (_m_node *)arr; + _c_MEMB(_sort_lowhigh)(self, 0, len - 1); + self->last = keep; + for (c_each(i, Self, *self)) + *i.ref = *p++; + done: _i_free_n(arr, cap); + return p != NULL; +} +#endif // _i_has_cmp +#endif // i_implement +#undef _i_is_list +#include "sys/finalize.h" diff --git a/src/finchlite/codegen/stc/stc/pqueue.h b/src/finchlite/codegen/stc/stc/pqueue.h new file mode 100644 index 00000000..dc952416 --- /dev/null +++ b/src/finchlite/codegen/stc/stc/pqueue.h @@ -0,0 +1,185 @@ +/* MIT License + * + * Copyright (c) 2025 Tyge Løvset + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "priv/linkage.h" +#include "types.h" + +#ifndef STC_PQUEUE_H_INCLUDED +#define STC_PQUEUE_H_INCLUDED +#include "common.h" +#include +#endif // STC_PQUEUIE_H_INCLUDED + +#ifndef _i_prefix + #define _i_prefix pqueue_ +#endif +#define _i_sorted +#include "priv/template.h" +#ifndef i_declared + _c_DEFTYPES(_declare_stack, Self, i_key, _i_aux_def); +#endif +typedef i_keyraw _m_raw; + +STC_API void _c_MEMB(_make_heap)(Self* self); +STC_API void _c_MEMB(_erase_at)(Self* self, isize idx); +STC_API _m_value* _c_MEMB(_push)(Self* self, _m_value value); + +#ifndef _i_no_put +STC_INLINE void _c_MEMB(_put_n)(Self* self, const _m_raw* raw, isize n) + { while (n--) _c_MEMB(_push)(self, i_keyfrom((*raw))), ++raw; } +#endif + +STC_INLINE bool _c_MEMB(_reserve)(Self* self, const isize cap) { + if (cap != self->size && cap <= self->capacity) return true; + _m_value *d = (_m_value *)_i_realloc_n(self->data, self->capacity, cap); + return d ? (self->data = d, self->capacity = cap, true) : false; +} + +STC_INLINE void _c_MEMB(_shrink_to_fit)(Self* self) + { _c_MEMB(_reserve)(self, self->size); } + +#ifndef _i_aux_alloc +STC_INLINE Self _c_MEMB(_init)(void) + { return c_literal(Self){0}; } + +#ifndef _i_no_put +STC_INLINE Self _c_MEMB(_from_n)(const _m_raw* raw, isize n) + { Self cx = {0}; _c_MEMB(_put_n)(&cx, raw, n); return cx; } +#endif + +STC_INLINE Self _c_MEMB(_with_capacity)(const isize cap) + { Self cx = {0}; _c_MEMB(_reserve)(&cx, cap); return cx; } +#endif + +STC_INLINE void _c_MEMB(_clear)(Self* self) { + isize i = self->size; self->size = 0; + while (i--) { i_keydrop((self->data + i)); } +} + +STC_INLINE void _c_MEMB(_drop)(const Self* cself) { + Self* self = (Self*)cself; + _c_MEMB(_clear)(self); + _i_free_n(self->data, self->capacity); +} + +STC_INLINE Self _c_MEMB(_move)(Self *self) { + Self m = *self; + self->size = self->capacity = 0; + self->data = NULL; + return m; +} + +STC_INLINE void _c_MEMB(_take)(Self *self, Self unowned) { + _c_MEMB(_drop)(self); + *self = unowned; +} + +STC_INLINE isize _c_MEMB(_size)(const Self* q) + { return q->size; } + +STC_INLINE bool _c_MEMB(_is_empty)(const Self* q) + { return !q->size; } + +STC_INLINE isize _c_MEMB(_capacity)(const Self* q) + { return q->capacity; } + +STC_INLINE const _m_value* _c_MEMB(_top)(const Self* self) + { return &self->data[0]; } + +STC_INLINE void _c_MEMB(_pop)(Self* self) + { c_assert(!_c_MEMB(_is_empty)(self)); _c_MEMB(_erase_at)(self, 0); } + +STC_INLINE _m_value _c_MEMB(_pull)(Self* self) + { _m_value v = self->data[0]; _c_MEMB(_erase_at)(self, 0); return v; } + +#ifndef i_no_clone +STC_API Self _c_MEMB(_clone)(Self q); + +STC_INLINE void _c_MEMB(_copy)(Self *self, const Self* other) { + if (self == other) return; + _c_MEMB(_drop)(self); + *self = _c_MEMB(_clone)(*other); +} +STC_INLINE _m_value _c_MEMB(_value_clone)(const Self* self, _m_value val) + { (void)self; return i_keyclone(val); } +#endif // !i_no_clone + +#ifndef i_no_emplace +STC_INLINE void _c_MEMB(_emplace)(Self* self, _m_raw raw) + { _c_MEMB(_push)(self, i_keyfrom(raw)); } +#endif // !i_no_emplace + +/* -------------------------- IMPLEMENTATION ------------------------- */ +#if defined i_implement + +STC_DEF void +_c_MEMB(_sift_down_)(Self* self, const isize idx, const isize n) { + _m_value t, *arr = self->data - 1; + for (isize r = idx, c = idx*2; c <= n; c *= 2) { + c += i_less((&arr[c]), (&arr[c + (c < n)])); + if (!(i_less((&arr[r]), (&arr[c])))) return; + t = arr[r], arr[r] = arr[c], arr[r = c] = t; + } +} + +STC_DEF void +_c_MEMB(_make_heap)(Self* self) { + isize n = self->size; + for (isize k = n/2; k != 0; --k) + _c_MEMB(_sift_down_)(self, k, n); +} + +#ifndef i_no_clone +STC_DEF Self _c_MEMB(_clone)(Self q) { + Self out = q, *self = &out; (void)self; + out.capacity = out.size = 0; out.data = NULL; + _c_MEMB(_reserve)(&out, q.size); + out.size = q.size; + for (c_range(i, q.size)) + out.data[i] = i_keyclone(q.data[i]); + return out; +} +#endif + +STC_DEF void +_c_MEMB(_erase_at)(Self* self, const isize idx) { + i_keydrop((self->data + idx)); + const isize n = --self->size; + self->data[idx] = self->data[n]; + _c_MEMB(_sift_down_)(self, idx + 1, n); +} + +STC_DEF _m_value* +_c_MEMB(_push)(Self* self, _m_value value) { + if (self->size == self->capacity) + _c_MEMB(_reserve)(self, self->size*3/2 + 4); + _m_value *arr = self->data - 1; /* base 1 */ + isize c = ++self->size; + for (; c > 1 && (i_less((&arr[c/2]), (&value))); c /= 2) + arr[c] = arr[c/2]; + arr[c] = value; + return arr + c; +} +#endif + +#undef _i_sorted +#include "sys/finalize.h" diff --git a/src/finchlite/codegen/stc/stc/priv/cstr_prv.h b/src/finchlite/codegen/stc/stc/priv/cstr_prv.h new file mode 100644 index 00000000..40f58c94 --- /dev/null +++ b/src/finchlite/codegen/stc/stc/priv/cstr_prv.h @@ -0,0 +1,420 @@ +/* MIT License + * + * Copyright (c) 2025 Tyge Løvset + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +// IWYU pragma: private, include "stc/cstr.h" +#ifndef STC_CSTR_PRV_H_INCLUDED +#define STC_CSTR_PRV_H_INCLUDED + +#include /* FILE*, vsnprintf */ +#include /* malloc */ +#include /* size_t */ +#include /* cstr_vfmt() */ +/**************************** PRIVATE API **********************************/ + +#if defined __GNUC__ && !defined __clang__ + // linkage.h already does diagnostic push + // Warns wrongfully on -O3 on cstr_assign(&str, "literal longer than 23 ..."); + #pragma GCC diagnostic ignored "-Warray-bounds" +#endif + +enum { cstr_s_cap = sizeof(cstr_buf) - 2 }; +#define cstr_s_size(s) ((isize)(s)->sml.size) +#define cstr_s_set_size(s, len) ((s)->sml.data[(s)->sml.size = (uint8_t)(len)] = 0) +#define cstr_s_data(s) (s)->sml.data + +#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + #define byte_rotl_(x, b) ((x) << (b)*8 | (x) >> (sizeof(x) - (b))*8) + #define cstr_l_cap(s) (isize)(~byte_rotl_((s)->lon.ncap, sizeof((s)->lon.ncap) - 1)) + #define cstr_l_set_cap(s, cap) ((s)->lon.ncap = ~byte_rotl_((uintptr_t)(cap), 1)) +#else + #define cstr_l_cap(s) (isize)(~(s)->lon.ncap) + #define cstr_l_set_cap(s, cap) ((s)->lon.ncap = ~(uintptr_t)(cap)) +#endif +#define cstr_l_size(s) (isize)((s)->lon.size) +#define cstr_l_set_size(s, len) ((s)->lon.data[(s)->lon.size = (uintptr_t)(len)] = 0) +#define cstr_l_data(s) (s)->lon.data +#define cstr_l_drop(s) c_free((s)->lon.data, cstr_l_cap(s) + 1) + +#define cstr_is_long(s) ((s)->sml.size >= 128) +extern char* _cstr_init(cstr* self, isize len, isize cap); +extern char* _cstr_internal_move(cstr* self, isize pos1, isize pos2); + +/**************************** PUBLIC API **********************************/ + +#define cstr_init() (c_literal(cstr){0}) +#define cstr_lit(literal) cstr_from_n(literal, c_litstrlen(literal)) + +extern cstr cstr_from_replace(csview sv, csview search, csview repl, int32_t count); +extern cstr cstr_from_fmt(const char* fmt, ...) c_GNUATTR(format(printf, 1, 2)); + +extern void cstr_drop(const cstr* self); +extern cstr* cstr_take(cstr* self, const cstr s); +extern char* cstr_reserve(cstr* self, isize cap); +extern void cstr_shrink_to_fit(cstr* self); +extern char* cstr_resize(cstr* self, isize size, char value); +extern isize cstr_find_at(const cstr* self, isize pos, const char* search); +extern isize cstr_find_sv(const cstr* self, csview search); +extern char* cstr_assign_n(cstr* self, const char* str, isize len); +extern char* cstr_append_n(cstr* self, const char* str, isize len); +extern isize cstr_append_fmt(cstr* self, const char* fmt, ...) c_GNUATTR(format(printf, 2, 3)); +extern char* cstr_append_uninit(cstr *self, isize len); + +extern bool cstr_getdelim(cstr *self, int delim, FILE *fp); +extern void cstr_erase(cstr* self, isize pos, isize len); +extern isize cstr_printf(cstr* self, const char* fmt, ...) c_GNUATTR(format(printf, 2, 3)); +extern isize cstr_vfmt(cstr* self, isize start, const char* fmt, va_list args); +extern size_t cstr_hash(const cstr *self); +extern bool cstr_u8_valid(const cstr* self); +extern void cstr_u8_erase(cstr* self, isize u8pos, isize u8len); + +STC_INLINE cstr_buf cstr_getbuf(cstr* s) { + return cstr_is_long(s) ? c_literal(cstr_buf){s->lon.data, cstr_l_size(s), cstr_l_cap(s)} + : c_literal(cstr_buf){s->sml.data, cstr_s_size(s), cstr_s_cap}; +} +STC_INLINE zsview cstr_zv(const cstr* s) { + return cstr_is_long(s) ? c_literal(zsview){s->lon.data, cstr_l_size(s)} + : c_literal(zsview){s->sml.data, cstr_s_size(s)}; +} +STC_INLINE csview cstr_sv(const cstr* s) { + return cstr_is_long(s) ? c_literal(csview){s->lon.data, cstr_l_size(s)} + : c_literal(csview){s->sml.data, cstr_s_size(s)}; +} + +STC_INLINE cstr cstr_from_n(const char* str, const isize len) { + cstr s; + c_memcpy(_cstr_init(&s, len, len), str, len); + return s; +} + +STC_INLINE cstr cstr_from(const char* str) + { return cstr_from_n(str, c_strlen(str)); } + +STC_INLINE cstr cstr_from_sv(csview sv) + { return cstr_from_n(sv.buf, sv.size); } + +STC_INLINE cstr cstr_from_zv(zsview zv) + { return cstr_from_n(zv.str, zv.size); } + +STC_INLINE cstr cstr_with_size(const isize size, const char value) { + cstr s; + c_memset(_cstr_init(&s, size, size), value, size); + return s; +} + +STC_INLINE cstr cstr_with_capacity(const isize cap) { + cstr s; + _cstr_init(&s, 0, cap); + return s; +} + +STC_INLINE cstr cstr_move(cstr* self) { + cstr tmp = *self; + *self = cstr_init(); + return tmp; +} + +STC_INLINE cstr cstr_clone(cstr s) { + csview sv = cstr_sv(&s); + return cstr_from_n(sv.buf, sv.size); +} + +#define SSO_CALL(s, call) (cstr_is_long(s) ? cstr_l_##call : cstr_s_##call) + +STC_INLINE void _cstr_set_size(cstr* self, isize len) + { SSO_CALL(self, set_size(self, len)); } + +STC_INLINE void cstr_clear(cstr* self) + { _cstr_set_size(self, 0); } + +STC_INLINE char* cstr_data(cstr* self) + { return SSO_CALL(self, data(self)); } + +STC_INLINE const char* cstr_str(const cstr* self) + { return SSO_CALL(self, data(self)); } + +STC_INLINE const char* cstr_toraw(const cstr* self) + { return SSO_CALL(self, data(self)); } + +STC_INLINE isize cstr_size(const cstr* self) + { return SSO_CALL(self, size(self)); } + +STC_INLINE bool cstr_is_empty(const cstr* self) + { return cstr_size(self) == 0; } + +STC_INLINE isize cstr_capacity(const cstr* self) + { return cstr_is_long(self) ? cstr_l_cap(self) : cstr_s_cap; } + +STC_INLINE isize cstr_to_index(const cstr* self, cstr_iter it) + { return it.ref - cstr_str(self); } + +STC_INLINE cstr cstr_from_s(cstr s, isize pos, isize len) + { return cstr_from_n(cstr_str(&s) + pos, len); } + +STC_INLINE csview cstr_subview(const cstr* self, isize pos, isize len) { + csview sv = cstr_sv(self); + c_assert(((size_t)pos <= (size_t)sv.size) & (len >= 0)); + if (pos + len > sv.size) len = sv.size - pos; + return c_literal(csview){sv.buf + pos, len}; +} + +STC_INLINE zsview cstr_tail(const cstr* self, isize len) { + c_assert(len >= 0); + csview sv = cstr_sv(self); + if (len > sv.size) len = sv.size; + return c_literal(zsview){&sv.buf[sv.size - len], len}; +} + +// BEGIN utf8 functions ===== + +STC_INLINE cstr cstr_u8_from(const char* str, isize u8pos, isize u8len) + { str = utf8_at(str, u8pos); return cstr_from_n(str, utf8_to_index(str, u8len)); } + +STC_INLINE isize cstr_u8_size(const cstr* self) + { return utf8_count(cstr_str(self)); } + +STC_INLINE isize cstr_u8_to_index(const cstr* self, isize u8pos) + { return utf8_to_index(cstr_str(self), u8pos); } + +STC_INLINE zsview cstr_u8_tail(const cstr* self, isize u8len) { + csview sv = cstr_sv(self); + const char* p = &sv.buf[sv.size]; + while (u8len && p != sv.buf) + u8len -= (*--p & 0xC0) != 0x80; + return c_literal(zsview){p, sv.size - (p - sv.buf)}; +} + +STC_INLINE csview cstr_u8_subview(const cstr* self, isize u8pos, isize u8len) + { return utf8_subview(cstr_str(self), u8pos, u8len); } + +STC_INLINE cstr_iter cstr_u8_at(const cstr* self, isize u8pos) { + csview sv; + sv.buf = utf8_at(cstr_str(self), u8pos); + sv.size = utf8_chr_size(sv.buf); + c_assert(sv.size); + return c_literal(cstr_iter){.chr = sv}; +} + +// utf8 iterator + +STC_INLINE cstr_iter cstr_begin(const cstr* self) { + csview sv = cstr_sv(self); + cstr_iter it = {.chr = {sv.buf, utf8_chr_size(sv.buf)}}; + return it; +} +STC_INLINE cstr_iter cstr_end(const cstr* self) { + (void)self; cstr_iter it = {0}; return it; +} +STC_INLINE void cstr_next(cstr_iter* it) { + it->ref += it->chr.size; + it->chr.size = utf8_chr_size(it->ref); + if (*it->ref == '\0') it->ref = NULL; +} + +STC_INLINE cstr_iter cstr_advance(cstr_iter it, isize u8pos) { + it.ref = utf8_offset(it.ref, u8pos); + it.chr.size = utf8_chr_size(it.ref); + if (*it.ref == '\0') it.ref = NULL; + return it; +} + +// utf8 case conversion: requires `#define i_import` before including cstr.h in one TU. +extern cstr cstr_tocase_sv(csview sv, int k); + +STC_INLINE cstr cstr_casefold_sv(csview sv) + { return cstr_tocase_sv(sv, 0); } + +STC_INLINE cstr cstr_tolower_sv(csview sv) + { return cstr_tocase_sv(sv, 1); } + +STC_INLINE cstr cstr_toupper_sv(csview sv) + { return cstr_tocase_sv(sv, 2); } + +STC_INLINE cstr cstr_tolower(const char* str) + { return cstr_tolower_sv(c_sv(str, c_strlen(str))); } + +STC_INLINE cstr cstr_toupper(const char* str) + { return cstr_toupper_sv(c_sv(str, c_strlen(str))); } + +STC_INLINE void cstr_lowercase(cstr* self) + { cstr_take(self, cstr_tolower_sv(cstr_sv(self))); } + +STC_INLINE void cstr_uppercase(cstr* self) + { cstr_take(self, cstr_toupper_sv(cstr_sv(self))); } + +STC_INLINE bool cstr_istarts_with(const cstr* self, const char* sub) { + csview sv = cstr_sv(self); + isize len = c_strlen(sub); + return len <= sv.size && !utf8_icompare((sv.size = len, sv), c_sv(sub, len)); +} + +STC_INLINE bool cstr_iends_with(const cstr* self, const char* sub) { + csview sv = cstr_sv(self); + isize len = c_strlen(sub); + return len <= sv.size && !utf8_icmp(sv.buf + sv.size - len, sub); +} + +STC_INLINE int cstr_icmp(const cstr* s1, const cstr* s2) + { return utf8_icmp(cstr_str(s1), cstr_str(s2)); } + +STC_INLINE bool cstr_ieq(const cstr* s1, const cstr* s2) { + csview x = cstr_sv(s1), y = cstr_sv(s2); + return x.size == y.size && !utf8_icompare(x, y); +} + +STC_INLINE bool cstr_iequals(const cstr* self, const char* str) + { return !utf8_icmp(cstr_str(self), str); } + +// END utf8 ===== + +STC_INLINE int cstr_cmp(const cstr* s1, const cstr* s2) + { return strcmp(cstr_str(s1), cstr_str(s2)); } + +STC_INLINE bool cstr_eq(const cstr* s1, const cstr* s2) { + csview x = cstr_sv(s1), y = cstr_sv(s2); + return x.size == y.size && !c_memcmp(x.buf, y.buf, x.size); +} + +STC_INLINE bool cstr_equals(const cstr* self, const char* str) + { return !strcmp(cstr_str(self), str); } + +STC_INLINE bool cstr_equals_sv(const cstr* self, csview sv) + { return sv.size == cstr_size(self) && !c_memcmp(cstr_str(self), sv.buf, sv.size); } + +STC_INLINE isize cstr_find(const cstr* self, const char* search) { + const char *str = cstr_str(self), *res = strstr((char*)str, search); + return res ? (res - str) : c_NPOS; +} + +STC_INLINE bool cstr_contains(const cstr* self, const char* search) + { return strstr((char*)cstr_str(self), search) != NULL; } + +STC_INLINE bool cstr_contains_sv(const cstr* self, csview search) + { return cstr_find_sv(self, search) != c_NPOS; } + + +STC_INLINE bool cstr_starts_with_sv(const cstr* self, csview sub) { + if (sub.size > cstr_size(self)) return false; + return !c_memcmp(cstr_str(self), sub.buf, sub.size); +} + +STC_INLINE bool cstr_starts_with(const cstr* self, const char* sub) { + const char* str = cstr_str(self); + while (*sub && *str == *sub) ++str, ++sub; + return !*sub; +} + +STC_INLINE bool cstr_ends_with_sv(const cstr* self, csview sub) { + csview sv = cstr_sv(self); + if (sub.size > sv.size) return false; + return !c_memcmp(sv.buf + sv.size - sub.size, sub.buf, sub.size); +} + +STC_INLINE bool cstr_ends_with(const cstr* self, const char* sub) + { return cstr_ends_with_sv(self, c_sv(sub, c_strlen(sub))); } + +STC_INLINE char* cstr_assign(cstr* self, const char* str) + { return cstr_assign_n(self, str, c_strlen(str)); } + +STC_INLINE char* cstr_assign_sv(cstr* self, csview sv) + { return cstr_assign_n(self, sv.buf, sv.size); } + +STC_INLINE char* cstr_copy(cstr* self, cstr s) { + csview sv = cstr_sv(&s); + return cstr_assign_n(self, sv.buf, sv.size); +} + + +STC_INLINE char* cstr_push(cstr* self, const char* chr) + { return cstr_append_n(self, chr, utf8_chr_size(chr)); } + +STC_INLINE void cstr_pop(cstr* self) { + csview sv = cstr_sv(self); + const char* s = sv.buf + sv.size; + while ((*--s & 0xC0) == 0x80) ; + _cstr_set_size(self, (s - sv.buf)); +} + +STC_INLINE char* cstr_append(cstr* self, const char* str) + { return cstr_append_n(self, str, c_strlen(str)); } + +STC_INLINE char* cstr_append_sv(cstr* self, csview sv) + { return cstr_append_n(self, sv.buf, sv.size); } + +STC_INLINE char* cstr_append_s(cstr* self, cstr s) + { return cstr_append_sv(self, cstr_sv(&s)); } + +#define cstr_join(self, sep, vec) do { \ + struct _vec_s { cstr* data; ptrdiff_t size; } \ + *_vec = (struct _vec_s*)&(vec); \ + (void)sizeof((vec).data == _vec->data && &(vec).size == &_vec->size); \ + cstr_join_sn(self, sep, _vec->data, _vec->size); \ +} while (0); + +#define cstr_join_items(self, sep, ...) \ + cstr_join_n(self, sep, c_make_array(const char*, __VA_ARGS__), c_sizeof((const char*[])__VA_ARGS__)/c_sizeof(char*)) + +STC_INLINE void cstr_join_n(cstr* self, const char* sep, const char* arr[], isize n) { + const char* _sep = cstr_is_empty(self) ? "" : sep; + while (n--) { cstr_append(self, _sep); cstr_append(self, *arr++); _sep = sep; } +} +STC_INLINE void cstr_join_sn(cstr* self, const char* sep, const cstr arr[], isize n) { + const char* _sep = cstr_is_empty(self) ? "" : sep; + while (n--) { cstr_append(self, _sep); cstr_append_s(self, *arr++); _sep = sep; } +} + + +STC_INLINE void cstr_replace_sv(cstr* self, csview search, csview repl, int32_t count) + { cstr_take(self, cstr_from_replace(cstr_sv(self), search, repl, count)); } + +STC_INLINE void cstr_replace_nfirst(cstr* self, const char* search, const char* repl, int32_t count) + { cstr_replace_sv(self, c_sv(search, c_strlen(search)), c_sv(repl, c_strlen(repl)), count); } + +STC_INLINE void cstr_replace(cstr* self, const char* search, const char* repl) + { cstr_replace_nfirst(self, search, repl, INT32_MAX); } + + +STC_INLINE void cstr_replace_at_sv(cstr* self, isize pos, isize len, const csview repl) { + char* d = _cstr_internal_move(self, pos + len, pos + repl.size); + c_memcpy(d + pos, repl.buf, repl.size); +} +STC_INLINE void cstr_replace_at(cstr* self, isize pos, isize len, const char* repl) + { cstr_replace_at_sv(self, pos, len, c_sv(repl, c_strlen(repl))); } + +STC_INLINE void cstr_u8_replace(cstr* self, isize u8pos, isize u8len, const char* repl) { + const char* s = cstr_str(self); csview span = utf8_subview(s, u8pos, u8len); + cstr_replace_at(self, span.buf - s, span.size, repl); +} + + +STC_INLINE void cstr_insert_sv(cstr* self, isize pos, csview sv) + { cstr_replace_at_sv(self, pos, 0, sv); } + +STC_INLINE void cstr_insert(cstr* self, isize pos, const char* str) + { cstr_replace_at_sv(self, pos, 0, c_sv(str, c_strlen(str))); } + +STC_INLINE void cstr_u8_insert(cstr* self, isize u8pos, const char* str) + { cstr_insert(self, utf8_to_index(cstr_str(self), u8pos), str); } + +STC_INLINE bool cstr_getline(cstr *self, FILE *fp) + { return cstr_getdelim(self, '\n', fp); } + +#endif // STC_CSTR_PRV_H_INCLUDED diff --git a/src/finchlite/codegen/stc/stc/priv/linkage.h b/src/finchlite/codegen/stc/stc/priv/linkage.h new file mode 100644 index 00000000..ed18c729 --- /dev/null +++ b/src/finchlite/codegen/stc/stc/priv/linkage.h @@ -0,0 +1,77 @@ +/* MIT License + * + * Copyright (c) 2025 Tyge Løvset + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#undef STC_API +#undef STC_DEF + +#if !defined i_static && !defined STC_STATIC && (defined i_header || defined STC_HEADER || \ + defined i_implement || defined STC_IMPLEMENT) + #define STC_API extern + #define STC_DEF +#else + #define i_implement + #if defined __GNUC__ || defined __clang__ || defined __INTEL_LLVM_COMPILER + #define STC_API static __attribute__((unused)) + #else + #define STC_API static inline + #endif + #define STC_DEF static +#endif +#if defined STC_IMPLEMENT || defined i_import + #define i_implement +#endif + +#if defined i_aux && defined i_allocator + #define _i_aux_alloc +#endif +#ifndef i_allocator + #define i_allocator c +#endif +#ifndef i_free + #define i_malloc c_JOIN(i_allocator, _malloc) + #define i_calloc c_JOIN(i_allocator, _calloc) + #define i_realloc c_JOIN(i_allocator, _realloc) + #define i_free c_JOIN(i_allocator, _free) +#endif + +#if defined __clang__ && !defined __cplusplus + #pragma clang diagnostic push + #pragma clang diagnostic warning "-Wall" + #pragma clang diagnostic warning "-Wextra" + #pragma clang diagnostic warning "-Wpedantic" + #pragma clang diagnostic warning "-Wconversion" + #pragma clang diagnostic warning "-Wwrite-strings" + // ignored + #pragma clang diagnostic ignored "-Wmissing-field-initializers" +#elif defined __GNUC__ && !defined __cplusplus + #pragma GCC diagnostic push + #pragma GCC diagnostic warning "-Wall" + #pragma GCC diagnostic warning "-Wextra" + #pragma GCC diagnostic warning "-Wpedantic" + #pragma GCC diagnostic warning "-Wconversion" + #pragma GCC diagnostic warning "-Wwrite-strings" + // ignored + #pragma GCC diagnostic ignored "-Wclobbered" + #pragma GCC diagnostic ignored "-Wimplicit-fallthrough=3" + #pragma GCC diagnostic ignored "-Wstringop-overflow=" + #pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#endif diff --git a/src/finchlite/codegen/stc/stc/priv/linkage2.h b/src/finchlite/codegen/stc/stc/priv/linkage2.h new file mode 100644 index 00000000..d99dd239 --- /dev/null +++ b/src/finchlite/codegen/stc/stc/priv/linkage2.h @@ -0,0 +1,42 @@ +/* MIT License + * + * Copyright (c) 2025 Tyge Løvset + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#undef i_aux +#undef _i_aux_alloc + +#undef i_allocator +#undef i_malloc +#undef i_calloc +#undef i_realloc +#undef i_free + +#undef i_static +#undef i_header +#undef i_implement +#undef i_import + +#if defined __clang__ && !defined __cplusplus + #pragma clang diagnostic pop +#elif defined __GNUC__ && !defined __cplusplus + #pragma GCC diagnostic pop +#endif diff --git a/src/finchlite/codegen/stc/stc/priv/queue_prv.h b/src/finchlite/codegen/stc/stc/priv/queue_prv.h new file mode 100644 index 00000000..b1d46c60 --- /dev/null +++ b/src/finchlite/codegen/stc/stc/priv/queue_prv.h @@ -0,0 +1,291 @@ +/* MIT License + * + * Copyright (c) 2025 Tyge Løvset + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +// IWYU pragma: private +#ifndef i_declared +_c_DEFTYPES(_declare_queue, Self, i_key, _i_aux_def); +#endif +typedef i_keyraw _m_raw; + +STC_API bool _c_MEMB(_reserve)(Self* self, const isize cap); +STC_API void _c_MEMB(_clear)(Self* self); +STC_API void _c_MEMB(_drop)(const Self* cself); +STC_API _m_value* _c_MEMB(_push)(Self* self, _m_value value); // push_back +STC_API void _c_MEMB(_shrink_to_fit)(Self *self); +STC_API _m_iter _c_MEMB(_advance)(_m_iter it, isize n); + +#define _cbuf_toidx(self, pos) (((pos) - (self)->start) & (self)->capmask) +#define _cbuf_topos(self, idx) (((self)->start + (idx)) & (self)->capmask) + +#ifndef _i_no_put +STC_INLINE void _c_MEMB(_put_n)(Self* self, const _m_raw* raw, isize n) + { while (n--) _c_MEMB(_push)(self, i_keyfrom((*raw))), ++raw; } +#endif + +STC_INLINE void _c_MEMB(_value_drop)(const Self* self, _m_value* val) + { (void)self; i_keydrop(val); } + +#ifndef _i_aux_alloc + STC_INLINE Self _c_MEMB(_init)(void) + { Self out = {0}; return out; } + + STC_INLINE Self _c_MEMB(_with_capacity)(isize cap) { + cap = c_next_pow2(cap + 1); + Self out = {_i_new_n(_m_value, cap), 0, 0, cap - 1}; + return out; + } + + STC_INLINE Self _c_MEMB(_with_size_uninit)(isize size) + { Self out = _c_MEMB(_with_capacity)(size); out.end = size; return out; } + + STC_INLINE Self _c_MEMB(_with_size)(isize size, _m_raw default_raw) { + Self out = _c_MEMB(_with_capacity)(size); + while (out.end < size) out.cbuf[out.end++] = i_keyfrom(default_raw); + return out; + } + + #ifndef _i_no_put + STC_INLINE Self _c_MEMB(_from_n)(const _m_raw* raw, isize n) { + Self out = _c_MEMB(_with_capacity)(n); + _c_MEMB(_put_n)(&out, raw, n); return out; + } + #endif +#endif + +#ifndef i_no_emplace +STC_INLINE _m_value* _c_MEMB(_emplace)(Self* self, _m_raw raw) + { return _c_MEMB(_push)(self, i_keyfrom(raw)); } +#endif + +#if defined _i_has_eq +STC_API bool _c_MEMB(_eq)(const Self* self, const Self* other); +#endif + +#ifndef i_no_clone +STC_API Self _c_MEMB(_clone)(Self q); + +STC_INLINE _m_value _c_MEMB(_value_clone)(const Self* self, _m_value val) + { (void)self; return i_keyclone(val); } + +STC_INLINE void _c_MEMB(_copy)(Self* self, const Self* other) { + if (self == other) return; + _c_MEMB(_drop)(self); + *self = _c_MEMB(_clone)(*other); +} +#endif // !i_no_clone + +STC_INLINE isize _c_MEMB(_size)(const Self* self) + { return _cbuf_toidx(self, self->end); } + +STC_INLINE isize _c_MEMB(_capacity)(const Self* self) + { return self->capmask; } + +STC_INLINE bool _c_MEMB(_is_empty)(const Self* self) + { return self->start == self->end; } + +STC_INLINE _m_raw _c_MEMB(_value_toraw)(const _m_value* pval) + { return i_keytoraw(pval); } + +STC_INLINE const _m_value* _c_MEMB(_front)(const Self* self) + { return self->cbuf + self->start; } + +STC_INLINE _m_value* _c_MEMB(_front_mut)(Self* self) + { return self->cbuf + self->start; } + +STC_INLINE const _m_value* _c_MEMB(_back)(const Self* self) + { return self->cbuf + ((self->end - 1) & self->capmask); } + +STC_INLINE _m_value* _c_MEMB(_back_mut)(Self* self) + { return (_m_value*)_c_MEMB(_back)(self); } + +STC_INLINE Self _c_MEMB(_move)(Self *self) { + Self m = *self; + self->capmask = self->start = self->end = 0; + self->cbuf = NULL; + return m; +} + +STC_INLINE void _c_MEMB(_take)(Self *self, Self unowned) + { _c_MEMB(_drop)(self); *self = unowned; } + +STC_INLINE void _c_MEMB(_pop)(Self* self) { // pop_front + c_assert(!_c_MEMB(_is_empty)(self)); + i_keydrop((self->cbuf + self->start)); + self->start = (self->start + 1) & self->capmask; +} + +STC_INLINE _m_value _c_MEMB(_pull)(Self* self) { // move front out of queue + c_assert(!_c_MEMB(_is_empty)(self)); + isize s = self->start; + self->start = (s + 1) & self->capmask; + return self->cbuf[s]; +} + +STC_INLINE _m_iter _c_MEMB(_begin)(const Self* self) { + return c_literal(_m_iter){ + .ref=_c_MEMB(_is_empty)(self) ? NULL : self->cbuf + self->start, + .pos=self->start, ._s=self}; +} + +STC_INLINE _m_iter _c_MEMB(_rbegin)(const Self* self) { + isize pos = (self->end - 1) & self->capmask; + return c_literal(_m_iter){ + .ref=_c_MEMB(_is_empty)(self) ? NULL : self->cbuf + pos, + .pos=pos, ._s=self}; +} + +STC_INLINE _m_iter _c_MEMB(_end)(const Self* self) + { (void)self; return c_literal(_m_iter){0}; } + +STC_INLINE _m_iter _c_MEMB(_rend)(const Self* self) + { (void)self; return c_literal(_m_iter){0}; } + +STC_INLINE void _c_MEMB(_next)(_m_iter* it) { + if (it->pos != it->_s->capmask) { ++it->ref; ++it->pos; } + else { it->ref -= it->pos; it->pos = 0; } + if (it->pos == it->_s->end) it->ref = NULL; +} + +STC_INLINE void _c_MEMB(_rnext)(_m_iter* it) { + if (it->pos == it->_s->start) it->ref = NULL; + else if (it->pos != 0) { --it->ref; --it->pos; } + else it->ref += (it->pos = it->_s->capmask); +} + +STC_INLINE isize _c_MEMB(_index)(const Self* self, _m_iter it) + { return _cbuf_toidx(self, it.pos); } + +STC_INLINE void _c_MEMB(_adjust_end_)(Self* self, isize n) + { self->end = (self->end + n) & self->capmask; } + +/* -------------------------- IMPLEMENTATION ------------------------- */ +#if defined i_implement + +STC_DEF _m_iter _c_MEMB(_advance)(_m_iter it, isize n) { + isize len = _c_MEMB(_size)(it._s); + isize pos = it.pos, idx = _cbuf_toidx(it._s, pos); + it.pos = (pos + n) & it._s->capmask; + it.ref += it.pos - pos; + if (!c_uless(idx + n, len)) it.ref = NULL; + return it; +} + +STC_DEF void +_c_MEMB(_clear)(Self* self) { + for (c_each(i, Self, *self)) + { i_keydrop(i.ref); } + self->start = 0, self->end = 0; +} + +STC_DEF void +_c_MEMB(_drop)(const Self* cself) { + Self* self = (Self*)cself; + _c_MEMB(_clear)(self); + _i_free_n(self->cbuf, self->capmask + 1); +} + +STC_DEF bool +_c_MEMB(_reserve)(Self* self, const isize cap) { + isize oldpow2 = self->capmask + (self->capmask & 1); // handle capmask = 0 + isize newpow2 = c_next_pow2(cap + 1); + if (newpow2 <= oldpow2) + return self->cbuf != NULL; + _m_value* d = (_m_value *)_i_realloc_n(self->cbuf, oldpow2, newpow2); + if (d == NULL) + return false; + isize head = oldpow2 - self->start; + if (self->start <= self->end) // [..S########E....|................] + ; + else if (head < self->end) { // [#######E.....S##|.............s!!] + c_memcpy(d + newpow2 - head, d + self->start, head*c_sizeof *d); + self->start = newpow2 - head; + } else { // [##E.....S#######|!!e.............] + c_memcpy(d + oldpow2, d, self->end*c_sizeof *d); + self->end += oldpow2; + } + self->capmask = newpow2 - 1; + self->cbuf = d; + return true; +} + +STC_DEF _m_value* +_c_MEMB(_push)(Self* self, _m_value value) { // push_back + isize end = (self->end + 1) & self->capmask; + if (end == self->start) { // full + if (!_c_MEMB(_reserve)(self, self->capmask + 3)) // => 2x expand + return NULL; + end = (self->end + 1) & self->capmask; + } + _m_value *v = self->cbuf + self->end; + self->end = end; + *v = value; + return v; +} + +STC_DEF void +_c_MEMB(_shrink_to_fit)(Self *self) { + isize sz = _c_MEMB(_size)(self); + isize newpow2 = c_next_pow2(sz + 1); + if (newpow2 > self->capmask) + return; + if (self->start <= self->end) { + c_memmove(self->cbuf, self->cbuf + self->start, sz*c_sizeof *self->cbuf); + self->start = 0, self->end = sz; + } else { + isize n = self->capmask - self->start + 1; + c_memmove(self->cbuf + (newpow2 - n), self->cbuf + self->start, n*c_sizeof *self->cbuf); + self->start = newpow2 - n; + } + self->cbuf = (_m_value *)_i_realloc_n(self->cbuf, self->capmask + 1, newpow2); + self->capmask = newpow2 - 1; +} + +#ifndef i_no_clone +STC_DEF Self +_c_MEMB(_clone)(Self q) { + Self out = q, *self = &out; (void)self; // may be used by _i_new_n/i_keyclone via i_aux. + out.start = 0; out.end = _c_MEMB(_size)(&q); + out.capmask = c_next_pow2(out.end + 1) - 1; + out.cbuf = _i_new_n(_m_value, out.capmask + 1); + isize i = 0; + if (out.cbuf) + for (c_each(it, Self, q)) + out.cbuf[i++] = i_keyclone((*it.ref)); + return out; +} +#endif // i_no_clone + +#if defined _i_has_eq +STC_DEF bool +_c_MEMB(_eq)(const Self* self, const Self* other) { + if (_c_MEMB(_size)(self) != _c_MEMB(_size)(other)) return false; + for (_m_iter i = _c_MEMB(_begin)(self), j = _c_MEMB(_begin)(other); + i.ref; _c_MEMB(_next)(&i), _c_MEMB(_next)(&j)) + { + const _m_raw _rx = i_keytoraw(i.ref), _ry = i_keytoraw(j.ref); + if (!(i_eq((&_rx), (&_ry)))) return false; + } + return true; +} +#endif // _i_has_eq +#endif // IMPLEMENTATION diff --git a/src/finchlite/codegen/stc/stc/priv/sort_prv.h b/src/finchlite/codegen/stc/stc/priv/sort_prv.h new file mode 100644 index 00000000..6a9f5098 --- /dev/null +++ b/src/finchlite/codegen/stc/stc/priv/sort_prv.h @@ -0,0 +1,136 @@ +/* MIT License + * + * Copyright (c) 2025 Tyge Løvset + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +// IWYU pragma: private +#ifdef _i_is_list + #define i_at(self, idx) (&((_m_value *)(self)->last)[idx]) + #define i_at_mut i_at +#elif !defined i_at + #define i_at(self, idx) _c_MEMB(_at)(self, idx) + #define i_at_mut(self, idx) _c_MEMB(_at_mut)(self, idx) +#endif + +STC_API void _c_MEMB(_sort_lowhigh)(Self* self, isize lo, isize hi); + +#ifdef _i_is_array +STC_API isize _c_MEMB(_lower_bound_range)(const Self* self, const _m_raw raw, isize start, isize end); +STC_API isize _c_MEMB(_binary_search_range)(const Self* self, const _m_raw raw, isize start, isize end); + +static inline void _c_MEMB(_sort)(Self* arr, isize n) + { _c_MEMB(_sort_lowhigh)(arr, 0, n - 1); } + +static inline isize // c_NPOS = not found +_c_MEMB(_lower_bound)(const Self* arr, const _m_raw raw, isize n) + { return _c_MEMB(_lower_bound_range)(arr, raw, 0, n); } + +static inline isize // c_NPOS = not found +_c_MEMB(_binary_search)(const Self* arr, const _m_raw raw, isize n) + { return _c_MEMB(_binary_search_range)(arr, raw, 0, n); } + +#elif !defined _i_is_list +STC_API isize _c_MEMB(_lower_bound_range)(const Self* self, const _m_raw raw, isize start, isize end); +STC_API isize _c_MEMB(_binary_search_range)(const Self* self, const _m_raw raw, isize start, isize end); + +static inline void _c_MEMB(_sort)(Self* self) + { _c_MEMB(_sort_lowhigh)(self, 0, _c_MEMB(_size)(self) - 1); } + +static inline isize // c_NPOS = not found +_c_MEMB(_lower_bound)(const Self* self, const _m_raw raw) + { return _c_MEMB(_lower_bound_range)(self, raw, 0, _c_MEMB(_size)(self)); } + +static inline isize // c_NPOS = not found +_c_MEMB(_binary_search)(const Self* self, const _m_raw raw) + { return _c_MEMB(_binary_search_range)(self, raw, 0, _c_MEMB(_size)(self)); } +#endif + +/* -------------------------- IMPLEMENTATION ------------------------- */ +#if defined i_implement + +static void _c_MEMB(_insertsort_lowhigh)(Self* self, isize lo, isize hi) { + for (isize j = lo, i = lo + 1; i <= hi; j = i, ++i) { + _m_value x = *i_at(self, i); + _m_raw rx = i_keytoraw((&x)); + while (j >= 0) { + _m_raw ry = i_keytoraw(i_at(self, j)); + if (!(i_less((&rx), (&ry)))) break; + *i_at_mut(self, j + 1) = *i_at(self, j); + --j; + } + *i_at_mut(self, j + 1) = x; + } +} + +STC_DEF void _c_MEMB(_sort_lowhigh)(Self* self, isize lo, isize hi) { + isize i = lo, j; + while (lo < hi) { + _m_raw pivot = i_keytoraw(i_at(self, (isize)(lo + (hi - lo)*7LL/16))), rx; + j = hi; + do { + do { rx = i_keytoraw(i_at(self, i)); } while ((i_less((&rx), (&pivot))) && ++i); + do { rx = i_keytoraw(i_at(self, j)); } while ((i_less((&pivot), (&rx))) && --j); + if (i > j) break; + c_swap(i_at_mut(self, i), i_at_mut(self, j)); + ++i; --j; + } while (i <= j); + + if (j - lo > hi - i) { + c_swap(&lo, &i); + c_swap(&hi, &j); + } + if (j - lo > 64) _c_MEMB(_sort_lowhigh)(self, lo, j); + else if (j > lo) _c_MEMB(_insertsort_lowhigh)(self, lo, j); + lo = i; + } +} + +#ifndef _i_is_list +STC_DEF isize // c_NPOS = not found +_c_MEMB(_lower_bound_range)(const Self* self, const _m_raw raw, isize start, isize end) { + isize count = end - start, step = count/2; + while (count > 0) { + const _m_raw rx = i_keytoraw(i_at(self, start + step)); + if (i_less((&rx), (&raw))) { + start += step + 1; + count -= step + 1; + step = count*7/8; + } else { + count = step; + step = count/8; + } + } + return start >= end ? c_NPOS : start; +} + +STC_DEF isize // c_NPOS = not found +_c_MEMB(_binary_search_range)(const Self* self, const _m_raw raw, isize start, isize end) { + isize res = _c_MEMB(_lower_bound_range)(self, raw, start, end); + if (res != c_NPOS) { + const _m_raw rx = i_keytoraw(i_at(self, res)); + if (i_less((&raw), (&rx))) res = c_NPOS; + } + return res; +} +#endif // !_i_is_list +#endif // IMPLEMENTATION +#undef i_at +#undef i_at_mut diff --git a/src/finchlite/codegen/stc/stc/priv/template.h b/src/finchlite/codegen/stc/stc/priv/template.h new file mode 100644 index 00000000..86211456 --- /dev/null +++ b/src/finchlite/codegen/stc/stc/priv/template.h @@ -0,0 +1,301 @@ +/* MIT License + * + * Copyright (c) 2025 Tyge Løvset + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +// IWYU pragma: private +#ifndef _i_template +#define _i_template + +#ifndef STC_TEMPLATE_H_INCLUDED +#define STC_TEMPLATE_H_INCLUDED + + #define _c_MEMB(name) c_JOIN(Self, name) + #define _c_DEFTYPES(macro, SELF, ...) macro(SELF, __VA_ARGS__) + #define _m_value _c_MEMB(_value) + #define _m_key _c_MEMB(_key) + #define _m_mapped _c_MEMB(_mapped) + #define _m_rmapped _c_MEMB(_rmapped) + #define _m_raw _c_MEMB(_raw) + #define _m_keyraw _c_MEMB(_keyraw) + #define _m_iter _c_MEMB(_iter) + #define _m_result _c_MEMB(_result) + #define _m_node _c_MEMB(_node) + + #define c_OPTION(flag) ((i_opt) & (flag)) + #define c_declared (1<<0) + #define c_no_atomic (1<<1) + #define c_arc2 (1<<2) + #define c_no_clone (1<<3) + #define c_no_hash (1<<4) + #define c_use_cmp (1<<5) + #define c_use_eq (1<<6) + #define c_cmpclass (1<<7) + #define c_keyclass (1<<8) + #define c_valclass (1<<9) + #define c_keypro (1<<10) + #define c_valpro (1<<11) +#endif + +#if defined i_rawclass // [deprecated] + #define i_cmpclass i_rawclass +#endif + +#if defined T && !defined i_type + #define i_type T +#endif +#if defined i_type && c_NUMARGS(i_type) > 1 + #define Self c_GETARG(1, i_type) + #define i_key c_GETARG(2, i_type) + #if c_NUMARGS(i_type) == 3 + #if defined _i_is_map + #define i_val c_GETARG(3, i_type) + #else + #define i_opt c_GETARG(3, i_type) + #endif + #elif c_NUMARGS(i_type) == 4 + #define i_val c_GETARG(3, i_type) + #define i_opt c_GETARG(4, i_type) + #endif +#elif !defined Self && defined i_type + #define Self i_type +#elif !defined Self + #define Self c_JOIN(_i_prefix, i_tag) +#endif + +#if defined i_aux && c_NUMARGS(i_aux) == 2 + // shorthand for defining i_aux AND i_allocator as a one-liner combo. + #define _i_aux_alloc + #define _i_aux_def c_GETARG(1, i_aux) aux; + #undef i_allocator // override: + #define i_allocator c_GETARG(2, i_aux) +#elif defined i_aux + #define _i_aux_def i_aux aux; +#else + #define _i_aux_def +#endif + +#if c_OPTION(c_declared) + #define i_declared +#endif +#if c_OPTION(c_no_hash) + #define i_no_hash +#endif +#if c_OPTION(c_use_cmp) + #define i_use_cmp +#endif +#if c_OPTION(c_use_eq) + #define i_use_eq +#endif +#if c_OPTION(c_no_clone) || defined _i_is_arc + #define i_no_clone +#endif +#if c_OPTION(c_keyclass) + #define i_keyclass i_key +#endif +#if c_OPTION(c_valclass) + #define i_valclass i_val +#endif +#if c_OPTION(c_cmpclass) + #define i_cmpclass i_key + #define i_use_cmp +#endif +#if c_OPTION(c_keypro) + #define i_keypro i_key +#endif +#if c_OPTION(c_valpro) + #define i_valpro i_val +#endif + +#if defined i_keypro + #define i_keyclass i_keypro + #define i_cmpclass c_JOIN(i_keypro, _raw) +#endif + +#if defined i_cmpclass + #define i_keyraw i_cmpclass +#elif defined i_keyclass && !defined i_keyraw + // When only i_keyclass is defined, we also define i_cmpclass to the same. + // We do not define i_keyraw here, otherwise _from() / _toraw() is expected to exist. + #define i_cmpclass i_key +#elif defined i_keyraw && !defined i_keyfrom + // Define _i_no_put when i_keyfrom is not explicitly defined and i_keyraw is. + // In this case, i_keytoraw needs to be defined (may be done later in this file). + #define _i_no_put +#endif + +// Bind to i_key "class members": _clone, _drop, _from and _toraw (when conditions are met). +#if defined i_keyclass + #ifndef i_key + #define i_key i_keyclass + #endif + #if !defined i_keyclone && !defined i_no_clone + #define i_keyclone c_JOIN(i_keyclass, _clone) + #endif + #ifndef i_keydrop + #define i_keydrop c_JOIN(i_keyclass, _drop) + #endif + #if !defined i_keyfrom && defined i_keyraw + #define i_keyfrom c_JOIN(i_keyclass, _from) + #endif + #if !defined i_keytoraw && defined i_keyraw + #define i_keytoraw c_JOIN(i_keyclass, _toraw) + #endif +#endif + +// Define when container has support for sorting (cmp) and linear search (eq) +#if defined i_use_cmp || defined i_cmp || defined i_less + #define _i_has_cmp +#endif +#if defined i_use_cmp || defined i_cmp || defined i_use_eq || defined i_eq + #define _i_has_eq +#endif + +// Bind to i_cmpclass "class members": _cmp, _eq and _hash (when conditions are met). +#if defined i_cmpclass + #if !(defined i_cmp || defined i_less) && (defined i_use_cmp || defined _i_sorted) + #define i_cmp c_JOIN(i_cmpclass, _cmp) + #endif + #if !defined i_eq && (defined i_use_eq || defined i_hash || defined _i_is_hash) + #define i_eq c_JOIN(i_cmpclass, _eq) + #endif + #if !(defined i_hash || defined i_no_hash) + #define i_hash c_JOIN(i_cmpclass, _hash) + #endif +#endif + +#if !defined i_key + #error "No i_key defined" +#elif defined i_keyraw && !(c_OPTION(c_cmpclass) || defined i_keytoraw) + #error "If i_cmpclass / i_keyraw is defined, i_keytoraw must be defined too" +#elif !defined i_no_clone && (defined i_keyclone ^ defined i_keydrop) + #error "Both i_keyclone and i_keydrop must be defined, if any (unless i_no_clone defined)." +#elif defined i_from || defined i_drop + #error "i_from / i_drop not supported. Use i_keyfrom/i_keydrop" +#elif defined i_keyto || defined i_valto + #error i_keyto / i_valto not supported. Use i_keytoraw / i_valtoraw +#elif defined i_keyraw && defined i_use_cmp && !defined _i_has_cmp + #error "For smap / sset / pqueue, i_cmp or i_less must be defined when i_keyraw is defined." +#endif + +// Fill in missing i_eq, i_less, i_cmp functions with defaults. +#if !defined i_eq && defined i_cmp + #define i_eq(x, y) (i_cmp(x, y)) == 0 +#elif !defined i_eq + #define i_eq(x, y) *x == *y // works for integral types +#endif +#if !defined i_less && defined i_cmp + #define i_less(x, y) (i_cmp(x, y)) < 0 +#elif !defined i_less + #define i_less(x, y) *x < *y // works for integral types +#endif +#if !defined i_cmp && defined i_less + #define i_cmp(x, y) (i_less(y, x)) - (i_less(x, y)) +#endif +#if !(defined i_hash || defined i_no_hash) + #define i_hash c_default_hash +#endif + +#define i_no_emplace + +#ifndef i_tag + #define i_tag i_key +#endif +#ifndef i_keyfrom + #define i_keyfrom c_default_clone +#else + #undef i_no_emplace +#endif +#ifndef i_keyraw + #define i_keyraw i_key +#endif +#ifndef i_keytoraw + #define i_keytoraw c_default_toraw +#endif +#ifndef i_keyclone + #define i_keyclone c_default_clone +#endif +#ifndef i_keydrop + #define i_keydrop c_default_drop +#endif + +#if defined _i_is_map // ---- process hashmap/sortedmap value i_val, ... ---- + +#if defined i_valpro + #define i_valclass i_valpro + #define i_valraw c_JOIN(i_valpro, _raw) +#endif + +#ifdef i_valclass + #ifndef i_val + #define i_val i_valclass + #endif + #if !defined i_valclone && !defined i_no_clone + #define i_valclone c_JOIN(i_valclass, _clone) + #endif + #ifndef i_valdrop + #define i_valdrop c_JOIN(i_valclass, _drop) + #endif + #if !defined i_valfrom && defined i_valraw + #define i_valfrom c_JOIN(i_valclass, _from) + #endif + #if !defined i_valtoraw && defined i_valraw + #define i_valtoraw c_JOIN(i_valclass, _toraw) + #endif +#endif + +#ifndef i_val + #error "i_val* must be defined for maps" +#elif defined i_valraw && !defined i_valtoraw + #error "If i_valraw is defined, i_valtoraw must be defined too" +#elif !defined i_no_clone && (defined i_valclone ^ defined i_valdrop) + #error "Both i_valclone and i_valdrop must be defined, if any" +#endif + +#ifndef i_valfrom + #define i_valfrom c_default_clone + #ifdef i_valraw + #define _i_no_put + #endif +#else + #undef i_no_emplace +#endif +#ifndef i_valraw + #define i_valraw i_val +#endif +#ifndef i_valtoraw + #define i_valtoraw c_default_toraw +#endif +#ifndef i_valclone + #define i_valclone c_default_clone +#endif +#ifndef i_valdrop + #define i_valdrop c_default_drop +#endif + +#endif // !_i_is_map + +#ifndef i_val + #define i_val i_key +#endif +#ifndef i_valraw + #define i_valraw i_keyraw +#endif +#endif // STC_TEMPLATE_H_INCLUDED diff --git a/src/finchlite/codegen/stc/stc/priv/template2.h b/src/finchlite/codegen/stc/stc/priv/template2.h new file mode 100644 index 00000000..fc5cc8f2 --- /dev/null +++ b/src/finchlite/codegen/stc/stc/priv/template2.h @@ -0,0 +1,72 @@ +/* MIT License + * + * Copyright (c) 2025 Tyge Løvset + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +// IWYU pragma: private +#undef T // alias for i_type +#undef i_type +#undef i_class +#undef i_tag +#undef i_opt +#undef i_capacity + +#undef i_key +#undef i_keypro // Replaces next two +#undef i_key_str // [deprecated] +#undef i_key_arcbox // [deprecated] +#undef i_keyclass +#undef i_cmpclass // define i_keyraw, and bind i_cmp, i_eq, i_hash "class members" +#undef i_rawclass // [deprecated] for i_cmpclass +#undef i_keyclone +#undef i_keydrop +#undef i_keyraw +#undef i_keyfrom +#undef i_keytoraw +#undef i_cmp +#undef i_less +#undef i_eq +#undef i_hash + +#undef i_val +#undef i_valpro // Replaces next two +#undef i_val_str // [deprecated] +#undef i_val_arcbox // [deprecated] +#undef i_valclass +#undef i_valclone +#undef i_valdrop +#undef i_valraw +#undef i_valfrom +#undef i_valtoraw + +#undef i_use_cmp +#undef i_use_eq +#undef i_no_hash +#undef i_no_clone +#undef i_no_emplace +#undef i_declared + +#undef _i_no_put +#undef _i_aux_def +#undef _i_has_cmp +#undef _i_has_eq +#undef _i_prefix +#undef _i_template +#undef Self diff --git a/src/finchlite/codegen/stc/stc/priv/utf8_prv.h b/src/finchlite/codegen/stc/stc/priv/utf8_prv.h new file mode 100644 index 00000000..1a5e6154 --- /dev/null +++ b/src/finchlite/codegen/stc/stc/priv/utf8_prv.h @@ -0,0 +1,192 @@ +/* MIT License + * + * Copyright (c) 2025 Tyge Løvset + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +// IWYU pragma: private, include "stc/utf8.h" +#ifndef STC_UTF8_PRV_H_INCLUDED +#define STC_UTF8_PRV_H_INCLUDED + +#include + +// The following functions assume valid utf8 strings: + +/* number of bytes in the utf8 codepoint from s */ +STC_INLINE int utf8_chr_size(const char *s) { + unsigned b = (uint8_t)*s; + if (b < 0x80) return 1; + /*if (b < 0xC2) return 0;*/ + if (b < 0xE0) return 2; + if (b < 0xF0) return 3; + /*if (b < 0xF5)*/ return 4; + /*return 0;*/ +} + +/* number of codepoints in the utf8 string s */ +STC_INLINE isize utf8_count(const char *s) { + isize size = 0; + while (*s) + size += (*++s & 0xC0) != 0x80; + return size; +} + +STC_INLINE isize utf8_count_n(const char *s, isize nbytes) { + isize size = 0; + while ((nbytes-- != 0) & (*s != 0)) { + size += (*++s & 0xC0) != 0x80; + } + return size; +} + +STC_INLINE const char* utf8_at(const char *s, isize u8pos) { + while ((u8pos > 0) & (*s != 0)) + u8pos -= (*++s & 0xC0) != 0x80; + return s; +} + +STC_INLINE const char* utf8_offset(const char* s, isize u8pos) { + int inc = 1; + if (u8pos < 0) u8pos = -u8pos, inc = -1; + while (u8pos && *s) + u8pos -= (*(s += inc) & 0xC0) != 0x80; + return s; +} + +STC_INLINE isize utf8_to_index(const char* s, isize u8pos) + { return utf8_at(s, u8pos) - s; } + +STC_INLINE csview utf8_subview(const char *s, isize u8pos, isize u8len) { + csview span; + span.buf = utf8_at(s, u8pos); + span.size = utf8_to_index(span.buf, u8len); + return span; +} + +// ------------------------------------------------------ +// Functions below must be linked with utf8_prv.c content +// To call them, either define i_import before including +// one of cstr, csview, zsview, or link with src/libstc.a + +/* decode next utf8 codepoint. https://bjoern.hoehrmann.de/utf-8/decoder/dfa */ +typedef struct { uint32_t state, codep; } utf8_decode_t; +extern const uint8_t utf8_dtab[]; /* utf8code.c */ +#define utf8_ACCEPT 0 +#define utf8_REJECT 12 + +extern bool utf8_valid(const char* s); +extern bool utf8_valid_n(const char* s, isize nbytes); +extern int utf8_encode(char *out, uint32_t c); +extern int utf8_decode_codepoint(utf8_decode_t* d, const char* s, const char* end); +extern int utf8_icompare(const csview s1, const csview s2); +extern uint32_t utf8_peek_at(const char* s, isize u8offset); +extern uint32_t utf8_casefold(uint32_t c); +extern uint32_t utf8_tolower(uint32_t c); +extern uint32_t utf8_toupper(uint32_t c); + +STC_INLINE bool utf8_isupper(uint32_t c) + { return c < 128 ? (c >= 'A') & (c <= 'Z') : utf8_tolower(c) != c; } + +STC_INLINE bool utf8_islower(uint32_t c) + { return c < 128 ? (c >= 'a') & (c <= 'z') : utf8_toupper(c) != c; } + +STC_INLINE uint32_t utf8_decode(utf8_decode_t* d, const uint32_t byte) { + const uint32_t type = utf8_dtab[byte]; + d->codep = d->state ? (byte & 0x3fu) | (d->codep << 6) + : (0xffU >> type) & byte; + return d->state = utf8_dtab[256 + d->state + type]; +} + +STC_INLINE uint32_t utf8_peek(const char* s) { + utf8_decode_t d = {.state=0}; + do { + utf8_decode(&d, (uint8_t)*s++); + } while (d.state > utf8_REJECT); + return d.state == utf8_ACCEPT ? d.codep : 0xFFFD; +} + +/* case-insensitive utf8 string comparison */ +STC_INLINE int utf8_icmp(const char* s1, const char* s2) { + return utf8_icompare(c_sv(s1, INTPTR_MAX), c_sv(s2, INTPTR_MAX)); +} + +// ------------------------------------------------------ +// Functions below must be linked with ucd_prv.c content + +enum utf8_group { + U8G_Cc, U8G_L, U8G_Lm, U8G_Lt, U8G_Nd, U8G_Nl, U8G_No, + U8G_P, U8G_Pc, U8G_Pd, U8G_Pe, U8G_Pf, U8G_Pi, U8G_Ps, + U8G_Sc, U8G_Sk, U8G_Sm, U8G_Zl, U8G_Zp, U8G_Zs, + U8G_Arabic, U8G_Bengali, U8G_Cyrillic, + U8G_Devanagari, U8G_Georgian, U8G_Greek, + U8G_Han, U8G_Hiragana, U8G_Katakana, + U8G_Latin, U8G_Thai, + U8G_SIZE +}; + +extern bool utf8_isgroup(int group, uint32_t c); + +STC_INLINE bool utf8_isdigit(uint32_t c) + { return c < 128 ? (c >= '0') & (c <= '9') : utf8_isgroup(U8G_Nd, c); } + +STC_INLINE bool utf8_isalpha(uint32_t c) + { return (c < 128 ? isalpha((int)c) != 0 : utf8_isgroup(U8G_L, c)); } + +STC_INLINE bool utf8_iscased(uint32_t c) { + if (c < 128) return isalpha((int)c) != 0; + return utf8_toupper(c) != c || utf8_tolower(c) != c || utf8_isgroup(U8G_Lt, c); +} + +STC_INLINE bool utf8_isalnum(uint32_t c) { + if (c < 128) return isalnum((int)c) != 0; + return utf8_isgroup(U8G_L, c) || utf8_isgroup(U8G_Nd, c); +} + +STC_INLINE bool utf8_isword(uint32_t c) { + if (c < 128) return (isalnum((int)c) != 0) | (c == '_'); + return utf8_isgroup(U8G_L, c) || utf8_isgroup(U8G_Nd, c) || utf8_isgroup(U8G_Pc, c); +} + +STC_INLINE bool utf8_isblank(uint32_t c) { + if (c < 128) return (c == ' ') | (c == '\t'); + return utf8_isgroup(U8G_Zs, c); +} + +STC_INLINE bool utf8_isspace(uint32_t c) { + if (c < 128) return isspace((int)c) != 0; + return ((c == 8232) | (c == 8233)) || utf8_isgroup(U8G_Zs, c); +} + +#define c_lowerbound(T, c, at, less, N, ret) do { \ + int _n = N, _i = 0, _mid = _n/2; \ + T _c = c; \ + while (_n > 0) { \ + if (less(at((_i + _mid)), &_c)) { \ + _i += _mid + 1; \ + _n -= _mid + 1; \ + _mid = _n*7/8; \ + } else { \ + _n = _mid; \ + _mid = _n/8; \ + } \ + } \ + *(ret) = _i; \ +} while (0) + +#endif // STC_UTF8_PRV_H_INCLUDED diff --git a/src/finchlite/codegen/stc/stc/queue.h b/src/finchlite/codegen/stc/stc/queue.h new file mode 100644 index 00000000..507cf8ae --- /dev/null +++ b/src/finchlite/codegen/stc/stc/queue.h @@ -0,0 +1,39 @@ +/* MIT License + * + * Copyright (c) 2025 Tyge Løvset + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +// Queue. Implemented as a ring buffer. +#include "priv/linkage.h" +#include "types.h" + +#ifndef STC_QUEUE_H_INCLUDED +#define STC_QUEUE_H_INCLUDED +#include "common.h" +#include +#endif // STC_QUEUE_H_INCLUDED + +#ifndef _i_prefix + #define _i_prefix queue_ +#endif +#include "priv/template.h" +#include "priv/queue_prv.h" +#include "sys/finalize.h" diff --git a/src/finchlite/codegen/stc/stc/random.h b/src/finchlite/codegen/stc/stc/random.h new file mode 100644 index 00000000..4d729054 --- /dev/null +++ b/src/finchlite/codegen/stc/stc/random.h @@ -0,0 +1,248 @@ +/* MIT License + * + * Copyright (c) 2025 Tyge Løvset + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. +*/ +#define i_header // external linkage of normal_dist by default. +#include "priv/linkage.h" + +#ifndef STC_RANDOM_H_INCLUDED +#define STC_RANDOM_H_INCLUDED + +#include "common.h" + +// ===== crand64 =================================== + +typedef struct { + uint64_t data[4]; +} crand64; + +typedef struct { + double mean, stddev; + double _next; + int _has_next; +} crand64_normal_dist; + +STC_API double crand64_normal(crand64_normal_dist* d); +STC_API double crand64_normal_r(crand64* rng, uint64_t stream, crand64_normal_dist* d); + +#if INTPTR_MAX == INT64_MAX + #define crandWS crand64 +#else + #define crandWS crand32 +#endif + +#define c_shuffle_seed(s) \ + c_JOIN(crandWS, _seed)(s) + +#define c_shuffle_array(array, n) do { \ + typedef struct { char d[sizeof 0[array]]; } _etype; \ + _etype* _arr = (_etype *)(array); \ + for (isize _i = (n) - 1; _i > 0; --_i) { \ + isize _j = (isize)(c_JOIN(crandWS, _uint)() % (_i + 1)); \ + c_swap(_arr + _i, _arr + _j); \ + } \ +} while (0) + +// Compiles with vec, stack, and deque container types: +#define c_shuffle(CntType, self) do { \ + CntType* _self = self; \ + for (isize _i = CntType##_size(_self) - 1; _i > 0; --_i) { \ + isize _j = (isize)(c_JOIN(crandWS, _uint)() % (_i + 1)); \ + c_swap(CntType##_at_mut(_self, _i), CntType##_at_mut(_self, _j)); \ + } \ +} while (0) + +STC_INLINE void crand64_seed_r(crand64* rng, uint64_t seed) { + uint64_t* s = rng->data; + s[0] = seed*0x9e3779b97f4a7c15; s[0] ^= s[0] >> 30; + s[1] = s[0]*0xbf58476d1ce4e5b9; s[1] ^= s[1] >> 27; + s[2] = s[1]*0x94d049bb133111eb; s[2] ^= s[2] >> 31; + s[3] = seed; +} + +// Minimum period length 2^64 per stream. 2^63 streams (odd numbers only) +STC_INLINE uint64_t crand64_uint_r(crand64* rng, uint64_t stream) { + uint64_t* s = rng->data; + const uint64_t result = (s[0] ^ (s[3] += stream)) + s[1]; + s[0] = s[1] ^ (s[1] >> 11); + s[1] = s[2] + (s[2] << 3); + s[2] = ((s[2] << 24) | (s[2] >> 40)) + result; + return result; +} + +STC_INLINE double crand64_real_r(crand64* rng, uint64_t stream) + { return (double)(crand64_uint_r(rng, stream) >> 11) * 0x1.0p-53; } + +STC_INLINE crand64* _stc64(void) { + static crand64 rng = {{0x9e3779bb07979af0,0x6f682616bae3641a,0xe220a8397b1dcdaf,0x1}}; + return &rng; +} + +STC_INLINE void crand64_seed(uint64_t seed) + { crand64_seed_r(_stc64(), seed); } + +STC_INLINE crand64 crand64_from(uint64_t seed) + { crand64 rng; crand64_seed_r(&rng, seed); return rng; } + +STC_INLINE uint64_t crand64_uint(void) + { return crand64_uint_r(_stc64(), 1); } + +STC_INLINE double crand64_real(void) + { return crand64_real_r(_stc64(), 1); } + +// --- crand64_uniform --- + +typedef struct { + int64_t low; + uint64_t range, threshold; +} crand64_uniform_dist; + +STC_INLINE crand64_uniform_dist +crand64_make_uniform(int64_t low, int64_t high) { + crand64_uniform_dist d = {low, (uint64_t)(high - low + 1)}; + d.threshold = (uint64_t)(0 - d.range) % d.range; + return d; +} + +// 128-bit multiplication +#if defined(__SIZEOF_INT128__) + #define c_umul128(a, b, lo, hi) \ + do { __uint128_t _z = (__uint128_t)(a)*(b); \ + *(lo) = (uint64_t)_z, *(hi) = (uint64_t)(_z >> 64U); } while(0) +#elif defined(_MSC_VER) && defined(_WIN64) + #include + #define c_umul128(a, b, lo, hi) ((void)(*(lo) = _umul128(a, b, hi))) +#elif defined(__x86_64__) + #define c_umul128(a, b, lo, hi) \ + asm("mulq %3" : "=a"(*(lo)), "=d"(*(hi)) : "a"(a), "rm"(b)) +#endif + +STC_INLINE int64_t +crand64_uniform_r(crand64* rng, uint64_t stream, crand64_uniform_dist* d) { + uint64_t lo, hi; + #ifdef c_umul128 + do { c_umul128(crand64_uint_r(rng, stream), d->range, &lo, &hi); } while (lo < d->threshold); + #else + do { lo = crand64_uint_r(rng, stream); hi = lo % d->range; } while (lo - hi > -d->range); + #endif + return d->low + (int64_t)hi; +} + +STC_INLINE int64_t crand64_uniform(crand64_uniform_dist* d) + { return crand64_uniform_r(_stc64(), 1, d); } + +// ===== crand32 =================================== + +typedef struct { uint32_t data[4]; } crand32; + +STC_INLINE void crand32_seed_r(crand32* rng, uint32_t seed) { + uint32_t* s = rng->data; + s[0] = seed*0x9e3779b9; s[0] ^= s[0] >> 16; + s[1] = s[0]*0x21f0aaad; s[1] ^= s[1] >> 15; + s[2] = s[1]*0x735a2d97; s[2] ^= s[2] >> 15; + s[3] = seed; +} + +// Minimum period length 2^32 per stream. 2^31 streams (odd numbers only) +STC_INLINE uint32_t crand32_uint_r(crand32* rng, uint32_t stream) { + uint32_t* s = rng->data; + const uint32_t result = (s[0] ^ (s[3] += stream)) + s[1]; + s[0] = s[1] ^ (s[1] >> 9); + s[1] = s[2] + (s[2] << 3); + s[2] = ((s[2] << 21) | (s[2] >> 11)) + result; + return result; +} + +STC_INLINE double crand32_real_r(crand32* rng, uint32_t stream) + { return crand32_uint_r(rng, stream) * 0x1.0p-32; } + +STC_INLINE crand32* _stc32(void) { + static crand32 rng = {{0x9e37e78e,0x6eab1ba1,0x64625032,0x1}}; + return &rng; +} + +STC_INLINE void crand32_seed(uint32_t seed) + { crand32_seed_r(_stc32(), seed); } + +STC_INLINE crand32 crand32_from(uint32_t seed) + { crand32 rng; crand32_seed_r(&rng, seed); return rng; } + +STC_INLINE uint32_t crand32_uint(void) + { return crand32_uint_r(_stc32(), 1); } + +STC_INLINE double crand32_real(void) + { return crand32_real_r(_stc32(), 1); } + +// --- crand32_uniform --- + +typedef struct { + int32_t low; + uint32_t range, threshold; +} crand32_uniform_dist; + +STC_INLINE crand32_uniform_dist +crand32_make_uniform(int32_t low, int32_t high) { + crand32_uniform_dist d = {low, (uint32_t)(high - low + 1)}; + d.threshold = (uint32_t)(0 - d.range) % d.range; + return d; +} + +STC_INLINE int32_t +crand32_uniform_r(crand32* rng, uint32_t stream, crand32_uniform_dist* d) { + uint64_t r; + do { + r = crand32_uint_r(rng, stream) * (uint64_t)d->range; + } while ((uint32_t)r < d->threshold); + return d->low + (int32_t)(r >> 32); +} + +STC_INLINE int64_t crand32_uniform(crand32_uniform_dist* d) + { return crand32_uniform_r(_stc32(), 1, d); } + +#endif // STC_RANDOM_H_INCLUDED + +/* -------------------------- IMPLEMENTATION ------------------------- */ +#if defined i_implement && !defined STC_RANDOM_IMPLEMENT +#define STC_RANDOM_IMPLEMENT +#include + +STC_DEF double +crand64_normal_r(crand64* rng, uint64_t stream, crand64_normal_dist* d) { + double v1, v2, sq, rt; + if (d->_has_next++ & 1) + return d->_next*d->stddev + d->mean; + do { + // range (-1.0, 1.0): + v1 = (double)((int64_t)crand64_uint_r(rng, stream) >> 11) * 0x1.0p-52; + v2 = (double)((int64_t)crand64_uint_r(rng, stream) >> 11) * 0x1.0p-52; + + sq = v1*v1 + v2*v2; + } while (sq >= 1.0 || sq == 0.0); + rt = sqrt(-2.0 * log(sq) / sq); + d->_next = v2*rt; + return (v1*rt)*d->stddev + d->mean; +} + +STC_DEF double crand64_normal(crand64_normal_dist* d) + { return crand64_normal_r(_stc64(), 1, d); } + +#endif // IMPLEMENT +#include "priv/linkage2.h" diff --git a/src/finchlite/codegen/stc/stc/rc.h b/src/finchlite/codegen/stc/stc/rc.h new file mode 100644 index 00000000..98ec6e08 --- /dev/null +++ b/src/finchlite/codegen/stc/stc/rc.h @@ -0,0 +1,38 @@ +/* MIT License + * + * Copyright (c) 2025 Tyge Løvmap + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +// Unordered map - implemented with the robin-hood hashing scheme. +/* +#define T IRefc, int +#include +#include + +int main(void) { + IRefc rc = IRefc_make(42); + IRefc_drop(&rc); +} +*/ + +#define i_no_atomic +#define _i_prefix rc_ +#include "arc.h" diff --git a/src/finchlite/codegen/stc/stc/smap.h b/src/finchlite/codegen/stc/stc/smap.h new file mode 100644 index 00000000..1dfdb4b8 --- /dev/null +++ b/src/finchlite/codegen/stc/stc/smap.h @@ -0,0 +1,612 @@ +/* MIT License + * + * Copyright (c) 2025 Tyge Løvset + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +// Sorted/Ordered set and map - implemented as an AA-tree. +/* +#include +#include + +#define T SMap, cstr, double, (c_keypro) // Sorted map +#include + +int main(void) { + SMap m = {0}; + SMap_emplace(&m, "Testing one", 1.234); + SMap_emplace(&m, "Testing two", 12.34); + SMap_emplace(&m, "Testing three", 123.4); + + SMap_value *v = SMap_get(&m, "Testing five"); // NULL + double num = *SMap_at(&m, "Testing one"); + SMap_emplace_or_assign(&m, "Testing three", 1000.0); // update + SMap_erase(&m, "Testing two"); + + for (c_each(i, SMap, m)) + printf("map %s: %g\n", cstr_str(&i.ref->first), i.ref->second); + + SMap_drop(&m); +} +*/ +#include "priv/linkage.h" +#include "types.h" + +#ifndef STC_SMAP_H_INCLUDED +#define STC_SMAP_H_INCLUDED +#include "common.h" +#include +#endif // STC_SMAP_H_INCLUDED + +#ifndef _i_prefix + #define _i_prefix smap_ +#endif +#ifndef _i_is_set + #define _i_is_map + #define _i_MAP_ONLY c_true + #define _i_SET_ONLY c_false + #define _i_keyref(vp) (&(vp)->first) +#else + #define _i_MAP_ONLY c_false + #define _i_SET_ONLY c_true + #define _i_keyref(vp) (vp) +#endif +#define _i_sorted +#include "priv/template.h" +#ifndef i_declared + _c_DEFTYPES(_declare_aatree, Self, i_key, i_val, _i_MAP_ONLY, _i_SET_ONLY, _i_aux_def); +#endif + +_i_MAP_ONLY( struct _m_value { + _m_key first; + _m_mapped second; +}; ) +struct _m_node { + int32_t link[2]; + int8_t level; + _m_value value; +}; + +typedef i_keyraw _m_keyraw; +typedef i_valraw _m_rmapped; +typedef _i_SET_ONLY( _m_keyraw ) + _i_MAP_ONLY( struct { _m_keyraw first; _m_rmapped second; } ) + _m_raw; + +#ifndef i_no_emplace +STC_API _m_result _c_MEMB(_emplace)(Self* self, _m_keyraw rkey _i_MAP_ONLY(, _m_rmapped rmapped)); +#endif // !i_no_emplace + +#ifndef i_no_clone +STC_API Self _c_MEMB(_clone)(Self tree); +#endif // !i_no_clone + +STC_API void _c_MEMB(_drop)(const Self* cself); +STC_API bool _c_MEMB(_reserve)(Self* self, isize cap); +STC_API _m_value* _c_MEMB(_find_it)(const Self* self, _m_keyraw rkey, _m_iter* out); +STC_API _m_iter _c_MEMB(_lower_bound)(const Self* self, _m_keyraw rkey); +STC_API _m_value* _c_MEMB(_front)(const Self* self); +STC_API _m_value* _c_MEMB(_back)(const Self* self); +STC_API int _c_MEMB(_erase)(Self* self, _m_keyraw rkey); +STC_API _m_iter _c_MEMB(_erase_at)(Self* self, _m_iter it); +STC_API _m_iter _c_MEMB(_erase_range)(Self* self, _m_iter it1, _m_iter it2); +STC_API _m_iter _c_MEMB(_begin)(const Self* self); +STC_API void _c_MEMB(_next)(_m_iter* it); + +STC_INLINE bool _c_MEMB(_is_empty)(const Self* self) { return self->size == 0; } +STC_INLINE isize _c_MEMB(_size)(const Self* self) { return self->size; } +STC_INLINE isize _c_MEMB(_capacity)(const Self* self) { return self->capacity; } +STC_INLINE _m_iter _c_MEMB(_find)(const Self* self, _m_keyraw rkey) + { _m_iter it; _c_MEMB(_find_it)(self, rkey, &it); return it; } +STC_INLINE bool _c_MEMB(_contains)(const Self* self, _m_keyraw rkey) + { _m_iter it; return _c_MEMB(_find_it)(self, rkey, &it) != NULL; } +STC_INLINE const _m_value* _c_MEMB(_get)(const Self* self, _m_keyraw rkey) + { _m_iter it; return _c_MEMB(_find_it)(self, rkey, &it); } +STC_INLINE _m_value* _c_MEMB(_get_mut)(Self* self, _m_keyraw rkey) + { _m_iter it; return _c_MEMB(_find_it)(self, rkey, &it); } + +STC_INLINE _m_raw _c_MEMB(_value_toraw)(const _m_value* val) { + return _i_SET_ONLY( i_keytoraw(val) ) + _i_MAP_ONLY( c_literal(_m_raw){i_keytoraw((&val->first)), + i_valtoraw((&val->second))} ); +} + +STC_INLINE void _c_MEMB(_value_drop)(const Self* self, _m_value* val) { + (void)self; + i_keydrop(_i_keyref(val)); + _i_MAP_ONLY( i_valdrop((&val->second)); ) +} + +STC_INLINE Self _c_MEMB(_move)(Self *self) { + Self m = *self; + self->capacity = self->size = self->root = self->disp = self->head = 0; + self->nodes = NULL; + return m; +} + +STC_INLINE void _c_MEMB(_clear)(Self* self) { + _c_MEMB(_drop)(self); + (void)_c_MEMB(_move)(self); +} + +STC_INLINE void _c_MEMB(_take)(Self *self, Self unowned) { + _c_MEMB(_drop)(self); + *self = unowned; +} + +#ifndef i_no_clone +STC_INLINE _m_value _c_MEMB(_value_clone)(const Self* self, _m_value _val) { + (void)self; + *_i_keyref(&_val) = i_keyclone((*_i_keyref(&_val))); + _i_MAP_ONLY( _val.second = i_valclone(_val.second); ) + return _val; +} + +STC_INLINE void _c_MEMB(_copy)(Self *self, const Self* other) { + if (self == other) + return; + _c_MEMB(_drop)(self); + *self = _c_MEMB(_clone)(*other); +} + +STC_INLINE void _c_MEMB(_shrink_to_fit)(Self *self) { + Self tmp = _c_MEMB(_clone)(*self); + _c_MEMB(_drop)(self); *self = tmp; +} +#endif // !i_no_clone + +STC_API _m_result _c_MEMB(_insert_entry_)(Self* self, _m_keyraw rkey); + +#ifdef _i_is_map + STC_API _m_result _c_MEMB(_insert_or_assign)(Self* self, _m_key key, _m_mapped mapped); + #ifndef i_no_emplace + STC_API _m_result _c_MEMB(_emplace_or_assign)(Self* self, _m_keyraw rkey, _m_rmapped rmapped); + #endif + + STC_INLINE const _m_mapped* _c_MEMB(_at)(const Self* self, _m_keyraw rkey) + { _m_iter it; return &_c_MEMB(_find_it)(self, rkey, &it)->second; } + + STC_INLINE _m_mapped* _c_MEMB(_at_mut)(Self* self, _m_keyraw rkey) + { _m_iter it; return &_c_MEMB(_find_it)(self, rkey, &it)->second; } +#endif // _i_is_map + +STC_INLINE _m_iter _c_MEMB(_end)(const Self* self) { + _m_iter it; (void)self; + it.ref = NULL, it._top = 0, it._tn = 0; + return it; +} + +STC_INLINE _m_iter _c_MEMB(_advance)(_m_iter it, size_t n) { + while (n-- && it.ref) + _c_MEMB(_next)(&it); + return it; +} + +#if defined _i_has_eq +STC_INLINE bool +_c_MEMB(_eq)(const Self* self, const Self* other) { + if (_c_MEMB(_size)(self) != _c_MEMB(_size)(other)) return false; + _m_iter i = _c_MEMB(_begin)(self), j = _c_MEMB(_begin)(other); + for (; i.ref; _c_MEMB(_next)(&i), _c_MEMB(_next)(&j)) { + const _m_keyraw _rx = i_keytoraw(_i_keyref(i.ref)), _ry = i_keytoraw(_i_keyref(j.ref)); + if (!(i_eq((&_rx), (&_ry)))) return false; + } + return true; +} +#endif + +STC_INLINE _m_result +_c_MEMB(_insert)(Self* self, _m_key _key _i_MAP_ONLY(, _m_mapped _mapped)) { + _m_result _res = _c_MEMB(_insert_entry_)(self, i_keytoraw((&_key))); + if (_res.inserted) + { *_i_keyref(_res.ref) = _key; _i_MAP_ONLY( _res.ref->second = _mapped; )} + else + { i_keydrop((&_key)); _i_MAP_ONLY( i_valdrop((&_mapped)); )} + return _res; +} + +STC_INLINE _m_value* _c_MEMB(_push)(Self* self, _m_value _val) { + _m_result _res = _c_MEMB(_insert_entry_)(self, i_keytoraw(_i_keyref(&_val))); + if (_res.inserted) + *_res.ref = _val; + else + _c_MEMB(_value_drop)(self, &_val); + return _res.ref; +} + +#if defined _i_is_map && !defined _i_no_put +STC_INLINE _m_result _c_MEMB(_put)(Self* self, _m_keyraw rkey, _m_rmapped rmapped) { + #ifdef i_no_emplace + return _c_MEMB(_insert_or_assign)(self, rkey, rmapped); + #else + return _c_MEMB(_emplace_or_assign)(self, rkey, rmapped); + #endif +} +#endif + +#ifndef _i_no_put +STC_INLINE void _c_MEMB(_put_n)(Self* self, const _m_raw* raw, isize n) { + while (n--) + #if defined _i_is_set && defined i_no_emplace + _c_MEMB(_insert)(self, *raw++); + #elif defined _i_is_set + _c_MEMB(_emplace)(self, *raw++); + #else + _c_MEMB(_put)(self, raw->first, raw->second), ++raw; + #endif +} +#endif + +#ifndef _i_aux_alloc +STC_INLINE Self _c_MEMB(_init)(void) + { Self cx = {0}; return cx; } + +#ifndef _i_no_put +STC_INLINE Self _c_MEMB(_from_n)(const _m_raw* raw, isize n) + { Self cx = {0}; _c_MEMB(_put_n)(&cx, raw, n); return cx; } +#endif + +STC_INLINE Self _c_MEMB(_with_capacity)(const isize cap) + { Self cx = {0}; _c_MEMB(_reserve)(&cx, cap); return cx; } +#endif + +/* -------------------------- IMPLEMENTATION ------------------------- */ +#if defined i_implement + +STC_DEF void +_c_MEMB(_next)(_m_iter *it) { + int32_t tn = it->_tn; + if (it->_top || tn) { + while (tn) { + it->_st[it->_top++] = tn; + tn = it->_d[tn].link[0]; + } + tn = it->_st[--it->_top]; + it->_tn = it->_d[tn].link[1]; + it->ref = &it->_d[tn].value; + } else + it->ref = NULL; +} + +STC_DEF _m_iter +_c_MEMB(_begin)(const Self* self) { + _m_iter it; + it.ref = NULL; + it._d = self->nodes, it._top = 0; + it._tn = self->root; + if (it._tn) + _c_MEMB(_next)(&it); + return it; +} + +STC_DEF bool +_c_MEMB(_reserve)(Self* self, const isize cap) { + if (cap <= self->capacity) + return false; + _m_node* nodes = (_m_node*)_i_realloc_n(self->nodes, self->capacity + 1, cap + 1); + if (nodes == NULL) + return false; + nodes[0] = c_literal(_m_node){0}; + self->nodes = nodes; + self->capacity = (int32_t)cap; + return true; +} + +STC_DEF _m_value* +_c_MEMB(_front)(const Self* self) { + _m_node *d = self->nodes; + int32_t tn = self->root; + while (d[tn].link[0]) + tn = d[tn].link[0]; + return &d[tn].value; +} + +STC_DEF _m_value* +_c_MEMB(_back)(const Self* self) { + _m_node *d = self->nodes; + int32_t tn = self->root; + while (d[tn].link[1]) + tn = d[tn].link[1]; + return &d[tn].value; +} + +static int32_t +_c_MEMB(_new_node_)(Self* self, int level) { + int32_t tn; + if (self->disp != 0) { + tn = self->disp; + self->disp = self->nodes[tn].link[1]; + } else { + if (self->head == self->capacity) + if (!_c_MEMB(_reserve)(self, self->head*3/2 + 4)) + return 0; + tn = ++self->head; /* start with 1, 0 is nullnode. */ + } + _m_node* dn = &self->nodes[tn]; + dn->link[0] = dn->link[1] = 0; dn->level = (int8_t)level; + return tn; +} + +#ifdef _i_is_map + STC_DEF _m_result + _c_MEMB(_insert_or_assign)(Self* self, _m_key _key, _m_mapped _mapped) { + _m_result _res = _c_MEMB(_insert_entry_)(self, i_keytoraw((&_key))); + _m_mapped* _mp = _res.ref ? &_res.ref->second : &_mapped; + if (_res.inserted) + _res.ref->first = _key; + else + { i_keydrop((&_key)); i_valdrop(_mp); } + *_mp = _mapped; + return _res; + } + + #ifndef i_no_emplace + STC_DEF _m_result + _c_MEMB(_emplace_or_assign)(Self* self, _m_keyraw rkey, _m_rmapped rmapped) { + _m_result _res = _c_MEMB(_insert_entry_)(self, rkey); + if (_res.inserted) + _res.ref->first = i_keyfrom(rkey); + else { + if (_res.ref == NULL) return _res; + i_valdrop((&_res.ref->second)); + } + _res.ref->second = i_valfrom(rmapped); + return _res; + } + #endif // !i_no_emplace +#endif // !_i_is_map + +STC_DEF _m_value* +_c_MEMB(_find_it)(const Self* self, _m_keyraw rkey, _m_iter* out) { + int32_t tn = self->root; + _m_node *d = out->_d = self->nodes; + out->_top = 0; + while (tn) { + int c; const _m_keyraw _raw = i_keytoraw(_i_keyref(&d[tn].value)); + if ((c = i_cmp((&_raw), (&rkey))) < 0) + tn = d[tn].link[1]; + else if (c > 0) + { out->_st[out->_top++] = tn; tn = d[tn].link[0]; } + else + { out->_tn = d[tn].link[1]; return (out->ref = &d[tn].value); } + } + return (out->ref = NULL); +} + +STC_DEF _m_iter +_c_MEMB(_lower_bound)(const Self* self, _m_keyraw rkey) { + _m_iter it; + _c_MEMB(_find_it)(self, rkey, &it); + if (it.ref == NULL && it._top != 0) { + int32_t tn = it._st[--it._top]; + it._tn = it._d[tn].link[1]; + it.ref = &it._d[tn].value; + } + return it; +} + +STC_DEF int32_t +_c_MEMB(_skew_)(_m_node *d, int32_t tn) { + if (tn != 0 && d[d[tn].link[0]].level == d[tn].level) { + int32_t tmp = d[tn].link[0]; + d[tn].link[0] = d[tmp].link[1]; + d[tmp].link[1] = tn; + tn = tmp; + } + return tn; +} + +STC_DEF int32_t +_c_MEMB(_split_)(_m_node *d, int32_t tn) { + if (d[d[d[tn].link[1]].link[1]].level == d[tn].level) { + int32_t tmp = d[tn].link[1]; + d[tn].link[1] = d[tmp].link[0]; + d[tmp].link[0] = tn; + tn = tmp; + ++d[tn].level; + } + return tn; +} + +STC_DEF int32_t +_c_MEMB(_insert_entry_i_)(Self* self, int32_t tn, const _m_keyraw* rkey, _m_result* _res) { + int32_t up[64], tx = tn; + _m_node* d = self->nodes; + int c, top = 0, dir = 0; + while (tx) { + up[top++] = tx; + const _m_keyraw _raw = i_keytoraw(_i_keyref(&d[tx].value)); + if ((c = i_cmp((&_raw), rkey)) == 0) + { _res->ref = &d[tx].value; return tn; } + dir = (c < 0); + tx = d[tx].link[dir]; + } + if ((tx = _c_MEMB(_new_node_)(self, 1)) == 0) + return 0; + d = self->nodes; + _res->ref = &d[tx].value; + _res->inserted = true; + if (top == 0) + return tx; + d[up[top - 1]].link[dir] = tx; + while (top--) { + if (top != 0) + dir = (d[up[top - 1]].link[1] == up[top]); + up[top] = _c_MEMB(_skew_)(d, up[top]); + up[top] = _c_MEMB(_split_)(d, up[top]); + if (top) + d[up[top - 1]].link[dir] = up[top]; + } + return up[0]; +} + +STC_DEF _m_result +_c_MEMB(_insert_entry_)(Self* self, _m_keyraw rkey) { + _m_result res = {0}; + int32_t tn = _c_MEMB(_insert_entry_i_)(self, self->root, &rkey, &res); + self->root = tn; + self->size += res.inserted; + return res; +} + +STC_DEF int32_t +_c_MEMB(_erase_r_)(Self *self, int32_t tn, const _m_keyraw* rkey, int *erased) { + _m_node *d = self->nodes; + if (tn == 0) + return 0; + _m_keyraw raw = i_keytoraw(_i_keyref(&d[tn].value)); + int32_t tx; int c = i_cmp((&raw), rkey); + if (c != 0) + d[tn].link[c < 0] = _c_MEMB(_erase_r_)(self, d[tn].link[c < 0], rkey, erased); + else { + if ((*erased)++ == 0) + _c_MEMB(_value_drop)(self, &d[tn].value); // drop first time, not second. + if (d[tn].link[0] && d[tn].link[1]) { + tx = d[tn].link[0]; + while (d[tx].link[1]) + tx = d[tx].link[1]; + d[tn].value = d[tx].value; /* move */ + raw = i_keytoraw(_i_keyref(&d[tn].value)); + d[tn].link[0] = _c_MEMB(_erase_r_)(self, d[tn].link[0], &raw, erased); + } else { /* unlink node */ + tx = tn; + tn = d[tn].link[ d[tn].link[0] == 0 ]; + /* move it to disposed nodes list */ + d[tx].link[1] = self->disp; + self->disp = tx; + } + } + tx = d[tn].link[1]; + if (d[d[tn].link[0]].level < d[tn].level - 1 || d[tx].level < d[tn].level - 1) { + if (d[tx].level > --d[tn].level) + d[tx].level = d[tn].level; + tn = _c_MEMB(_skew_)(d, tn); + tx = d[tn].link[1] = _c_MEMB(_skew_)(d, d[tn].link[1]); + d[tx].link[1] = _c_MEMB(_skew_)(d, d[tx].link[1]); + tn = _c_MEMB(_split_)(d, tn); + d[tn].link[1] = _c_MEMB(_split_)(d, d[tn].link[1]); + } + return tn; +} + +STC_DEF int +_c_MEMB(_erase)(Self* self, _m_keyraw rkey) { + int erased = 0; + int32_t root = _c_MEMB(_erase_r_)(self, self->root, &rkey, &erased); + if (erased == 0) + return 0; + self->root = root; + --self->size; + return 1; +} + +STC_DEF _m_iter +_c_MEMB(_erase_at)(Self* self, _m_iter it) { + _m_keyraw raw = i_keytoraw(_i_keyref(it.ref)); + _c_MEMB(_next)(&it); + if (it.ref != NULL) { + _m_keyraw nxt = i_keytoraw(_i_keyref(it.ref)); + _c_MEMB(_erase)(self, raw); + _c_MEMB(_find_it)(self, nxt, &it); + } else + _c_MEMB(_erase)(self, raw); + return it; +} + +STC_DEF _m_iter +_c_MEMB(_erase_range)(Self* self, _m_iter it1, _m_iter it2) { + if (it2.ref == NULL) { + while (it1.ref != NULL) + it1 = _c_MEMB(_erase_at)(self, it1); + return it1; + } + _m_key k1 = *_i_keyref(it1.ref), k2 = *_i_keyref(it2.ref); + _m_keyraw r1 = i_keytoraw((&k1)); + for (;;) { + if (memcmp(&k1, &k2, sizeof k1) == 0) + return it1; + _c_MEMB(_next)(&it1); + k1 = *_i_keyref(it1.ref); + _c_MEMB(_erase)(self, r1); + r1 = i_keytoraw((&k1)); + _c_MEMB(_find_it)(self, r1, &it1); + } +} + +#ifndef i_no_clone +STC_DEF int32_t +_c_MEMB(_clone_r_)(Self* self, _m_node* src, int32_t sn) { + if (sn == 0) + return 0; + int32_t tx, tn = _c_MEMB(_new_node_)(self, src[sn].level); + self->nodes[tn].value = _c_MEMB(_value_clone)(self, src[sn].value); + tx = _c_MEMB(_clone_r_)(self, src, src[sn].link[0]); self->nodes[tn].link[0] = tx; + tx = _c_MEMB(_clone_r_)(self, src, src[sn].link[1]); self->nodes[tn].link[1] = tx; + return tn; +} + +STC_DEF Self +_c_MEMB(_clone)(Self tree) { + Self out = tree; + out.root = out.disp = out.head = out.size = out.capacity = 0; + out.nodes = NULL; _c_MEMB(_reserve)(&out, tree.size); + out.root = _c_MEMB(_clone_r_)(&out, tree.nodes, tree.root); + return out; +} +#endif // !i_no_clone + +#ifndef i_no_emplace +STC_DEF _m_result +_c_MEMB(_emplace)(Self* self, _m_keyraw rkey _i_MAP_ONLY(, _m_rmapped rmapped)) { + _m_result res = _c_MEMB(_insert_entry_)(self, rkey); + if (res.inserted) { + *_i_keyref(res.ref) = i_keyfrom(rkey); + _i_MAP_ONLY(res.ref->second = i_valfrom(rmapped);) + } + return res; +} +#endif // i_no_emplace + +static void +_c_MEMB(_drop_r_)(Self* s, int32_t tn) { + if (tn != 0) { + _c_MEMB(_drop_r_)(s, s->nodes[tn].link[0]); + _c_MEMB(_drop_r_)(s, s->nodes[tn].link[1]); + _c_MEMB(_value_drop)(s, &s->nodes[tn].value); + } +} + +STC_DEF void +_c_MEMB(_drop)(const Self* cself) { + Self* self = (Self*)cself; + if (self->capacity != 0) { + _c_MEMB(_drop_r_)(self, self->root); + _i_free_n(self->nodes, self->capacity + 1); + } +} + +#endif // i_implement +#undef _i_is_set +#undef _i_is_map +#undef _i_sorted +#undef _i_keyref +#undef _i_MAP_ONLY +#undef _i_SET_ONLY +#include "sys/finalize.h" diff --git a/src/finchlite/codegen/stc/stc/sort.h b/src/finchlite/codegen/stc/stc/sort.h new file mode 100644 index 00000000..a2f95a57 --- /dev/null +++ b/src/finchlite/codegen/stc/stc/sort.h @@ -0,0 +1,109 @@ +/* MIT License + * + * Copyright (c) 2025 Tyge Løvset + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +/* Generic Quicksort in C, performs as fast as c++ std::sort(), and more robust. +template params: +#define i_key keytype - [required] (or use i_type, see below) +#define i_less(xp, yp) - optional less function. default: *xp < *yp +#define i_cmp(xp, yp) - alternative 3-way comparison. c_default_cmp(xp, yp) +#define T name - optional, defines {name}_sort(), else {i_key}s_sort(). +#define T name, key - alternative one-liner to define both i_type and i_key. + +// ex1: +#include +#define i_key int +#include + +int main(void) { + int nums[] = {23, 321, 5434, 25, 245, 1, 654, 33, 543, 21}; + + ints_sort(nums, c_arraylen(nums)); + + for (int i = 0; i < c_arraylen(nums); i++) + printf(" %d", nums[i]); + puts(""); + + isize idx = ints_binary_search(nums, 25, c_arraylen(nums)); + if (idx != c_NPOS) printf("found: %d\n", nums[idx]); + + idx = ints_lower_bound(nums, 200, c_arraylen(nums)); + if (idx != c_NPOS) printf("found lower 200: %d\n", nums[idx]); +} + +// ex2: Test on a deque !! +#include +#define T IDeq, int, (c_use_cmp) // enable comparison functions +#include + +int main(void) { + IDeq nums = c_make(IDeq, {5434, 25, 245, 1, 654, 33, 543, 21}); + IDeq_push_front(&nums, 23); + IDeq_push_front(&nums, 321); + + IDeq_sort(&nums); + + for (c_each (i, IDeq, nums)) + printf(" %d", *i.ref); + puts(""); + + isize idx = IDeq_binary_search(&nums, 25); + if (idx != c_NPOS) printf("found: %d\n", *IDeq_at(&nums, idx)); + + idx = IDeq_lower_bound(&nums, 200); + if (idx != c_NPOS) printf("found lower 200: %d\n", *IDeq_at(&nums, idx)); + + IDeq_drop(&nums); +} +*/ +#ifndef _i_template + #include "priv/linkage.h" + #include "common.h" + + #define _i_is_array + #if defined T && !defined i_type + #define i_type T + #endif + #if defined i_type && !defined i_key + #define Self c_GETARG(1, i_type) + #define i_key c_GETARG(2, i_type) + #elif defined i_type + #define Self i_type + #else + #define Self c_JOIN(i_key, s) + #endif + + typedef i_key Self; + typedef Self c_JOIN(Self, _value), c_JOIN(Self, _raw); + #define i_at(arr, idx) (&(arr)[idx]) + #define i_at_mut i_at + #include "priv/template.h" // IWYU pragma: keep +#endif + +#include "priv/sort_prv.h" + +#ifdef _i_is_array + #undef _i_is_array + #include "priv/linkage2.h" + #include "priv/template2.h" +#endif +#undef i_at +#undef i_at_mut diff --git a/src/finchlite/codegen/stc/stc/sortedmap.h b/src/finchlite/codegen/stc/stc/sortedmap.h new file mode 100644 index 00000000..f293122d --- /dev/null +++ b/src/finchlite/codegen/stc/stc/sortedmap.h @@ -0,0 +1,46 @@ +/* MIT License + * + * Copyright (c) 2025 Tyge Løvset + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +// Sorted map - implemented as an AA-tree (balanced binary tree). +/* +#include + +#define T Intmap, int, int +#include // sorted map of int + +int main(void) { + Intmap map = {0}; + Intmap_insert(&map, 5, 25); + Intmap_insert(&map, 8, 38); + Intmap_insert(&map, 3, 43); + Intmap_insert(&map, 5, 55); + + for (c_each_kv(k, v, Intmap, map)) + printf(" %d -> %d\n", *k, *v); + + Intmap_drop(&map); +} +*/ + +#define _i_prefix smap_ +#include "smap.h" diff --git a/src/finchlite/codegen/stc/stc/sortedset.h b/src/finchlite/codegen/stc/stc/sortedset.h new file mode 100644 index 00000000..17c847e9 --- /dev/null +++ b/src/finchlite/codegen/stc/stc/sortedset.h @@ -0,0 +1,47 @@ +/* MIT License + * + * Copyright (c) 2025 Tyge Løvset + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +// Sorted set - implemented as an AA-tree (balanced binary tree). +/* +#include + +#define T Intset, int +#include // sorted set of int + +int main(void) { + Intset set = {0}; + Intset_insert(&set, 5); + Intset_insert(&set, 8); + Intset_insert(&set, 3); + Intset_insert(&set, 5); + + for (c_each(k, Intset, set)) + printf(" %d\n", *k.ref); + + Intset_drop(&set); +} +*/ + +#define _i_prefix sset_ +#define _i_is_set +#include "smap.h" diff --git a/src/finchlite/codegen/stc/stc/sset.h b/src/finchlite/codegen/stc/stc/sset.h new file mode 100644 index 00000000..6558a1af --- /dev/null +++ b/src/finchlite/codegen/stc/stc/sset.h @@ -0,0 +1,46 @@ +/* MIT License + * + * Copyright (c) 2025 Tyge Løvset + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +// Sorted set - implemented as an AA-tree (balanced binary tree). +/* +#include + +#define T Intset, int +#include // sorted set of int + +int main(void) { + Intset s = {0}; + Intset_insert(&s, 5); + Intset_insert(&s, 8); + Intset_insert(&s, 3); + Intset_insert(&s, 5); + + for (c_each(k, Intset, s)) + printf("set %d\n", *k.ref); + Intset_drop(&s); +} +*/ + +#define _i_prefix sset_ +#define _i_is_set +#include "smap.h" diff --git a/src/finchlite/codegen/stc/stc/stack.h b/src/finchlite/codegen/stc/stc/stack.h new file mode 100644 index 00000000..871b0c75 --- /dev/null +++ b/src/finchlite/codegen/stc/stc/stack.h @@ -0,0 +1,285 @@ +/* MIT License + * + * Copyright (c) 2025 Tyge Løvset + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "priv/linkage.h" +#include "types.h" + +// Stack - a simplified vec type without linear search and insert/erase inside the stack. + +#ifndef STC_STACK_H_INCLUDED +#define STC_STACK_H_INCLUDED +#include "common.h" +#include +#endif // STC_STACK_H_INCLUDED + +#ifndef _i_prefix + #define _i_prefix stack_ +#endif +#include "priv/template.h" +#ifndef i_declared + #if c_NUMARGS(i_type) == 4 + #define i_capacity i_val + #endif + #ifdef i_capacity + #define i_no_clone + _c_DEFTYPES(declare_stack_fixed, Self, i_key, i_capacity); + #else + _c_DEFTYPES(_declare_stack, Self, i_key, _i_aux_def); + #endif +#endif +typedef i_keyraw _m_raw; + +#ifdef i_capacity + STC_INLINE void _c_MEMB(_init)(Self* news) + { news->size = 0; } + + STC_INLINE isize _c_MEMB(_capacity)(const Self* self) + { (void)self; return i_capacity; } + + STC_INLINE bool _c_MEMB(_reserve)(Self* self, isize n) + { (void)self; return n <= i_capacity; } +#else + STC_INLINE Self _c_MEMB(_move)(Self *self) { + Self m = *self; + self->capacity = self->size = 0; + self->data = NULL; + return m; + } + + STC_INLINE isize _c_MEMB(_capacity)(const Self* self) + { return self->capacity; } + + STC_INLINE bool _c_MEMB(_reserve)(Self* self, isize n) { + if (n > self->capacity || (n && n == self->size)) { + _m_value *d = (_m_value *)_i_realloc_n(self->data, self->capacity, n); + if (d == NULL) + return false; + self->data = d; + self->capacity = n; + } + return self->data != NULL; + } +#endif // i_capacity + +STC_INLINE void _c_MEMB(_clear)(Self* self) { + if (self->size == 0) return; + _m_value *p = self->data + self->size; + while (p-- != self->data) { i_keydrop(p); } + self->size = 0; +} + +STC_INLINE void _c_MEMB(_drop)(const Self* cself) { + Self* self = (Self*)cself; + _c_MEMB(_clear)(self); +#ifndef i_capacity + _i_free_n(self->data, self->capacity); +#endif +} + +STC_INLINE void _c_MEMB(_take)(Self *self, Self unowned) { + _c_MEMB(_drop)(self); + *self = unowned; +} + +STC_INLINE isize _c_MEMB(_size)(const Self* self) + { return self->size; } + +STC_INLINE bool _c_MEMB(_is_empty)(const Self* self) + { return !self->size; } + +STC_INLINE void _c_MEMB(_value_drop)(const Self* self, _m_value* val) + { (void)self; i_keydrop(val); } + +STC_INLINE _m_value* _c_MEMB(_append_uninit)(Self *self, isize n) { + isize len = self->size; + if (len + n >= _c_MEMB(_capacity)(self)) + if (!_c_MEMB(_reserve)(self, len*3/2 + n)) + return NULL; + self->size += n; + return self->data + len; +} + +STC_INLINE void _c_MEMB(_shrink_to_fit)(Self* self) + { _c_MEMB(_reserve)(self, self->size); } + +STC_INLINE const _m_value* _c_MEMB(_front)(const Self* self) + { return &self->data[0]; } +STC_INLINE _m_value* _c_MEMB(_front_mut)(Self* self) + { return &self->data[0]; } + +STC_INLINE const _m_value* _c_MEMB(_back)(const Self* self) + { return &self->data[self->size - 1]; } +STC_INLINE _m_value* _c_MEMB(_back_mut)(Self* self) + { return &self->data[self->size - 1]; } + +STC_INLINE const _m_value* _c_MEMB(_top)(const Self* self) + { return _c_MEMB(_back)(self); } +STC_INLINE _m_value* _c_MEMB(_top_mut)(Self* self) + { return _c_MEMB(_back_mut)(self); } + +STC_INLINE _m_value* _c_MEMB(_push)(Self* self, _m_value val) { + if (self->size == _c_MEMB(_capacity)(self)) + if (!_c_MEMB(_reserve)(self, self->size*3/2 + 4)) + return NULL; + _m_value* vp = self->data + self->size++; + *vp = val; return vp; +} + +STC_INLINE void _c_MEMB(_pop)(Self* self) + { c_assert(self->size); _m_value* p = &self->data[--self->size]; i_keydrop(p); } + +STC_INLINE _m_value _c_MEMB(_pull)(Self* self) + { c_assert(self->size); return self->data[--self->size]; } + +#ifndef _i_no_put +STC_INLINE void _c_MEMB(_put_n)(Self* self, const _m_raw* raw, isize n) + { while (n--) _c_MEMB(_push)(self, i_keyfrom((*raw))), ++raw; } +#endif + +#if !defined _i_aux_alloc && !defined i_capacity + STC_INLINE Self _c_MEMB(_init)(void) + { Self out = {0}; return out; } + + STC_INLINE Self _c_MEMB(_with_capacity)(isize cap) + { Self out = {_i_new_n(_m_value, cap), 0, cap}; return out; } + + STC_INLINE Self _c_MEMB(_with_size_uninit)(isize size) + { Self out = {_i_new_n(_m_value, size), size, size}; return out; } + + STC_INLINE Self _c_MEMB(_with_size)(isize size, _m_raw default_raw) { + Self out = {_i_new_n(_m_value, size), size, size}; + while (size) out.data[--size] = i_keyfrom(default_raw); + return out; + } + + #ifndef _i_no_put + STC_INLINE Self _c_MEMB(_from_n)(const _m_raw* raw, isize n) { + Self out = _c_MEMB(_with_capacity)(n); + _c_MEMB(_put_n)(&out, raw, n); return out; + } + #endif +#endif + +STC_INLINE const _m_value* _c_MEMB(_at)(const Self* self, isize idx) + { c_assert(c_uless(idx, self->size)); return self->data + idx; } + +STC_INLINE _m_value* _c_MEMB(_at_mut)(Self* self, isize idx) + { c_assert(c_uless(idx, self->size)); return self->data + idx; } + +#ifndef i_no_emplace +STC_INLINE _m_value* _c_MEMB(_emplace)(Self* self, _m_raw raw) + { return _c_MEMB(_push)(self, i_keyfrom(raw)); } +#endif // !i_no_emplace + +#ifndef i_no_clone +STC_INLINE Self _c_MEMB(_clone)(Self stk) { + Self out = stk, *self = &out; (void)self; // i_keyclone may use self via i_aux + out.data = NULL; out.size = out.capacity = 0; + _c_MEMB(_reserve)(&out, stk.size); + out.size = stk.size; + for (c_range(i, stk.size)) + out.data[i] = i_keyclone(stk.data[i]); + return out; +} + +STC_INLINE void _c_MEMB(_copy)(Self *self, const Self* other) { + if (self == other) return; + _c_MEMB(_clear)(self); + _c_MEMB(_reserve)(self, other->size); + for (c_range(i, other->size)) + self->data[self->size++] = i_keyclone((other->data[i])); +} + +STC_INLINE _m_value _c_MEMB(_value_clone)(const Self* self, _m_value val) + { (void)self; return i_keyclone(val); } + +STC_INLINE _m_raw _c_MEMB(_value_toraw)(const _m_value* val) + { return i_keytoraw(val); } +#endif // !i_no_clone + +// iteration + +STC_INLINE _m_iter _c_MEMB(_begin)(const Self* self) { + _m_iter it = {(_m_value*)self->data, (_m_value*)self->data}; + if (self->size) it.end += self->size; + else it.ref = NULL; + return it; +} + +STC_INLINE _m_iter _c_MEMB(_rbegin)(const Self* self) { + _m_iter it = {(_m_value*)self->data, (_m_value*)self->data}; + if (self->size) { it.ref += self->size - 1; it.end -= 1; } + else it.ref = NULL; + return it; +} + +STC_INLINE _m_iter _c_MEMB(_end)(const Self* self) + { (void)self; _m_iter it = {0}; return it; } + +STC_INLINE _m_iter _c_MEMB(_rend)(const Self* self) + { (void)self; _m_iter it = {0}; return it; } + +STC_INLINE void _c_MEMB(_next)(_m_iter* it) + { if (++it->ref == it->end) it->ref = NULL; } + +STC_INLINE void _c_MEMB(_rnext)(_m_iter* it) + { if (--it->ref == it->end) it->ref = NULL; } + +STC_INLINE _m_iter _c_MEMB(_advance)(_m_iter it, size_t n) + { if ((it.ref += n) >= it.end) it.ref = NULL ; return it; } + +STC_INLINE isize _c_MEMB(_index)(const Self* self, _m_iter it) + { return (it.ref - self->data); } + +STC_INLINE void _c_MEMB(_adjust_end_)(Self* self, isize n) + { self->size += n; } + +#if defined _i_has_cmp +#include "priv/sort_prv.h" +#endif // _i_has_cmp + +#if defined _i_has_eq +STC_INLINE _m_iter _c_MEMB(_find_in)(const Self* self, _m_iter i1, _m_iter i2, _m_raw raw) { + (void)self; + const _m_value* p2 = i2.ref ? i2.ref : i1.end; + for (; i1.ref != p2; ++i1.ref) { + const _m_raw r = i_keytoraw(i1.ref); + if (i_eq((&raw), (&r))) + return i1; + } + i2.ref = NULL; + return i2; +} + +STC_INLINE _m_iter _c_MEMB(_find)(const Self* self, _m_raw raw) + { return _c_MEMB(_find_in)(self, _c_MEMB(_begin)(self), _c_MEMB(_end)(self), raw); } + +STC_INLINE bool _c_MEMB(_eq)(const Self* self, const Self* other) { + if (self->size != other->size) return false; + for (isize i = 0; i < self->size; ++i) { + const _m_raw _rx = i_keytoraw((self->data+i)), _ry = i_keytoraw((other->data+i)); + if (!(i_eq((&_rx), (&_ry)))) return false; + } + return true; +} +#endif // _i_has_eq +#include "sys/finalize.h" diff --git a/src/finchlite/codegen/stc/stc/sys/crange.h b/src/finchlite/codegen/stc/stc/sys/crange.h new file mode 100644 index 00000000..c71cef61 --- /dev/null +++ b/src/finchlite/codegen/stc/stc/sys/crange.h @@ -0,0 +1,118 @@ +/* MIT License + * + * Copyright (c) 2025 Tyge Løvset + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. +*/ +/* +#include +#include + +int main(void) +{ + crange r1 = crange_make(80, 90); + for (c_each(i, crange, r1)) + printf(" %d", *i.ref); + puts(""); + + c_filter(crange, c_iota(100, INT_MAX, 10), true + && c_flt_skip(25) + && c_flt_take(3) + && printf(" %d", *value) + ); + puts(""); +} +*/ +// IWYU pragma: private, include "stc/algorithm.h" +#ifndef STC_CRANGE_H_INCLUDED +#define STC_CRANGE_H_INCLUDED + +#include "../priv/linkage.h" +#include "../common.h" + +// crange: isize range ----- + +typedef isize crange_value; +typedef struct { crange_value start, end, step, value; } crange; +typedef struct { crange_value *ref, end, step; } crange_iter; + +STC_INLINE crange crange_make_3(crange_value start, crange_value stop, crange_value step) + { crange r = {start, stop - (step > 0), step}; return r; } + +#define crange_make(...) c_MACRO_OVERLOAD(crange_make, __VA_ARGS__) +#define crange_make_1(stop) crange_make_3(0, stop, 1) // NB! arg is stop +#define crange_make_2(start, stop) crange_make_3(start, stop, 1) + +STC_INLINE crange_iter crange_begin(crange* self) { + self->value = self->start; + crange_iter it = {&self->value, self->end, self->step}; + return it; +} + +STC_INLINE void crange_next(crange_iter* it) { + if ((it->step > 0) == ((*it->ref += it->step) > it->end)) + it->ref = NULL; +} + +STC_INLINE crange_iter crange_advance(crange_iter it, size_t n) { + if ((it.step > 0) == ((*it.ref += it.step*(isize)n) > it.end)) + it.ref = NULL; + return it; +} + +// iota: c++-like std::iota, use in iterations on-the-fly ----- +// Note: c_iota() does not compile with c++, crange does. +#define c_iota(...) c_MACRO_OVERLOAD(c_iota, __VA_ARGS__) +#define c_iota_1(start) c_iota_3(start, INTPTR_MAX, 1) // NB! arg is start. +#define c_iota_2(start, stop) c_iota_3(start, stop, 1) +#define c_iota_3(start, stop, step) ((crange[]){crange_make_3(start, stop, step)})[0] + + +// crange32 ----- + +typedef int32_t crange32_value; +typedef struct { crange32_value start, end, step, value; } crange32; +typedef struct { crange32_value *ref, end, step; } crange32_iter; + +STC_INLINE crange32 crange32_make_3(crange32_value start, crange32_value stop, crange32_value step) + { crange32 r = {start, stop - (step > 0), step}; return r; } + +#define crange32_make(...) c_MACRO_OVERLOAD(crange32_make, __VA_ARGS__) +#define crange32_make_1(stop) crange32_make_3(0, stop, 1) // NB! arg is stop +#define crange32_make_2(start, stop) crange32_make_3(start, stop, 1) + +STC_INLINE crange32_iter crange32_begin(crange32* self) { + self->value = self->start; + crange32_iter it = {&self->value, self->end, self->step}; + return it; +} + +STC_INLINE void crange32_next(crange32_iter* it) { + if ((it->step > 0) == ((*it->ref += it->step) > it->end)) + it->ref = NULL; +} + +STC_INLINE crange32_iter crange32_advance(crange32_iter it, uint32_t n) { + if ((it.step > 0) == ((*it.ref += it.step*(int32_t)n) > it.end)) + it.ref = NULL; + return it; +} + +#include "../priv/linkage2.h" +#endif // STC_CRANGE_H_INCLUDE diff --git a/src/finchlite/codegen/stc/stc/sys/filter.h b/src/finchlite/codegen/stc/stc/sys/filter.h new file mode 100644 index 00000000..512e68db --- /dev/null +++ b/src/finchlite/codegen/stc/stc/sys/filter.h @@ -0,0 +1,185 @@ +/* MIT License + * + * Copyright (c) 2025 Tyge Løvset + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. +*/ +/* +#include +#define T Vec, int +#include +#include + +int main(void) +{ + Vec vec = c_make(Vec, {1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 9, 10, 11, 12, 5}); + + c_filter(Vec, vec, true + && c_flt_skipwhile(*value < 3) // skip leading values < 3 + && (*value & 1) == 1 // then use odd values only + && c_flt_map(*value * 2) // multiply by 2 + && c_flt_takewhile(*value < 20) // stop if mapped *value >= 20 + && printf(" %d", *value) // print value + ); + // 6 10 14 2 6 18 + puts(""); + Vec_drop(&vec); +} +*/ +// IWYU pragma: private, include "stc/algorithm.h" +#ifndef STC_FILTER_H_INCLUDED +#define STC_FILTER_H_INCLUDED + +#include "../common.h" + +// ------- c_filter -------- +#define c_flt_take(n) _flt_take(&fltbase, n) +#define c_flt_skip(n) (c_flt_counter() > (n)) +#define c_flt_takewhile(pred) _flt_takewhile(&fltbase, pred) +#define c_flt_skipwhile(pred) (fltbase.sb[fltbase.sb_top++] |= !(pred)) +#define c_flt_counter() (++fltbase.sn[++fltbase.sn_top]) +#define c_flt_getcount() (fltbase.sn[fltbase.sn_top]) +#define c_flt_map(expr) (_mapped = (expr), value = &_mapped) +#define c_flt_src _it.ref + +#define c_filter(C, cnt, pred) \ + _c_filter(C, C##_begin(&cnt), _, pred) + +#define c_filter_from(C, start, pred) \ + _c_filter(C, start, _, pred) + +#define c_filter_reverse(C, cnt, pred) \ + _c_filter(C, C##_rbegin(&cnt), _r, pred) + +#define c_filter_reverse_from(C, start, pred) \ + _c_filter(C, start, _r, pred) + +#define _c_filter(C, start, rev, pred) do { \ + struct _flt_base fltbase = {0}; \ + C##_iter _it = start; \ + C##_value *value = _it.ref, _mapped = {0}; \ + for ((void)_mapped ; !fltbase.done & (_it.ref != NULL) ; \ + C##rev##next(&_it), value = _it.ref, fltbase.sn_top=0, fltbase.sb_top=0) \ + (void)(pred); \ +} while (0) + +// ------- c_filter_zip -------- +#define c_filter_zip(...) c_MACRO_OVERLOAD(c_filter_zip, __VA_ARGS__) +#define c_filter_zip_4(C, cnt1, cnt2, pred) \ + c_filter_zip_5(C, cnt1, C, cnt2, pred) +#define c_filter_zip_5(C1, cnt1, C2, cnt2, pred) \ + _c_filter_zip(C1, C1##_begin(&cnt1), C2, C2##_begin(&cnt2), _, pred) + +#define c_filter_reverse_zip(...) c_MACRO_OVERLOAD(c_filter_reverse_zip, __VA_ARGS__) +#define c_filter_reverse_zip_4(C, cnt1, cnt2, pred) \ + c_filter_reverse_zip_5(C, cnt1, C, cnt2, pred) +#define c_filter_reverse_zip_5(C1, cnt1, C2, cnt2, pred) \ + _c_filter_zip(C1, C1##_rbegin(&cnt1), C2, C2##_rbegin(&cnt2), _r, pred) + +#define c_filter_pairwise(C, cnt, pred) \ + _c_filter_zip(C, C##_begin(&cnt), C, C##_advance(_it1, 1), _, pred) + +#define c_flt_map1(expr) (_mapped1 = (expr), value1 = &_mapped1) +#define c_flt_map2(expr) (_mapped2 = (expr), value2 = &_mapped2) +#define c_flt_src1 _it1.ref +#define c_flt_src2 _it2.ref + +#define _c_filter_zip(C1, start1, C2, start2, rev, pred) do { \ + struct _flt_base fltbase = {0}; \ + C1##_iter _it1 = start1; \ + C2##_iter _it2 = start2; \ + C1##_value* value1 = _it1.ref, _mapped1; (void)_mapped1; \ + C2##_value* value2 = _it2.ref, _mapped2; (void)_mapped2; \ + for (; !fltbase.done & (_it1.ref != NULL) & (_it2.ref != NULL); \ + C1##rev##next(&_it1), value1 = _it1.ref, C2##rev##next(&_it2), value2 = _it2.ref, \ + fltbase.sn_top=0, fltbase.sb_top=0) \ + (void)(pred); \ +} while (0) + +// ------- c_ffilter -------- +// c_ffilter allows to execute imperative statements for each element +// in a for-loop, e.g., calling nested generic statements instead +// of defining a function/expression for it: +/* + Vec vec = ..., vec2 = ...; + for (c_ffilter(i, Vec, vec, true + && c_fflt_skipwhile(i, *i.ref < 3) // skip leading values < 3 + && (*i.ref & 1) == 1 // then use odd values only + && c_fflt_map(i, *i.ref * 2) // multiply by 2 + && c_fflt_takewhile(i, *i.ref < 20) // stop if mapped *i.ref >= 20 + )){ + c_eraseremove_if(Vec, &vec2, *value == *i.ref); + } +*/ +#define c_fflt_take(i, n) _flt_take(&i.base, n) +#define c_fflt_skip(i, n) (c_fflt_counter(i) > (n)) +#define c_fflt_takewhile(i, pred) _flt_takewhile(&i.base, pred) +#define c_fflt_skipwhile(i, pred) (i.base.sb[i.base.sb_top++] |= !(pred)) +#define c_fflt_counter(i) (++i.base.sn[++i.base.sn_top]) +#define c_fflt_getcount(i) (i.base.sn[i.base.sn_top]) +#define c_fflt_map(i, expr) (i.mapped = (expr), i.ref = &i.mapped) +#define c_fflt_src(i) i.iter.ref + +#define c_forfilter(...) for (c_ffilter(__VA_ARGS__)) +#define c_forfilter_from(...) for (c_ffilter_from(__VA_ARGS__)) +#define c_forfilter_reverse(...) for (c_ffilter_reverse(__VA_ARGS__)) +#define c_forfilter_reverse_from(...) for (c_ffilter_reverse_from(__VA_ARGS__)) + +#define c_ffilter(i, C, cnt, pred) \ + _c_ffilter(i, C, C##_begin(&cnt), _, pred) + +#define c_ffilter_from(i, C, start, pred) \ + _c_ffilter(i, C, start, _, pred) + +#define c_ffilter_reverse(i, C, cnt,pred) \ + _c_ffilter(i, C, C##_rbegin(&cnt), _r, pred) + +#define c_ffilter_reverse_from(i, C, start, pred) \ + _c_ffilter(i, C, start, _r, pred) + +#define _c_ffilter(i, C, start, rev, pred) \ + struct {C##_iter iter; C##_value *ref, mapped; struct _flt_base base;} \ + i = {.iter=start, .ref=i.iter.ref} ; !i.base.done & (i.iter.ref != NULL) ; \ + C##rev##next(&i.iter), i.ref = i.iter.ref, i.base.sn_top=0, i.base.sb_top=0) \ + if (!(pred)) ; else if (1 + +// ------------------------ private ------------------------- +#ifndef c_NFILTERS +#define c_NFILTERS 20 +#endif + +struct _flt_base { + uint8_t sn_top, sb_top; + bool done, sb[c_NFILTERS]; + uint32_t sn[c_NFILTERS]; +}; + +static inline bool _flt_take(struct _flt_base* base, uint32_t n) { + uint32_t k = ++base->sn[++base->sn_top]; + base->done |= (k >= n); + return n > 0; +} + +static inline bool _flt_takewhile(struct _flt_base* base, bool pred) { + bool skip = (base->sb[base->sb_top++] |= !pred); + base->done |= skip; + return !skip; +} + +#endif // STC_FILTER_H_INCLUDED diff --git a/src/finchlite/codegen/stc/stc/sys/finalize.h b/src/finchlite/codegen/stc/stc/sys/finalize.h new file mode 100644 index 00000000..e7271fa4 --- /dev/null +++ b/src/finchlite/codegen/stc/stc/sys/finalize.h @@ -0,0 +1,5 @@ +#ifndef i_extend + #include "../priv/linkage2.h" + #include "../priv/template2.h" +#endif +#undef i_extend diff --git a/src/finchlite/codegen/stc/stc/sys/sumtype.h b/src/finchlite/codegen/stc/stc/sys/sumtype.h new file mode 100644 index 00000000..747a1064 --- /dev/null +++ b/src/finchlite/codegen/stc/stc/sys/sumtype.h @@ -0,0 +1,172 @@ +/* MIT License + * + * Copyright (c) 2025 Tyge Løvset + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +/* +// https://stackoverflow.com/questions/70935435/how-to-create-variants-in-rust +#include +#include +#include + +c_union (Action, + (ActionSpeak, cstr), + (ActionQuit, bool), + (ActionRunFunc, struct { + int32_t (*func)(int32_t, int32_t); + int32_t v1, v2; + }) +); + +void Action_drop(Action* self) { + if (c_is(self, ActionSpeak, s)) + cstr_drop(s); +} + +void action(Action* action) { + c_when (action) { + c_is(ActionSpeak, s) { + printf("Asked to speak: %s\n", cstr_str(s)); + } + c_is(ActionQuit) { + printf("Asked to quit!\n"); + } + c_is(ActionRunFunc, r) { + int32_t res = r->func(r->v1, r->v2); + printf("v1: %d, v2: %d, res: %d\n", r->v1, r->v2, res); + } + c_otherwise assert(!"no match"); + } +} + +int32_t add(int32_t a, int32_t b) { + return a + b; +} + +int main(void) { + Action act1 = c_variant(ActionSpeak, cstr_from("Hello")); + Action act2 = c_variant(ActionQuit, 1); + Action act3 = c_variant(ActionRunFunc, {add, 5, 6}); + + action(&act1); + action(&act2); + action(&act3); + + c_drop(Action, &act1, &act2, &act3); +} +*/ +#ifndef STC_SUMTYPE_H_INCLUDED +#define STC_SUMTYPE_H_INCLUDED + +#include "../common.h" + +#define _c_EMPTY() +#define _c_LOOP_INDIRECTION() c_LOOP +#define _c_LOOP_END_1 ,_c_LOOP1 +#define _c_LOOP0(f,T,x,...) f c_EXPAND((T, c_EXPAND x)) _c_LOOP_INDIRECTION _c_EMPTY()()(f,T,__VA_ARGS__) +#define _c_LOOP1(...) +#define _c_CHECK(x,...) c_TUPLE_AT_1(__VA_ARGS__,x,) +#define _c_E0(...) __VA_ARGS__ +#define _c_E1(...) _c_E0(_c_E0(_c_E0(_c_E0(_c_E0(_c_E0(__VA_ARGS__)))))) +#define _c_E2(...) _c_E1(_c_E1(_c_E1(_c_E1(_c_E1(_c_E1(__VA_ARGS__)))))) +#define c_EVAL(...) _c_E2(_c_E2(_c_E2(__VA_ARGS__))) // currently supports up to 130 variants +#define c_LOOP(f,T,x,...) _c_CHECK(_c_LOOP0, c_JOIN(_c_LOOP_END_, c_NUMARGS(c_EXPAND x)))(f,T,x,__VA_ARGS__) + + +#define _c_enum_1(x,...) (x=__LINE__*1000, __VA_ARGS__) +#define _c_vartuple_tag(T, Tag, ...) Tag, +#define _c_vartuple_type(T, Tag, ...) typedef __VA_ARGS__ Tag##_type; typedef T Tag##_sumtype; +#define _c_vartuple_var(T, Tag, ...) struct { enum enum_##T tag; Tag##_type get; } Tag; + +#define c_union(T, ...) \ + typedef union T T; \ + enum enum_##T { c_EVAL(c_LOOP(_c_vartuple_tag, T, _c_enum_1 __VA_ARGS__, (0),)) }; \ + c_EVAL(c_LOOP(_c_vartuple_type, T, __VA_ARGS__, (0),)) \ + union T { \ + struct { enum enum_##T tag; } _any_; \ + c_EVAL(c_LOOP(_c_vartuple_var, T, __VA_ARGS__, (0),)) \ + } +#define c_sumtype c_union + +#if defined STC_HAS_TYPEOF && STC_HAS_TYPEOF + #define c_when(varptr) \ + for (__typeof__(varptr) _vp1 = (varptr); _vp1; _vp1 = NULL) \ + switch (_vp1->_any_.tag) + + #define c_is_2(Tag, x) \ + break; case Tag: \ + for (__typeof__(_vp1->Tag.get)* x = &_vp1->Tag.get; x; x = NULL) + + #define c_is_3(varptr, Tag, x) \ + false) ; else for (__typeof__(varptr) _vp2 = (varptr); _vp2; _vp2 = NULL) \ + if (c_is_variant(_vp2, Tag)) \ + for (__typeof__(_vp2->Tag.get) *x = &_vp2->Tag.get; x; x = NULL +#else + typedef union { struct { int tag; } _any_; } _c_any_variant; + #define c_when(varptr) \ + for (_c_any_variant* _vp1 = (_c_any_variant *)(varptr); \ + _vp1; _vp1 = NULL, (void)sizeof((varptr)->_any_.tag)) \ + switch (_vp1->_any_.tag) + + #define c_is_2(Tag, x) \ + break; case Tag: \ + for (Tag##_type *x = &((Tag##_sumtype *)_vp1)->Tag.get; x; x = NULL) + + #define c_is_3(varptr, Tag, x) \ + false) ; else for (Tag##_sumtype* _vp2 = c_const_cast(Tag##_sumtype*, varptr); _vp2; _vp2 = NULL) \ + if (c_is_variant(_vp2, Tag)) \ + for (Tag##_type *x = &_vp2->Tag.get; x; x = NULL +#endif + +// Handling multiple tags with different payloads: +#define c_is(...) c_MACRO_OVERLOAD(c_is, __VA_ARGS__) +#define c_is_1(Tag) \ + break; case Tag: + +#define c_or_is(Tag) \ + ; case Tag: + +// Type checked multiple tags with same payload: +#define c_is_same(...) c_MACRO_OVERLOAD(c_is_same, __VA_ARGS__) +#define _c_chk(Tag1, Tag2) \ + case 1 ? Tag1 : sizeof((Tag1##_type*)0 == (Tag2##_type*)0): +#define c_is_same_2(Tag1, Tag2) \ + break; _c_chk(Tag1, Tag2) case Tag2: +#define c_is_same_3(Tag1, Tag2, Tag3) \ + break; _c_chk(Tag1, Tag2) _c_chk(Tag2, Tag3) case Tag3: +#define c_is_same_4(Tag1, Tag2, Tag3, Tag4) \ + break; _c_chk(Tag1, Tag2) _c_chk(Tag2, Tag3) _c_chk(Tag3, Tag4) case Tag4: + +#define c_otherwise \ + break; default: + +#define c_variant(Tag, ...) \ + (c_literal(Tag##_sumtype){.Tag={.tag=Tag, .get=__VA_ARGS__}}) + +#define c_is_variant(varptr, Tag) \ + ((varptr)->Tag.tag == Tag) + +#define c_get_if(varptr, Tag) \ + (c_is_variant(varptr, Tag) ? &(varptr)->Tag.get : NULL) + +#define c_variant_id(varptr) \ + ((int)(varptr)->_any_.tag) + +#endif // STC_SUMTYPE_H_INCLUDED diff --git a/src/finchlite/codegen/stc/stc/sys/utility.h b/src/finchlite/codegen/stc/stc/sys/utility.h new file mode 100644 index 00000000..b4ed1b64 --- /dev/null +++ b/src/finchlite/codegen/stc/stc/sys/utility.h @@ -0,0 +1,188 @@ +/* MIT License + * + * Copyright (c) 2025 Tyge Løvset + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. +*/ +// IWYU pragma: private, include "stc/algorithm.h" +#ifndef STC_UTILITY_H_INCLUDED +#define STC_UTILITY_H_INCLUDED + +// -------------------------------- +// c_find_if, c_find_reverse_if +// -------------------------------- + +#define c_find_if(...) c_MACRO_OVERLOAD(c_find_if, __VA_ARGS__) +#define c_find_if_4(C, cnt, outit_ptr, pred) \ + _c_find(C, C##_begin(&cnt), NULL, _, outit_ptr, pred) + +#define c_find_if_5(C, start, finish, outit_ptr, pred) \ + _c_find(C, start, (finish).ref, _, outit_ptr, pred) + +#define c_find_reverse_if(...) c_MACRO_OVERLOAD(c_find_reverse_if, __VA_ARGS__) +#define c_find_reverse_if_4(C, cnt, outit_ptr, pred) \ + _c_find(C, C##_rbegin(&cnt), NULL, _r, outit_ptr, pred) + +#define c_find_reverse_if_5(C, rstart, rfinish, outit_ptr, pred) \ + _c_find(C, rstart, (rfinish).ref, _r, outit_ptr, pred) + +// private +#define _c_find(C, start, endref, rev, outit_ptr, pred) do { \ + C##_iter* _out = outit_ptr; \ + const C##_value *value, *_endref = endref; \ + for (*_out = start; (value = _out->ref) != _endref; C##rev##next(_out)) \ + if (pred) goto c_JOIN(findif_, __LINE__); \ + _out->ref = NULL; c_JOIN(findif_, __LINE__):; \ +} while (0) + +// -------------------------------- +// c_reverse +// -------------------------------- + +#define c_reverse_array(array, n) do { \ + typedef struct { char d[sizeof 0[array]]; } _etype; \ + _etype* _arr = (_etype *)(array); \ + for (isize _i = 0, _j = (n) - 1; _i < _j; ++_i, --_j) \ + c_swap(_arr + _i, _arr + _j); \ +} while (0) + +// Compiles with vec, stack, and deque, and cspan container types: +#define c_reverse(CntType, self) do { \ + CntType* _self = self; \ + for (isize _i = 0, _j = CntType##_size(_self) - 1; _i < _j; ++_i, --_j) \ + c_swap(CntType##_at_mut(_self, _i), CntType##_at_mut(_self, _j)); \ +} while (0) + +// -------------------------------- +// c_erase_if +// -------------------------------- + +// Use with: list, hashmap, hashset, sortedmap, sortedset: +#define c_erase_if(C, cnt_ptr, pred) do { \ + C* _cnt = cnt_ptr; \ + const C##_value* value; \ + for (C##_iter _it = C##_begin(_cnt); (value = _it.ref); ) { \ + if (pred) _it = C##_erase_at(_cnt, _it); \ + else C##_next(&_it); \ + } \ +} while (0) + +// -------------------------------- +// c_eraseremove_if +// -------------------------------- + +// Use with: stack, vec, deque, queue: +#define c_eraseremove_if(C, cnt_ptr, pred) do { \ + C* _cnt = cnt_ptr; \ + isize _n = 0; \ + const C##_value* value; \ + C##_iter _i, _it = C##_begin(_cnt); \ + while ((value = _it.ref) && !(pred)) \ + C##_next(&_it); \ + for (_i = _it; (value = _it.ref); C##_next(&_it)) { \ + if (pred) C##_value_drop(_cnt, _it.ref), ++_n; \ + else *_i.ref = *_it.ref, C##_next(&_i); \ + } \ + C##_adjust_end_(_cnt, -_n); \ +} while (0) + +// -------------------------------- +// c_copy_to, c_copy_if +// -------------------------------- + +#define c_copy_to(...) c_MACRO_OVERLOAD(c_copy_to, __VA_ARGS__) +#define c_copy_to_3(C, outcnt_ptr, cnt) \ + _c_copy_if(C, outcnt_ptr, _, C, cnt, true) + +#define c_copy_to_4(C_out, outcnt_ptr, C, cnt) \ + _c_copy_if(C_out, outcnt_ptr, _, C, cnt, true) + +#define c_copy_if(...) c_MACRO_OVERLOAD(c_copy_if, __VA_ARGS__) +#define c_copy_if_4(C, outcnt_ptr, cnt, pred) \ + _c_copy_if(C, outcnt_ptr, _, C, cnt, pred) + +#define c_copy_if_5(C_out, outcnt_ptr, C, cnt, pred) \ + _c_copy_if(C_out, outcnt_ptr, _, C, cnt, pred) + +// private +#define _c_copy_if(C_out, outcnt_ptr, rev, C, cnt, pred) do { \ + C_out *_out = outcnt_ptr; \ + C _cnt = cnt; \ + const C##_value* value; \ + for (C##_iter _it = C##rev##begin(&_cnt); (value = _it.ref); C##rev##next(&_it)) \ + if (pred) C_out##_push(_out, C_out##_value_clone(_out, *_it.ref)); \ +} while (0) + +// -------------------------------- +// c_all_of, c_any_of, c_none_of +// -------------------------------- + +#define c_all_of(C, cnt, outbool_ptr, pred) do { \ + C##_iter _it; \ + c_find_if_4(C, cnt, &_it, !(pred)); \ + *(outbool_ptr) = _it.ref == NULL; \ +} while (0) + +#define c_any_of(C, cnt, outbool_ptr, pred) do { \ + C##_iter _it; \ + c_find_if_4(C, cnt, &_it, pred); \ + *(outbool_ptr) = _it.ref != NULL; \ +} while (0) + +#define c_none_of(C, cnt, outbool_ptr, pred) do { \ + C##_iter _it; \ + c_find_if_4(C, cnt, &_it, pred); \ + *(outbool_ptr) = _it.ref == NULL; \ +} while (0) + +// -------------------------------- +// c_min, c_max, c_min_n, c_max_n +// -------------------------------- +#define _c_minmax_call(fn, T, ...) \ + fn(c_make_array(T, {__VA_ARGS__}), c_sizeof((T[]){__VA_ARGS__})/c_sizeof(T)) + +#define c_min(...) _c_minmax_call(c_min_n, isize, __VA_ARGS__) +#define c_umin(...) _c_minmax_call(c_umin_n, size_t, __VA_ARGS__) +#define c_min32(...) _c_minmax_call(c_min32_n, int32_t, __VA_ARGS__) +#define c_fmin(...) _c_minmax_call(c_fmin_n, float, __VA_ARGS__) +#define c_dmin(...) _c_minmax_call(c_dmin_n, double, __VA_ARGS__) +#define c_max(...) _c_minmax_call(c_max_n, isize, __VA_ARGS__) +#define c_umax(...) _c_minmax_call(c_umax_n, size_t, __VA_ARGS__) +#define c_max32(...) _c_minmax_call(c_max32_n, int32_t, __VA_ARGS__) +#define c_fmax(...) _c_minmax_call(c_fmax_n, float, __VA_ARGS__) +#define c_dmax(...) _c_minmax_call(c_dmax_n, double, __VA_ARGS__) + +#define _c_minmax_def(fn, T, opr) \ + static inline T fn(const T a[], isize n) { \ + T x = a[0]; \ + for (isize i = 1; i < n; ++i) if (a[i] opr x) x = a[i]; \ + return x; \ + } +_c_minmax_def(c_min32_n, int32_t, <) +_c_minmax_def(c_min_n, isize, <) +_c_minmax_def(c_umin_n, size_t, <) +_c_minmax_def(c_fmin_n, float, <) +_c_minmax_def(c_dmin_n, double, <) +_c_minmax_def(c_max32_n, int32_t, >) +_c_minmax_def(c_max_n, isize, >) +_c_minmax_def(c_umax_n, size_t, >) +_c_minmax_def(c_fmax_n, float, >) +_c_minmax_def(c_dmax_n, double, >) + +#endif // STC_UTILITY_H_INCLUDED diff --git a/src/finchlite/codegen/stc/stc/types.h b/src/finchlite/codegen/stc/stc/types.h new file mode 100644 index 00000000..73af610c --- /dev/null +++ b/src/finchlite/codegen/stc/stc/types.h @@ -0,0 +1,223 @@ +/* MIT License + * + * Copyright (c) 2025 Tyge Løvset + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef STC_TYPES_H_INCLUDED +#define STC_TYPES_H_INCLUDED + +#include +#include +#include + +#define declare_rc(C, KEY) declare_arc(C, KEY) +#define declare_list(C, KEY) _declare_list(C, KEY,) +#define declare_stack(C, KEY) _declare_stack(C, KEY,) +#define declare_vec(C, KEY) _declare_stack(C, KEY,) +#define declare_pqueue(C, KEY) _declare_stack(C, KEY,) +#define declare_queue(C, KEY) _declare_queue(C, KEY,) +#define declare_deque(C, KEY) _declare_queue(C, KEY,) +#define declare_hashmap(C, KEY, VAL) _declare_htable(C, KEY, VAL, c_true, c_false,) +#define declare_hashset(C, KEY) _declare_htable(C, KEY, KEY, c_false, c_true,) +#define declare_sortedmap(C, KEY, VAL) _declare_aatree(C, KEY, VAL, c_true, c_false,) +#define declare_sortedset(C, KEY) _declare_aatree(C, KEY, KEY, c_false, c_true,) + +#define declare_list_aux(C, KEY, AUX) _declare_list(C, KEY, AUX aux;) +#define declare_stack_aux(C, KEY, AUX) _declare_stack(C, KEY, AUX aux;) +#define declare_vec_aux(C, KEY, AUX) _declare_stack(C, KEY, AUX aux;) +#define declare_pqueue_aux(C, KEY, AUX) _declare_stack(C, KEY, AUX aux;) +#define declare_queue_aux(C, KEY, AUX) _declare_queue(C, KEY, AUX aux;) +#define declare_deque_aux(C, KEY, AUX) _declare_queue(C, KEY, AUX aux;) +#define declare_hashmap_aux(C, KEY, VAL, AUX) _declare_htable(C, KEY, VAL, c_true, c_false, AUX aux;) +#define declare_hashset_aux(C, KEY, AUX) _declare_htable(C, KEY, KEY, c_false, c_true, AUX aux;) +#define declare_sortedmap_aux(C, KEY, VAL, AUX) _declare_aatree(C, KEY, VAL, c_true, c_false, AUX aux;) +#define declare_sortedset_aux(C, KEY, AUX) _declare_aatree(C, KEY, KEY, c_false, c_true, AUX aux;) + +// csview : non-null terminated string view +typedef const char csview_value; +typedef struct csview { + csview_value* buf; + ptrdiff_t size; +} csview; + +typedef union { + csview_value* ref; + csview chr; + struct { csview chr; csview_value* end; } u8; +} csview_iter; + +#define c_sv(...) c_MACRO_OVERLOAD(c_sv, __VA_ARGS__) +#define c_sv_1(literal) c_sv_2(literal, c_litstrlen(literal)) +#define c_sv_2(str, n) (c_literal(csview){str, n}) +#define c_svfmt "%.*s" +#define c_svarg(sv) (int)(sv).size, (sv).buf // printf(c_svfmt "\n", c_svarg(sv)); + +// zsview : zero-terminated string view +typedef csview_value zsview_value; +typedef struct zsview { + zsview_value* str; + ptrdiff_t size; +} zsview; + +typedef union { + zsview_value* ref; + csview chr; +} zsview_iter; + +#define c_zv(literal) (c_literal(zsview){literal, c_litstrlen(literal)}) + +// cstr : zero-terminated owning string (short string optimized - sso) +typedef char cstr_value; +typedef struct { cstr_value* data; intptr_t size, cap; } cstr_buf; +typedef union cstr { + struct { cstr_buf *a, *b, *c; } _dummy; + struct { cstr_value* data; uintptr_t size; uintptr_t ncap; } lon; + struct { cstr_value data[ sizeof(cstr_buf) - 1 ]; uint8_t size; } sml; +} cstr; + +typedef union { + csview chr; // utf8 character/codepoint + const cstr_value* ref; +} cstr_iter; + +#define c_true(...) __VA_ARGS__ +#define c_false(...) + +#define declare_arc(SELF, VAL) \ + typedef VAL SELF##_value; \ + typedef struct SELF##_ctrl SELF##_ctrl; \ +\ + typedef union SELF { \ + SELF##_value* get; \ + SELF##_ctrl* ctrl; \ + } SELF + +#define declare_arc2(SELF, VAL) \ + typedef VAL SELF##_value; \ + typedef struct SELF##_ctrl SELF##_ctrl; \ +\ + typedef struct SELF { \ + SELF##_value* get; \ + SELF##_ctrl* ctrl2; \ + } SELF + +#define declare_box(SELF, VAL) \ + typedef VAL SELF##_value; \ +\ + typedef struct SELF { \ + SELF##_value* get; \ + } SELF + +#define _declare_queue(SELF, VAL, AUXDEF) \ + typedef VAL SELF##_value; \ +\ + typedef struct SELF { \ + SELF##_value *cbuf; \ + ptrdiff_t start, end, capmask; \ + AUXDEF \ + } SELF; \ +\ + typedef struct { \ + SELF##_value *ref; \ + ptrdiff_t pos; \ + const SELF* _s; \ + } SELF##_iter + +#define _declare_list(SELF, VAL, AUXDEF) \ + typedef VAL SELF##_value; \ + typedef struct SELF##_node SELF##_node; \ +\ + typedef struct { \ + SELF##_value *ref; \ + SELF##_node *const *_last, *prev; \ + } SELF##_iter; \ +\ + typedef struct SELF { \ + SELF##_node *last; \ + AUXDEF \ + } SELF + +#define _declare_htable(SELF, KEY, VAL, MAP_ONLY, SET_ONLY, AUXDEF) \ + typedef KEY SELF##_key; \ + typedef VAL SELF##_mapped; \ +\ + typedef SET_ONLY( SELF##_key ) \ + MAP_ONLY( struct SELF##_value ) \ + SELF##_value, SELF##_entry; \ +\ + typedef struct { \ + SELF##_value *ref; \ + size_t idx; \ + bool inserted; \ + uint8_t hashx; \ + uint16_t dist; \ + } SELF##_result; \ +\ + typedef struct { \ + SELF##_value *ref, *_end; \ + struct hmap_meta *_mref; \ + } SELF##_iter; \ +\ + typedef struct SELF { \ + SELF##_value* table; \ + struct hmap_meta* meta; \ + ptrdiff_t size, bucket_count; \ + AUXDEF \ + } SELF + +#define _declare_aatree(SELF, KEY, VAL, MAP_ONLY, SET_ONLY, AUXDEF) \ + typedef KEY SELF##_key; \ + typedef VAL SELF##_mapped; \ + typedef struct SELF##_node SELF##_node; \ +\ + typedef SET_ONLY( SELF##_key ) \ + MAP_ONLY( struct SELF##_value ) \ + SELF##_value, SELF##_entry; \ +\ + typedef struct { \ + SELF##_value *ref; \ + bool inserted; \ + } SELF##_result; \ +\ + typedef struct { \ + SELF##_value *ref; \ + SELF##_node *_d; \ + int _top; \ + int32_t _tn, _st[36]; \ + } SELF##_iter; \ +\ + typedef struct SELF { \ + SELF##_node *nodes; \ + int32_t root, disp, head, size, capacity; \ + AUXDEF \ + } SELF + +#define declare_stack_fixed(SELF, VAL, CAP) \ + typedef VAL SELF##_value; \ + typedef struct { SELF##_value *ref, *end; } SELF##_iter; \ + typedef struct SELF { SELF##_value data[CAP]; ptrdiff_t size; } SELF + +#define _declare_stack(SELF, VAL, AUXDEF) \ + typedef VAL SELF##_value; \ + typedef struct { SELF##_value *ref, *end; } SELF##_iter; \ + typedef struct SELF { SELF##_value *data; ptrdiff_t size, capacity; AUXDEF } SELF + +#endif // STC_TYPES_H_INCLUDED diff --git a/src/finchlite/codegen/stc/stc/utf8.h b/src/finchlite/codegen/stc/stc/utf8.h new file mode 100644 index 00000000..3f91b652 --- /dev/null +++ b/src/finchlite/codegen/stc/stc/utf8.h @@ -0,0 +1,37 @@ +/* MIT License + * + * Copyright (c) 2025 Tyge Løvset + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "priv/linkage.h" + +#ifndef STC_UTF8_H_INCLUDED +#define STC_UTF8_H_INCLUDED + +#include "common.h" // IWYU pragma: keep +#include "types.h" +#include "priv/utf8_prv.h" // IWYU pragma: keep + +#endif // STC_UTF8_H_INCLUDED + +#if defined i_implement + #include "priv/utf8_prv.c" +#endif +#include "priv/linkage2.h" diff --git a/src/finchlite/codegen/stc/stc/vec.h b/src/finchlite/codegen/stc/stc/vec.h new file mode 100644 index 00000000..bf5f7faa --- /dev/null +++ b/src/finchlite/codegen/stc/stc/vec.h @@ -0,0 +1,397 @@ +/* MIT License + * + * Copyright (c) 2025 Tyge Løvset + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* +#define i_implement +#include +#include + +declare_vec(vec_i32, int); + +typedef struct MyStruct { + vec_i32 int_vec; + cstr name; +} MyStruct; + +#define i_key float +#include + +#define i_keypro cstr // cstr is a "pro"-type +#include + +#define T vec_i32, int32_t, (c_declared) +#include + +int main(void) { + vec_i32 vec = {0}; + vec_i32_push(&vec, 123); + vec_i32_drop(&vec); + + vec_float fvec = {0}; + vec_float_push(&fvec, 123.3); + vec_float_drop(&fvec); + + vec_cstr svec = {0}; + vec_cstr_emplace(&svec, "Hello, friend"); + vec_cstr_drop(&svec); +} +*/ +#include "priv/linkage.h" +#include "types.h" + +#ifndef STC_VEC_H_INCLUDED +#define STC_VEC_H_INCLUDED +#include "common.h" +#include + +#define _it2_ptr(it1, it2) (it1.ref && !it2.ref ? it1.end : it2.ref) +#define _it_ptr(it) (it.ref ? it.ref : it.end) +#endif // STC_VEC_H_INCLUDED + +#ifndef _i_prefix + #define _i_prefix vec_ +#endif +#include "priv/template.h" + +#ifndef i_declared + _c_DEFTYPES(_declare_stack, Self, i_key, _i_aux_def); +#endif +typedef i_keyraw _m_raw; +STC_API void _c_MEMB(_drop)(const Self* cself); +STC_API void _c_MEMB(_clear)(Self* self); +STC_API bool _c_MEMB(_reserve)(Self* self, isize cap); +STC_API bool _c_MEMB(_resize)(Self* self, isize size, _m_value null); +STC_API _m_iter _c_MEMB(_erase_n)(Self* self, isize idx, isize n); +STC_API _m_iter _c_MEMB(_insert_uninit)(Self* self, isize idx, isize n); +#if defined _i_has_eq +STC_API _m_iter _c_MEMB(_find_in)(const Self* self, _m_iter it1, _m_iter it2, _m_raw raw); +#endif // _i_has_eq + +STC_INLINE void _c_MEMB(_value_drop)(const Self* self, _m_value* val) + { (void)self; i_keydrop(val); } + +STC_INLINE Self _c_MEMB(_move)(Self *self) { + Self m = *self; + self->capacity = self->size = 0; + self->data = NULL; + return m; +} + +STC_INLINE void _c_MEMB(_take)(Self *self, Self unowned) { + _c_MEMB(_drop)(self); + *self = unowned; +} + +STC_INLINE _m_value* _c_MEMB(_push)(Self* self, _m_value value) { + if (self->size == self->capacity) + if (!_c_MEMB(_reserve)(self, self->size*2 + 4)) + return NULL; + _m_value *v = self->data + self->size++; + *v = value; + return v; +} + +#ifndef _i_no_put +STC_INLINE void _c_MEMB(_put_n)(Self* self, const _m_raw* raw, isize n) + { while (n--) _c_MEMB(_push)(self, i_keyfrom((*raw))), ++raw; } +#endif + +#ifndef i_no_emplace +STC_API _m_iter _c_MEMB(_emplace_n)(Self* self, isize idx, const _m_raw raw[], isize n); + +STC_INLINE _m_value* _c_MEMB(_emplace)(Self* self, _m_raw raw) + { return _c_MEMB(_push)(self, i_keyfrom(raw)); } + +STC_INLINE _m_value* _c_MEMB(_emplace_back)(Self* self, _m_raw raw) + { return _c_MEMB(_push)(self, i_keyfrom(raw)); } + +STC_INLINE _m_iter _c_MEMB(_emplace_at)(Self* self, _m_iter it, _m_raw raw) + { return _c_MEMB(_emplace_n)(self, _it_ptr(it) - self->data, &raw, 1); } +#endif // !i_no_emplace + +#ifndef i_no_clone +STC_API void _c_MEMB(_copy)(Self* self, const Self* other); +STC_API _m_iter _c_MEMB(_copy_to)(Self* self, isize idx, const _m_value arr[], isize n); +STC_API Self _c_MEMB(_clone)(Self vec); +STC_INLINE _m_value _c_MEMB(_value_clone)(const Self* self, _m_value val) + { (void)self; return i_keyclone(val); } +#endif // !i_no_clone + +STC_INLINE isize _c_MEMB(_size)(const Self* self) { return self->size; } +STC_INLINE isize _c_MEMB(_capacity)(const Self* self) { return self->capacity; } +STC_INLINE bool _c_MEMB(_is_empty)(const Self* self) { return !self->size; } +STC_INLINE _m_raw _c_MEMB(_value_toraw)(const _m_value* val) { return i_keytoraw(val); } +STC_INLINE const _m_value* _c_MEMB(_front)(const Self* self) { return self->data; } +STC_INLINE _m_value* _c_MEMB(_front_mut)(Self* self) { return self->data; } +STC_INLINE const _m_value* _c_MEMB(_back)(const Self* self) { return &self->data[self->size - 1]; } +STC_INLINE _m_value* _c_MEMB(_back_mut)(Self* self) { return &self->data[self->size - 1]; } + +STC_INLINE void _c_MEMB(_pop)(Self* self) + { c_assert(self->size); _m_value* p = &self->data[--self->size]; i_keydrop(p); } +STC_INLINE _m_value _c_MEMB(_pull)(Self* self) + { c_assert(self->size); return self->data[--self->size]; } +STC_INLINE _m_value* _c_MEMB(_push_back)(Self* self, _m_value value) + { return _c_MEMB(_push)(self, value); } +STC_INLINE void _c_MEMB(_pop_back)(Self* self) { _c_MEMB(_pop)(self); } + +#ifndef _i_aux_alloc + STC_INLINE Self _c_MEMB(_init)(void) + { return c_literal(Self){0}; } + + STC_INLINE Self _c_MEMB(_with_capacity)(isize cap) + { Self out = {_i_new_n(_m_value, cap), 0, cap}; return out; } + + STC_INLINE Self _c_MEMB(_with_size_uninit)(isize size) + { Self out = {_i_new_n(_m_value, size), size, size}; return out; } + + STC_INLINE Self _c_MEMB(_with_size)(isize size, _m_raw default_raw) { + Self out = {_i_new_n(_m_value, size), size, size}; + while (size) out.data[--size] = i_keyfrom(default_raw); + return out; + } + + #ifndef _i_no_put + STC_INLINE Self _c_MEMB(_from_n)(const _m_raw* raw, isize n) { + Self out = _c_MEMB(_with_capacity)(n); + _c_MEMB(_put_n)(&out, raw, n); return out; + } + #endif +#endif + +STC_INLINE void _c_MEMB(_shrink_to_fit)(Self* self) + { _c_MEMB(_reserve)(self, _c_MEMB(_size)(self)); } + +STC_INLINE _m_iter +_c_MEMB(_insert_n)(Self* self, const isize idx, const _m_value arr[], const isize n) { + _m_iter it = _c_MEMB(_insert_uninit)(self, idx, n); + if (it.ref) + c_memcpy(it.ref, arr, n*c_sizeof *arr); + return it; +} + +STC_INLINE _m_iter _c_MEMB(_insert_at)(Self* self, _m_iter it, const _m_value value) { + return _c_MEMB(_insert_n)(self, _it_ptr(it) - self->data, &value, 1); +} + +STC_INLINE _m_iter _c_MEMB(_erase_at)(Self* self, _m_iter it) { + return _c_MEMB(_erase_n)(self, it.ref - self->data, 1); +} + +STC_INLINE _m_iter _c_MEMB(_erase_range)(Self* self, _m_iter i1, _m_iter i2) { + return _c_MEMB(_erase_n)(self, i1.ref - self->data, _it2_ptr(i1, i2) - i1.ref); +} + +STC_INLINE const _m_value* _c_MEMB(_at)(const Self* self, const isize idx) { + c_assert(c_uless(idx, self->size)); return self->data + idx; +} + +STC_INLINE _m_value* _c_MEMB(_at_mut)(Self* self, const isize idx) { + c_assert(c_uless(idx, self->size)); return self->data + idx; +} + +// iteration + +STC_INLINE _m_iter _c_MEMB(_begin)(const Self* self) { + _m_iter it = {(_m_value*)self->data, (_m_value*)self->data}; + if (self->size) it.end += self->size; + else it.ref = NULL; + return it; +} + +STC_INLINE _m_iter _c_MEMB(_rbegin)(const Self* self) { + _m_iter it = {(_m_value*)self->data, (_m_value*)self->data}; + if (self->size) { it.ref += self->size - 1; it.end -= 1; } + else it.ref = NULL; + return it; +} + +STC_INLINE _m_iter _c_MEMB(_end)(const Self* self) + { (void)self; _m_iter it = {0}; return it; } + +STC_INLINE _m_iter _c_MEMB(_rend)(const Self* self) + { (void)self; _m_iter it = {0}; return it; } + +STC_INLINE void _c_MEMB(_next)(_m_iter* it) + { if (++it->ref == it->end) it->ref = NULL; } + +STC_INLINE void _c_MEMB(_rnext)(_m_iter* it) + { if (--it->ref == it->end) it->ref = NULL; } + +STC_INLINE _m_iter _c_MEMB(_advance)(_m_iter it, size_t n) { + if ((it.ref += n) >= it.end) it.ref = NULL; + return it; +} + +STC_INLINE isize _c_MEMB(_index)(const Self* self, _m_iter it) + { return (it.ref - self->data); } + +STC_INLINE void _c_MEMB(_adjust_end_)(Self* self, isize n) + { self->size += n; } + +#if defined _i_has_eq +STC_INLINE _m_iter _c_MEMB(_find)(const Self* self, _m_raw raw) { + return _c_MEMB(_find_in)(self, _c_MEMB(_begin)(self), _c_MEMB(_end)(self), raw); +} + +STC_INLINE bool _c_MEMB(_eq)(const Self* self, const Self* other) { + if (self->size != other->size) return false; + for (isize i = 0; i < self->size; ++i) { + const _m_raw _rx = i_keytoraw((self->data+i)), _ry = i_keytoraw((other->data+i)); + if (!(i_eq((&_rx), (&_ry)))) return false; + } + return true; +} +#endif // _i_has_eq + +#if defined _i_has_cmp +#include "priv/sort_prv.h" +#endif // _i_has_cmp + +/* -------------------------- IMPLEMENTATION ------------------------- */ +#if defined i_implement + +STC_DEF void +_c_MEMB(_clear)(Self* self) { + if (self->size == 0) return; + _m_value *p = self->data + self->size; + while (p-- != self->data) { i_keydrop(p); } + self->size = 0; +} + +STC_DEF void +_c_MEMB(_drop)(const Self* cself) { + Self* self = (Self*)cself; + if (self->capacity == 0) + return; + _c_MEMB(_clear)(self); + _i_free_n(self->data, self->capacity); +} + +STC_DEF bool +_c_MEMB(_reserve)(Self* self, const isize cap) { + if (cap > self->capacity || (cap && cap == self->size)) { + _m_value* d = (_m_value*)_i_realloc_n(self->data, self->capacity, cap); + if (d == NULL) + return false; + self->data = d; + self->capacity = cap; + } + return self->data != NULL; +} + +STC_DEF bool +_c_MEMB(_resize)(Self* self, const isize len, _m_value null) { + if (!_c_MEMB(_reserve)(self, len)) + return false; + const isize n = self->size; + for (isize i = len; i < n; ++i) + { i_keydrop((self->data + i)); } + for (isize i = n; i < len; ++i) + self->data[i] = null; + self->size = len; + return true; +} + +STC_DEF _m_iter +_c_MEMB(_insert_uninit)(Self* self, const isize idx, const isize n) { + if (self->size + n >= self->capacity) + if (!_c_MEMB(_reserve)(self, self->size*3/2 + n)) + return _c_MEMB(_end)(self); + + _m_value *pos = self->data + idx; + c_memmove(pos + n, pos, (self->size - idx)*c_sizeof *pos); + self->size += n; + return c_literal(_m_iter){pos, self->data + self->size}; +} + +STC_DEF _m_iter +_c_MEMB(_erase_n)(Self* self, const isize idx, const isize len) { + c_assert(idx + len <= self->size); + _m_value* d = self->data + idx, *p = d, *end = self->data + self->size; + for (isize i = 0; i < len; ++i, ++p) + { i_keydrop(p); } + memmove(d, p, (size_t)(end - p)*sizeof *d); + self->size -= len; + return c_literal(_m_iter){p == end ? NULL : d, end - len}; +} + +#ifndef i_no_clone +STC_DEF void +_c_MEMB(_copy)(Self* self, const Self* other) { + if (self == other) return; + _c_MEMB(_clear)(self); + _c_MEMB(_reserve)(self, other->size); + self->size = other->size; + for (c_range(i, other->size)) + self->data[i] = i_keyclone((other->data[i])); +} + +STC_DEF Self +_c_MEMB(_clone)(Self vec) { + Self out = vec, *self = &out; (void)self; + out.data = NULL; out.size = out.capacity = 0; + _c_MEMB(_reserve)(&out, vec.size); + out.size = vec.size; + for (c_range(i, vec.size)) + out.data[i] = i_keyclone(vec.data[i]); + return out; +} + +STC_DEF _m_iter +_c_MEMB(_copy_to)(Self* self, const isize idx, + const _m_value arr[], const isize n) { + _m_iter it = _c_MEMB(_insert_uninit)(self, idx, n); + if (it.ref) + for (_m_value* p = it.ref, *q = p + n; p != q; ++arr) + *p++ = i_keyclone((*arr)); + return it; +} +#endif // !i_no_clone + +#ifndef i_no_emplace +STC_DEF _m_iter +_c_MEMB(_emplace_n)(Self* self, const isize idx, const _m_raw raw[], isize n) { + _m_iter it = _c_MEMB(_insert_uninit)(self, idx, n); + if (it.ref) + for (_m_value* p = it.ref; n--; ++raw, ++p) + *p = i_keyfrom((*raw)); + return it; +} +#endif // !i_no_emplace + +#if defined _i_has_eq +STC_DEF _m_iter +_c_MEMB(_find_in)(const Self* self, _m_iter i1, _m_iter i2, _m_raw raw) { + (void)self; + const _m_value* p2 = _it2_ptr(i1, i2); + for (; i1.ref != p2; ++i1.ref) { + const _m_raw r = i_keytoraw(i1.ref); + if (i_eq((&raw), (&r))) + return i1; + } + i2.ref = NULL; + return i2; +} +#endif // _i_has_eq +#endif // i_implement +#include "sys/finalize.h" diff --git a/src/finchlite/codegen/stc/stc/zsview.h b/src/finchlite/codegen/stc/stc/zsview.h new file mode 100644 index 00000000..8afb73a6 --- /dev/null +++ b/src/finchlite/codegen/stc/stc/zsview.h @@ -0,0 +1,173 @@ +/* MIT License + * + * Copyright (c) 2025 Tyge Løvset + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +// zsview is a zero-terminated string view. + +#ifndef STC_ZSVIEW_H_INCLUDED +#define STC_ZSVIEW_H_INCLUDED + +#include "common.h" +#include "types.h" +#include "priv/utf8_prv.h" + +#define zsview_init() c_zv("") +#define zsview_clone(zs) c_default_clone(zs) +#define zsview_drop(self) c_default_drop(self) +#define zsview_toraw(self) (self)->str + +STC_INLINE zsview zsview_from(const char* str) + { return c_literal(zsview){str, c_strlen(str)}; } +STC_INLINE void zsview_clear(zsview* self) { *self = c_zv(""); } +STC_INLINE csview zsview_sv(zsview zs) { return c_sv_2(zs.str, zs.size); } + +STC_INLINE isize zsview_size(zsview zs) { return zs.size; } +STC_INLINE bool zsview_is_empty(zsview zs) { return zs.size == 0; } + +STC_INLINE bool zsview_equals(zsview zs, const char* str) { + isize n = c_strlen(str); + return zs.size == n && !c_memcmp(zs.str, str, n); +} + +STC_INLINE isize zsview_find(zsview zs, const char* search) { + const char* res = strstr(zs.str, search); + return res ? (res - zs.str) : c_NPOS; +} + +STC_INLINE bool zsview_contains(zsview zs, const char* str) + { return zsview_find(zs, str) != c_NPOS; } + +STC_INLINE bool zsview_starts_with(zsview zs, const char* str) { + isize n = c_strlen(str); + return n <= zs.size && !c_memcmp(zs.str, str, n); +} + +STC_INLINE bool zsview_ends_with(zsview zs, const char* str) { + isize n = c_strlen(str); + return n <= zs.size && !c_memcmp(zs.str + zs.size - n, str, n); +} + +STC_INLINE zsview zsview_from_pos(zsview zs, isize pos) { + if (pos > zs.size) pos = zs.size; + zs.str += pos; zs.size -= pos; return zs; +} + +STC_INLINE csview zsview_subview(zsview zs, isize pos, isize len) { + c_assert(((size_t)pos <= (size_t)zs.size) & (len >= 0)); + if (pos + len > zs.size) len = zs.size - pos; + return c_literal(csview){zs.str + pos, len}; +} + +STC_INLINE zsview zsview_tail(zsview zs, isize len) { + c_assert(len >= 0); + if (len > zs.size) len = zs.size; + zs.str += zs.size - len; zs.size = len; + return zs; +} + +/* utf8 */ + +STC_INLINE zsview zsview_u8_from_pos(zsview zs, isize u8pos) + { return zsview_from_pos(zs, utf8_to_index(zs.str, u8pos)); } + +STC_INLINE zsview zsview_u8_tail(zsview zs, isize u8len) { + const char* p = &zs.str[zs.size]; + while (u8len && p != zs.str) + u8len -= (*--p & 0xC0) != 0x80; + zs.size -= p - zs.str, zs.str = p; + return zs; +} + +STC_INLINE csview zsview_u8_subview(zsview zs, isize u8pos, isize u8len) + { return utf8_subview(zs.str, u8pos, u8len); } + +STC_INLINE zsview_iter zsview_u8_at(zsview zs, isize u8pos) { + csview sv; + sv.buf = utf8_at(zs.str, u8pos); + sv.size = utf8_chr_size(sv.buf); + return c_literal(zsview_iter){.chr = sv}; +} + +STC_INLINE isize zsview_u8_size(zsview zs) + { return utf8_count(zs.str); } + +STC_INLINE bool zsview_u8_valid(zsview zs) // requires linking with utf8 symbols + { return utf8_valid_n(zs.str, zs.size); } + +/* utf8 iterator */ + +STC_INLINE zsview_iter zsview_begin(const zsview* self) { + zsview_iter it = {.chr = {self->str, utf8_chr_size(self->str)}}; return it; +} + +STC_INLINE zsview_iter zsview_end(const zsview* self) { + (void)self; zsview_iter it = {0}; return it; +} + +STC_INLINE void zsview_next(zsview_iter* it) { + it->ref += it->chr.size; + it->chr.size = utf8_chr_size(it->ref); + if (*it->ref == '\0') it->ref = NULL; +} + +STC_INLINE zsview_iter zsview_advance(zsview_iter it, isize u8pos) { + it.ref = utf8_offset(it.ref, u8pos); + it.chr.size = utf8_chr_size(it.ref); + if (*it.ref == '\0') it.ref = NULL; + return it; +} + +/* ---- Container helper functions ---- */ + +STC_INLINE size_t zsview_hash(const zsview *self) + { return c_hash_str(self->str); } + +STC_INLINE int zsview_cmp(const zsview* x, const zsview* y) + { return strcmp(x->str, y->str); } + +STC_INLINE bool zsview_eq(const zsview* x, const zsview* y) + { return x->size == y->size && !c_memcmp(x->str, y->str, x->size); } + +STC_INLINE int zsview_icmp(const zsview* x, const zsview* y) + { return utf8_icmp(x->str, y->str); } + +STC_INLINE bool zsview_ieq(const zsview* x, const zsview* y) + { return x->size == y->size && !utf8_icmp(x->str, y->str); } + +/* ---- case insensitive ---- */ + +STC_INLINE bool zsview_iequals(zsview zs, const char* str) + { return c_strlen(str) == zs.size && !utf8_icmp(zs.str, str); } + +STC_INLINE bool zsview_istarts_with(zsview zs, const char* str) + { return c_strlen(str) <= zs.size && !utf8_icmp(zs.str, str); } + +STC_INLINE bool zsview_iends_with(zsview zs, const char* str) { + isize n = c_strlen(str); + return n <= zs.size && !utf8_icmp(zs.str + zs.size - n, str); +} + +#endif // STC_ZSVIEW_H_INCLUDED + +#if defined i_import + #include "priv/utf8_prv.c" +#endif diff --git a/src/finchlite/finch_assembly/map.py b/src/finchlite/finch_assembly/map.py index ccbf5194..d6adc548 100644 --- a/src/finchlite/finch_assembly/map.py +++ b/src/finchlite/finch_assembly/map.py @@ -10,7 +10,8 @@ class Map(FTyped, ABC): """ @abstractmethod - def __init__(self, length: int, dtype: type): ... + def __init__(self, key_len: int, value_len: int, map: "dict[tuple,tuple] | None"): + ... @property def element_type(self): diff --git a/src/finchlite/finch_assembly/nodes.py b/src/finchlite/finch_assembly/nodes.py index 0904fb9d..7c3cd24a 100644 --- a/src/finchlite/finch_assembly/nodes.py +++ b/src/finchlite/finch_assembly/nodes.py @@ -411,7 +411,7 @@ def result_format(self): @dataclass(eq=True, frozen=True) -class StoreMap(AssemblyTree): +class StoreMap(AssemblyTree, AssemblyStatement): """ Represents storing a value into a buffer given an integer tuple key. From e48b27827bb2b03f6b4176be700d37a85b0b49c1 Mon Sep 17 00:00:00 2001 From: Juni Kim Date: Mon, 24 Nov 2025 22:37:01 -0500 Subject: [PATCH 05/44] some more errors fixed? --- src/finchlite/codegen/hashtable.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/finchlite/codegen/hashtable.py b/src/finchlite/codegen/hashtable.py index 6ca17934..363ac324 100644 --- a/src/finchlite/codegen/hashtable.py +++ b/src/finchlite/codegen/hashtable.py @@ -94,7 +94,7 @@ def gen_code( assert isinstance(value_type, TupleFType) key_len = len(key_type.struct_fields) - value_len = len(key_type.struct_fields) + value_len = len(value_type.struct_fields) # dereference both key and value types; as given, they are both pointers. keytype_c = ctx.ctype_name(c_type(key_type)._type_) valuetype_c = ctx.ctype_name(c_type(value_type)._type_) @@ -252,7 +252,7 @@ def store(self, idx, val): KeyStruct = c_type(self.ftype().key_type)._type_ ValueStruct = c_type(self.ftype().value_type)._type_ c_key = KeyStruct(*idx) - c_value = ValueStruct() + c_value = ValueStruct(*val) getattr(self.lib.library, self.lib.methods["store"])( self.map, ctypes.byref(c_key), ctypes.byref(c_value) ) @@ -445,7 +445,7 @@ def c_existsmap(self, ctx: "CContext", map: "Stack", idx: "AssemblyExpression"): # TODO: call in the methods from the c library. assert isinstance(map.obj, CMapFields) methods: CHashMethods = ctx.datastructures[self] - return (f"{ctx.feed}{methods['store']}({map.obj.map}, {ctx(idx)})") + return (f"{ctx.feed}{methods['exists']}({map.obj.map}, {ctx(idx)})") def c_storemap( self, @@ -456,7 +456,7 @@ def c_storemap( ): assert isinstance(map.obj, CMapFields) methods: CHashMethods = ctx.datastructures[self] - ctx.exec(f"{methods['exists']}({map.obj.map}, {ctx(idx)}, {ctx(value)})") + ctx.exec(f"{methods['store']}({map.obj.map}, {ctx(idx)}, {ctx(value)})") def c_loadmap(self, ctx: "CContext", map: "Stack", idx: "AssemblyExpression"): """ @@ -506,6 +506,9 @@ def serialize_to_c(self, obj: CHashTable): assert isinstance(obj, CHashTable) map = ctypes.c_void_p(obj.map) struct = CHashMapStruct(map, obj) + # We NEED this for stupid ownership reasons. + obj._self_obj = ctypes.py_object(obj) # type: ignore + obj._struct = struct # type: ignore return ctypes.pointer(struct) def deserialize_from_c(self, obj: CHashTable, res): From da93ec60e649d6c9e06789d274a3c3927688af28 Mon Sep 17 00:00:00 2001 From: Juni Kim Date: Mon, 24 Nov 2025 23:01:44 -0500 Subject: [PATCH 06/44] more fixes --- src/finchlite/codegen/hashtable.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/finchlite/codegen/hashtable.py b/src/finchlite/codegen/hashtable.py index 363ac324..050b9b40 100644 --- a/src/finchlite/codegen/hashtable.py +++ b/src/finchlite/codegen/hashtable.py @@ -1,7 +1,7 @@ import ctypes from dataclasses import dataclass from pathlib import Path -from typing import NamedTuple, TypeAlias, TypedDict +from typing import NamedTuple, TypedDict import numba @@ -317,8 +317,7 @@ def __str__(self): # class HashTableFType(MapFType, CMapFType, CStackFType, NumbaMapFType, NumbaStackFType): class HashTableFType(CMapFType, NumbaMapFType, CStackFType, NumbaStackFType): """ - A ftype for buffers that uses NumPy arrays. This is a concrete implementation - of the BufferFType class. + An implementation of Hash Tables using the stc library. """ def __init__(self, key_len: int, value_len: int): @@ -399,9 +398,7 @@ def numba_unpack( self, ctx: "NumbaContext", var_n: str, val: "AssemblyExpression" ) -> NumbaMapFields: """ - Unpack the buffer into Numba context. - This is part of a step that will create a new slot for var_n - that contains variable names corresponding to the unpacked fields. + Unpack the map into numba context. """ # the val field will always be asm.Variable(var_n, var_t) map = ctx.freshen(var_n, "map") @@ -411,7 +408,7 @@ def numba_unpack( def numba_repack(self, ctx: "NumbaContext", lhs: str, obj: "NumbaMapFields"): """ - Repack the buffer from Numba context. + Repack the map from Numba context. """ # obj is the fields corresponding to the self.slots[lhs] ctx.exec(f"{ctx.feed}{lhs}[0] = {obj.map}") @@ -427,11 +424,11 @@ def serialize_to_numba(self, obj: "HashTable"): def deserialize_from_numba(self, obj: "HashTable", numba_map: "list[dict]"): obj.map = numba_map[0] - def construct_from_numba(self, numba_buffer): + def construct_from_numba(self, numba_map): """ - Construct a numba buffer from a Numba-compatible object. + Construct a numba map from a Numba-compatible object. """ - return HashTable(self.key_len, self.value_len, numba_buffer[0]) + return HashTable(self.key_len, self.value_len, numba_map[0]) """ Methods for the C Backend @@ -513,17 +510,20 @@ def serialize_to_c(self, obj: CHashTable): def deserialize_from_c(self, obj: CHashTable, res): """ - Update this buffer based on how the C call modified the CNumpyBuffer structure. + Update our hash table based on how the C call modified the CHashMapStruct. """ assert isinstance(res, ctypes.POINTER(CHashMapStruct)) assert isinstance(res.contents.obj, CHashTable) + obj.map = res.contents.map def construct_from_c(self, c_map): """ - Construct a NumpyBuffer from a C-compatible structure. + Construct a CHashTable from a C-compatible structure. c_map is a pointer to a CHashMapStruct + + I am going to refrain from doing this because lifecycle management is horrible. """ raise NotImplementedError From a87c2f4195bd644d775bfa04a87afc87998a27e7 Mon Sep 17 00:00:00 2001 From: Juni Kim Date: Tue, 25 Nov 2025 14:57:56 -0500 Subject: [PATCH 07/44] store tests actually work --- src/finchlite/codegen/c.py | 10 + src/finchlite/codegen/hashtable.py | 297 +++++++++++++---------- src/finchlite/codegen/numba_backend.py | 9 + src/finchlite/finch_assembly/__init__.py | 6 + src/finchlite/finch_assembly/nodes.py | 3 +- tests/test_codegen.py | 38 ++- 6 files changed, 238 insertions(+), 125 deletions(-) diff --git a/src/finchlite/codegen/c.py b/src/finchlite/codegen/c.py index abc0ea79..5c5a8cbd 100644 --- a/src/finchlite/codegen/c.py +++ b/src/finchlite/codegen/c.py @@ -810,6 +810,15 @@ def __call__(self, prgm: asm.AssemblyNode): case asm.Length(buf): buf = self.resolve(buf) return buf.result_format.c_length(self, buf) + case asm.LoadMap(map, idx): + map = self.resolve(map) + return map.result_format.c_loadmap(self, map, idx) + case asm.ExistsMap(map, idx): + map = self.resolve(map) + return map.result_format.c_existsmap(self, map, idx) + case asm.StoreMap(map, idx, val): + map = self.resolve(map) + return map.result_format.c_storemap(self, map, idx, val) case asm.Block(bodies): ctx_2 = self.block() for body in bodies: @@ -892,6 +901,7 @@ def __call__(self, prgm: asm.AssemblyNode): for arg in args: match arg: case asm.Variable(name, t): + print("Trying to get c type of", t) t_name = self.ctype_name(c_type(t)) arg_decls.append(f"{t_name} {name}") ctx_2.types[name] = t diff --git a/src/finchlite/codegen/hashtable.py b/src/finchlite/codegen/hashtable.py index 050b9b40..47337633 100644 --- a/src/finchlite/codegen/hashtable.py +++ b/src/finchlite/codegen/hashtable.py @@ -52,6 +52,10 @@ def _is_integer_tuple(tup, size): return True +def _int_tuple_ftype(size: int): + return TupleFType.from_tuple(tuple(int for _ in range(size))) + + class CHashMapStruct(ctypes.Structure): _fields_ = [ ("map", ctypes.c_void_p), @@ -113,7 +117,7 @@ def gen_code( "cleanup": ctx.freshen("finch_hmap_cleanup", key_len, value_len), } # register these methods in the datastructures. - ctx.datastructures[HashTableFType(key_len, value_len)] = methods + ctx.datastructures[CHashTableFType(key_len, value_len)] = methods # basically for the load functions, you need to provide a variable that # can be copied. @@ -141,7 +145,7 @@ def gen_code( free(hptr); }} """ - ctx.exec(lib_code) + ctx.add_header(lib_code) return methods, hmap_t @@ -152,8 +156,8 @@ def compile(cls, key_len: int, value_len: int) -> CHashTableLibrary: """ if (key_len, value_len) in cls.libraries: return cls.libraries[(key_len, value_len)] - key_type = TupleFType.from_tuple(tuple(int for _ in range(key_len))) - value_type = TupleFType.from_tuple(tuple(int for _ in range(value_len))) + key_type = _int_tuple_ftype(key_len) + value_type = _int_tuple_ftype(value_len) ctx = CContext() methods, hmap_t = cls.gen_code(ctx, key_type, value_type) @@ -229,7 +233,7 @@ def __del__(self): def exists(self, idx: tuple) -> bool: assert _is_integer_tuple(idx, self.key_len) - KeyStruct = c_type(self.ftype().key_type)._type_ + KeyStruct = c_type(self.ftype.key_type)._type_ c_key = KeyStruct(*idx) func = getattr(self.lib.library, self.lib.methods["exists"]) func.restype = ctypes.c_bool @@ -237,8 +241,8 @@ def exists(self, idx: tuple) -> bool: def load(self, idx): assert _is_integer_tuple(idx, self.key_len) - KeyStruct = c_type(self.ftype().key_type)._type_ - ValueStruct = c_type(self.ftype().value_type)._type_ + KeyStruct = c_type(self.ftype.key_type)._type_ + ValueStruct = c_type(self.ftype.value_type)._type_ c_key = KeyStruct(*idx) c_value = ValueStruct() getattr(self.lib.library, self.lib.methods["load"])( @@ -249,8 +253,8 @@ def load(self, idx): def store(self, idx, val): assert _is_integer_tuple(idx, self.key_len) assert _is_integer_tuple(val, self.value_len) - KeyStruct = c_type(self.ftype().key_type)._type_ - ValueStruct = c_type(self.ftype().value_type)._type_ + KeyStruct = c_type(self.ftype.key_type)._type_ + ValueStruct = c_type(self.ftype.value_type)._type_ c_key = KeyStruct(*idx) c_value = ValueStruct(*val) getattr(self.lib.library, self.lib.methods["store"])( @@ -258,13 +262,160 @@ def store(self, idx, val): ) def __str__(self): - return f"hashtable({self.map})" + return f"c_hashtable({self.map})" + @property def ftype(self): - return HashTableFType(self.key_len, self.value_len) + return CHashTableFType(self.key_len, self.value_len) + + +class CHashTableFType(CMapFType, CStackFType): + """ + An implementation of Hash Tables using the stc library. + """ + + def __init__(self, key_len: int, value_len: int): + self.key_len = key_len + self.value_len = value_len + self._key_type = _int_tuple_ftype(key_len) + self._value_type = _int_tuple_ftype(value_len) + + def __eq__(self, other): + if not isinstance(other, CHashTableFType): + return False + return self.key_len == other.key_len and self.value_len == other.value_len + + def __call__(self): + return CHashTable(self.key_len, self.value_len, {}) + + def __str__(self): + return f"chashtable_t({self.key_len}, {self.value_len})" + + def __repr__(self): + return f"cHashTableFType({self.key_len}, {self.value_len})" + + @property + def key_type(self): + """ + Returns the type of elements used as the keys of the hash table. + (some integer tuple) + """ + return self._key_type + + @property + def value_type(self): + """ + Returns the type of elements used as the value of the hash table. + (some integer tuple) + """ + return self._value_type + + def __hash__(self): + """ + This method needs to be here because you are going to be using this + type as a key in dictionaries. + """ + return hash(("CHashTableFType", self.key_len, self.value_len)) + + """ + Methods for the C Backend + This requires an external library (stc) to work. + """ + + def c_type(self): + return ctypes.POINTER(CHashMapStruct) + + def c_existsmap(self, ctx: "CContext", map: "Stack", idx: "AssemblyExpression"): + # TODO: call in the methods from the c library. + assert isinstance(map.obj, CMapFields) + methods: CHashMethods = ctx.datastructures[self] + return f"{ctx.feed}{methods['exists']}({map.obj.map}, {ctx(idx)})" + + def c_storemap( + self, + ctx: "CContext", + map: "Stack", + idx: "AssemblyExpression", + value: "AssemblyExpression", + ): + assert isinstance(map.obj, CMapFields) + methods: CHashMethods = ctx.datastructures[self] + ctx.exec(f"{methods['store']}({map.obj.map}, {ctx(idx)}, {ctx(value)});") + + def c_loadmap(self, ctx: "CContext", map: "Stack", idx: "AssemblyExpression"): + """ + Get an expression where we can get the value corresponding to a key. + + TODO: Do we want to use pointers to tuples (standard across everything + but requires lifecycle management) + Or do we want to just use tuple values? + + This load is incomplete without this design decision. + """ + assert isinstance(map.obj, CMapFields) + methods: CHashMethods = ctx.datastructures[self] + + valuetype_c = ctx.ctype_name(c_type(self.value_type)._type_) + value = ctx.freshen("value") + ctx.exec(f"{ctx.feed}{valuetype_c} {value};") + ctx.exec(f"{ctx.feed}{methods['load']}({map.obj.map}, {ctx(idx)}, &{value});") + return value + + def c_unpack(self, ctx: "CContext", var_n: str, val: AssemblyExpression): + """ + Unpack the map into C context. + """ + assert val.result_format == self + data = ctx.freshen(var_n, "data") + # Add all the stupid header stuff from above. + ctx.add_datastructure( + self, lambda ctx: CHashTable.gen_code(ctx, self.key_type, self.value_type) + ) + + ctx.exec(f"{ctx.feed}void* {data} = {ctx(val)}->map;") + return CMapFields(data, var_n) + + def c_repack(self, ctx: "CContext", lhs: str, obj: "CMapFields"): + """ + Repack the map out of C context. + """ + ctx.exec(f"{ctx.feed}{lhs}->map = {obj.map}") + + def serialize_to_c(self, obj: CHashTable): + """ + Serialize the Hash Map to a CHashMap structure. + This datatype will then immediately get turned into a struct. + """ + assert isinstance(obj, CHashTable) + map = ctypes.c_void_p(obj.map) + struct = CHashMapStruct(map, obj) + # We NEED this for stupid ownership reasons. + obj._self_obj = ctypes.py_object(obj) # type: ignore + obj._struct = struct # type: ignore + return ctypes.pointer(struct) + + def deserialize_from_c(self, obj: CHashTable, res): + """ + Update our hash table based on how the C call modified the CHashMapStruct. + """ + assert isinstance(res, ctypes.POINTER(CHashMapStruct)) + assert isinstance(res.contents.obj, CHashTable) + + obj.map = res.contents.map + + def construct_from_c(self, c_map): + """ + Construct a CHashTable from a C-compatible structure. + + c_map is a pointer to a CHashMapStruct + + I am going to refrain from doing this because lifecycle management is horrible. + Should we move? + """ + raise NotImplementedError -class HashTable(Map): +class NumbaHashTable(Map): """ A Hash Table that maps Z^{in_len} to Z^{out_len} """ @@ -295,7 +446,7 @@ def ftype(self): """ Returns the finch type of this hash table. """ - return HashTableFType(self.key_len, self.value_len) + return NumbaHashTableFType(self.key_len, self.value_len) def exists(self, idx) -> bool: assert _is_integer_tuple(idx, self.key_len) @@ -311,11 +462,10 @@ def store(self, idx, val): self.map[idx] = val def __str__(self): - return f"hashtable({self.map})" + return f"numba_hashtable({self.map})" -# class HashTableFType(MapFType, CMapFType, CStackFType, NumbaMapFType, NumbaStackFType): -class HashTableFType(CMapFType, NumbaMapFType, CStackFType, NumbaStackFType): +class NumbaHashTableFType(NumbaMapFType, NumbaStackFType): """ An implementation of Hash Tables using the stc library. """ @@ -323,19 +473,19 @@ class HashTableFType(CMapFType, NumbaMapFType, CStackFType, NumbaStackFType): def __init__(self, key_len: int, value_len: int): self.key_len = key_len self.value_len = value_len - self._key_type = TupleFType.from_tuple(tuple(int for _ in range(key_len))) - self._value_type = TupleFType.from_tuple(tuple(int for _ in range(value_len))) + self._key_type = _int_tuple_ftype(key_len) + self._value_type = _int_tuple_ftype(value_len) def __eq__(self, other): - if not isinstance(other, HashTableFType): + if not isinstance(other, NumbaHashTableFType): return False return self.key_len == other.key_len and self.value_len == other.value_len def __call__(self): - return HashTable(self.key_len, self.value_len, {}) + return NumbaHashTable(self.key_len, self.value_len, {}) def __str__(self): - return f"hashtable_t({self.key_len}, {self.value_len})" + return f"numba_hashtable_t({self.key_len}, {self.value_len})" def __repr__(self): return f"HashTableFType({self.key_len}, {self.value_len})" @@ -361,7 +511,7 @@ def __hash__(self): This method needs to be here because you are going to be using this type as a key in dictionaries. """ - return hash(("HashTableFType", self.key_len, self.value_len)) + return hash(("NumbaHashTableFType", self.key_len, self.value_len)) """ Methods for the Numba Backend @@ -413,7 +563,7 @@ def numba_repack(self, ctx: "NumbaContext", lhs: str, obj: "NumbaMapFields"): # obj is the fields corresponding to the self.slots[lhs] ctx.exec(f"{ctx.feed}{lhs}[0] = {obj.map}") - def serialize_to_numba(self, obj: "HashTable"): + def serialize_to_numba(self, obj: "NumbaHashTable"): """ Serialize the hashmap to a Numba-compatible object. @@ -421,111 +571,14 @@ def serialize_to_numba(self, obj: "HashTable"): """ return numba.typed.List([obj.map]) - def deserialize_from_numba(self, obj: "HashTable", numba_map: "list[dict]"): + def deserialize_from_numba(self, obj: "NumbaHashTable", numba_map: "list[dict]"): obj.map = numba_map[0] def construct_from_numba(self, numba_map): """ Construct a numba map from a Numba-compatible object. """ - return HashTable(self.key_len, self.value_len, numba_map[0]) - - """ - Methods for the C Backend - This requires an external library (stc) to work. - """ - - def c_type(self): - return ctypes.POINTER(CHashMapStruct) - - def c_existsmap(self, ctx: "CContext", map: "Stack", idx: "AssemblyExpression"): - # TODO: call in the methods from the c library. - assert isinstance(map.obj, CMapFields) - methods: CHashMethods = ctx.datastructures[self] - return (f"{ctx.feed}{methods['exists']}({map.obj.map}, {ctx(idx)})") - - def c_storemap( - self, - ctx: "CContext", - map: "Stack", - idx: "AssemblyExpression", - value: "AssemblyExpression", - ): - assert isinstance(map.obj, CMapFields) - methods: CHashMethods = ctx.datastructures[self] - ctx.exec(f"{methods['store']}({map.obj.map}, {ctx(idx)}, {ctx(value)})") - - def c_loadmap(self, ctx: "CContext", map: "Stack", idx: "AssemblyExpression"): - """ - Get an expression where we can get the value corresponding to a key. - - TODO: Do we want to use pointers to tuples (standard across everything - but requires lifecycle management) - Or do we want to just use tuple values? - - This load is incomplete without this design decision. - """ - assert isinstance(map.obj, CMapFields) - methods: CHashMethods = ctx.datastructures[self] - - valuetype_c = ctx.ctype_name(c_type(self.value_type)._type_) - value = ctx.freshen("value") - ctx.exec(f"{ctx.feed}{valuetype_c} {value};") - ctx.exec(f"{ctx.feed}{methods['load']}({map.obj.map}, {ctx(idx)}, &{value})") - return value - - def c_unpack(self, ctx: "CContext", var_n: str, val: AssemblyExpression): - """ - Unpack the map into C context. - """ - assert val.result_format == self - data = ctx.freshen(var_n, "data") - # Add all the stupid header stuff from above. - ctx.add_datastructure( - self, - lambda ctx: CHashTable.gen_code(ctx, self.key_type, self.value_type) - ) - - ctx.exec(f"{ctx.feed}void* {data} = {ctx(val)}->map;") - return CMapFields(data, var_n) - - def c_repack(self, ctx: "CContext", lhs: str, obj: "CMapFields"): - """ - Repack the map out of C context. - """ - ctx.exec(f"{ctx.feed}{lhs}->map = {obj.map}") - - def serialize_to_c(self, obj: CHashTable): - """ - Serialize the Hash Map to a CHashMap structure. - This datatype will then immediately get turned into a struct. - """ - assert isinstance(obj, CHashTable) - map = ctypes.c_void_p(obj.map) - struct = CHashMapStruct(map, obj) - # We NEED this for stupid ownership reasons. - obj._self_obj = ctypes.py_object(obj) # type: ignore - obj._struct = struct # type: ignore - return ctypes.pointer(struct) - - def deserialize_from_c(self, obj: CHashTable, res): - """ - Update our hash table based on how the C call modified the CHashMapStruct. - """ - assert isinstance(res, ctypes.POINTER(CHashMapStruct)) - assert isinstance(res.contents.obj, CHashTable) - - obj.map = res.contents.map - - def construct_from_c(self, c_map): - """ - Construct a CHashTable from a C-compatible structure. - - c_map is a pointer to a CHashMapStruct - - I am going to refrain from doing this because lifecycle management is horrible. - """ - raise NotImplementedError + return NumbaHashTable(self.key_len, self.value_len, numba_map[0]) if __name__ == "__main__": diff --git a/src/finchlite/codegen/numba_backend.py b/src/finchlite/codegen/numba_backend.py index 6527f4b1..bc2818bf 100644 --- a/src/finchlite/codegen/numba_backend.py +++ b/src/finchlite/codegen/numba_backend.py @@ -577,6 +577,15 @@ def __call__(self, prgm: asm.AssemblyNode): case asm.Length(buf): buf = self.resolve(buf) return buf.result_format.numba_length(self, buf) + case asm.LoadMap(map, idx): + map = self.resolve(map) + return map.result_format.numba_loadmap(self, map, idx) + case asm.ExistsMap(map, idx): + map = self.resolve(map) + return map.result_format.numba_existsmap(self, map, idx) + case asm.StoreMap(map, idx, val): + map = self.resolve(map) + return map.result_format.numba_storemap(self, map, idx, val) case asm.Block(bodies): ctx_2 = self.block() for body in bodies: diff --git a/src/finchlite/finch_assembly/__init__.py b/src/finchlite/finch_assembly/__init__.py index a2417d22..5b0ddd98 100644 --- a/src/finchlite/finch_assembly/__init__.py +++ b/src/finchlite/finch_assembly/__init__.py @@ -35,6 +35,9 @@ Unpack, Variable, WhileLoop, + StoreMap, + ExistsMap, + LoadMap ) from .struct import AssemblyStructFType, NamedTupleFType, TupleFType from .type_checker import AssemblyTypeChecker, AssemblyTypeError, assembly_check_types @@ -56,6 +59,7 @@ "BufferFType", "BufferLoop", "Call", + "ExistsMap", "ForLoop", "Function", "GetAttr", @@ -64,6 +68,7 @@ "Length", "Literal", "Load", + "LoadMap", "Module", "NamedTupleFType", "Print", @@ -74,6 +79,7 @@ "Slot", "Stack", "Store", + "StoreMap", "TaggedVariable", "TupleFType", "Unpack", diff --git a/src/finchlite/finch_assembly/nodes.py b/src/finchlite/finch_assembly/nodes.py index 7c3cd24a..b5f4f92b 100644 --- a/src/finchlite/finch_assembly/nodes.py +++ b/src/finchlite/finch_assembly/nodes.py @@ -395,8 +395,7 @@ class LoadMap(AssemblyExpression, AssemblyTree): Attributes: map: The map to load from. - index1: The first integer in the pair - index2: The second integer in the pair + index: The key value """ map: Slot | Stack diff --git a/tests/test_codegen.py b/tests/test_codegen.py index 202c406e..4274b9e4 100644 --- a/tests/test_codegen.py +++ b/tests/test_codegen.py @@ -12,6 +12,7 @@ from numpy.testing import assert_equal import finchlite +from finchlite.codegen.hashtable import CHashTable import finchlite.finch_assembly as asm from finchlite import ftype from finchlite.codegen import ( @@ -31,7 +32,6 @@ serialize_to_numba, ) - def test_add_function(): c_code = """ #include @@ -925,3 +925,39 @@ def test_e2e_numba(): assert_equal(result, a @ b) finchlite.set_default_scheduler(ctx=ctx) + +def test_hashtable_c(): + table = CHashTable(2, 3) + + table_v = asm.Variable("a", ftype(table)) + table_slt = asm.Slot("a_", ftype(table)) + + key_type = table.ftype.key_type + val_type = table.ftype.value_type + key_v = asm.Variable("key", key_type) + val_v = asm.Variable("val", val_type) + + module = asm.Module( + ( + asm.Function( + asm.Variable("setidx", np.intp), + (table_v, key_v, val_v), + asm.Block( + ( + asm.Unpack(table_slt, table_v), + asm.StoreMap( + table_slt, + key_v, + val_v, + ), + asm.Return(asm.Literal(0)), + ) + ), + ), + ) + ) + compiled = CCompiler()(module) + key = key_type(1, 2) + val = val_type(2, 3, 4) + compiled.setidx(table, key, val) + assert table.exists((1, 2)) From d755f8a680a6f22ba5a206c5755765ec07eb2947 Mon Sep 17 00:00:00 2001 From: Juni Kim Date: Tue, 25 Nov 2025 20:06:57 -0500 Subject: [PATCH 08/44] trying to fix up numba --- src/finchlite/codegen/c.py | 1 - src/finchlite/codegen/hashtable.py | 7 +++-- src/finchlite/codegen/numba_backend.py | 7 +++++ tests/test_codegen.py | 43 ++++++++++++++++++++++++-- 4 files changed, 53 insertions(+), 5 deletions(-) diff --git a/src/finchlite/codegen/c.py b/src/finchlite/codegen/c.py index 5c5a8cbd..60b10f9e 100644 --- a/src/finchlite/codegen/c.py +++ b/src/finchlite/codegen/c.py @@ -901,7 +901,6 @@ def __call__(self, prgm: asm.AssemblyNode): for arg in args: match arg: case asm.Variable(name, t): - print("Trying to get c type of", t) t_name = self.ctype_name(c_type(t)) arg_decls.append(f"{t_name} {name}") ctx_2.types[name] = t diff --git a/src/finchlite/codegen/hashtable.py b/src/finchlite/codegen/hashtable.py index 47337633..7f5aca8c 100644 --- a/src/finchlite/codegen/hashtable.py +++ b/src/finchlite/codegen/hashtable.py @@ -4,6 +4,7 @@ from typing import NamedTuple, TypedDict import numba +import numpy as np from finchlite.codegen.c import ( CContext, @@ -123,7 +124,6 @@ def gen_code( # can be copied. # Yeah, so which API's should we use for load and store? lib_code = f""" -#include void* {methods['init']}() {{ void* ptr = malloc(sizeof({hmap_t})); memset(ptr, 0, sizeof({hmap_t})); @@ -522,6 +522,9 @@ def numba_jitclass_type(self) -> numba.types.Type: value_t = numba.types.UniTuple(numba.types.int64, self.value_len) return numba.types.ListType(numba.types.DictType(key_t, value_t)) + def numba_type(self): + return list[dict] + def numba_existsmap( self, ctx: "NumbaContext", map: "Stack", idx: "AssemblyExpression" ): @@ -542,7 +545,7 @@ def numba_storemap( value: "AssemblyExpression", ): assert isinstance(map.obj, NumbaMapFields) - ctx.exec(f"{map.obj.map}[{ctx(idx)}] = {ctx(value)}") + ctx.exec(f"{ctx.feed}{map.obj.map}[{ctx(idx)}] = {ctx(value)}") def numba_unpack( self, ctx: "NumbaContext", var_n: str, val: "AssemblyExpression" diff --git a/src/finchlite/codegen/numba_backend.py b/src/finchlite/codegen/numba_backend.py index bc2818bf..1b9bc8e6 100644 --- a/src/finchlite/codegen/numba_backend.py +++ b/src/finchlite/codegen/numba_backend.py @@ -125,6 +125,13 @@ def assembly_struct_numba_jitclass_type(ftype_) -> numba.types.Type: lambda t: numba.from_dtype(t), ) +register_property( + int, + "numba_jitclass_type", + "__attr__", + lambda t: numba.int32, +) + register_property( AssemblyStructFType, "numba_jitclass_type", diff --git a/tests/test_codegen.py b/tests/test_codegen.py index 4274b9e4..44fe7066 100644 --- a/tests/test_codegen.py +++ b/tests/test_codegen.py @@ -12,7 +12,7 @@ from numpy.testing import assert_equal import finchlite -from finchlite.codegen.hashtable import CHashTable +from finchlite.codegen.hashtable import CHashTable, NumbaHashTable import finchlite.finch_assembly as asm from finchlite import ftype from finchlite.codegen import ( @@ -958,6 +958,45 @@ def test_hashtable_c(): ) compiled = CCompiler()(module) key = key_type(1, 2) - val = val_type(2, 3, 4) + val = key_type(2, 3, 4) compiled.setidx(table, key, val) assert table.exists((1, 2)) + assert not table.exists((1, 3)) + + +def test_hashtable_numba(): + table = NumbaHashTable(2, 3) + + table_v = asm.Variable("a", ftype(table)) + table_slt = asm.Slot("a_", ftype(table)) + + key_type = table.ftype.key_type + val_type = table.ftype.value_type + key_v = asm.Variable("key", key_type) + val_v = asm.Variable("val", val_type) + + module = asm.Module( + ( + asm.Function( + asm.Variable("setidx", np.intp), + (table_v, key_v, val_v), + asm.Block( + ( + asm.Unpack(table_slt, table_v), + asm.StoreMap( + table_slt, + key_v, + val_v, + ), + asm.Return(asm.Literal(0)), + ) + ), + ), + ) + ) + compiled = NumbaCompiler()(module) + key = key_type(1, 2) + val = key_type(2, 3, 4) + compiled.setidx(table, key, val) + assert table.exists((1, 2)) + assert not table.exists((1, 3)) From 594600c63448332baefba4f4b72ea3dcef2ef740 Mon Sep 17 00:00:00 2001 From: Juni Kim Date: Wed, 26 Nov 2025 22:58:03 -0500 Subject: [PATCH 09/44] immutable tuples Turn immutable tuples into C structs, and keep mutable tuples as C pointers. --- src/finchlite/codegen/c.py | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/src/finchlite/codegen/c.py b/src/finchlite/codegen/c.py index f9b72b6d..04101328 100644 --- a/src/finchlite/codegen/c.py +++ b/src/finchlite/codegen/c.py @@ -1035,16 +1035,37 @@ def struct_c_type(fmt: AssemblyStructFType): return new_struct +def struct_c_type_wrapper(fmt: AssemblyStructFType): + """ + C type decider for struct types. Serialization actually ensures that before + crossing the FFI boundary, all serialized structs are structs, not + pointers. + + The reason why we have this method is that ctypes can intelligently infer + whether we are working with a pointer arg type (pass by reference) or a + non-pointer type (pass by value) + """ + t = struct_c_type(fmt) + if fmt.is_mutable: + return ctypes.POINTER(t) + else: + return t + + register_property( AssemblyStructFType, "c_type", "__attr__", - lambda fmt: ctypes.POINTER(struct_c_type(fmt)), + struct_c_type_wrapper, ) def struct_c_getattr(fmt: AssemblyStructFType, ctx, obj, attr): - return f"{obj}->{attr}" + if fmt.is_mutable: + # we are passing things in as a pointer (reference c_type_wrapper) + return f"{obj}->{attr}" + else: + return f"{obj}.{attr}" register_property( @@ -1056,8 +1077,10 @@ def struct_c_getattr(fmt: AssemblyStructFType, ctx, obj, attr): def struct_c_setattr(fmt: AssemblyStructFType, ctx, obj, attr, val): - ctx.emit(f"{ctx.feed}{obj}->{attr} = {val};") - return + if fmt.is_mutable: + ctx.emit(f"{ctx.feed}{obj}->{attr} = {val};") + else: + ctx.emit(f"{ctx.feed}{obj}.{attr} = {val};") register_property( @@ -1103,7 +1126,5 @@ def serialize_tuple_to_c(fmt, obj): TupleFType, "c_type", "__attr__", - lambda fmt: ctypes.POINTER( - struct_c_type(asm.NamedTupleFType("CTuple", fmt.struct_fields)) - ), + lambda fmt: struct_c_type_wrapper(asm.NamedTupleFType("CTuple", fmt.struct_fields)), ) From 072f6c9e57bf77ac063b6a229ad8e059dd4d0891 Mon Sep 17 00:00:00 2001 From: Juni Kim Date: Wed, 26 Nov 2025 23:01:36 -0500 Subject: [PATCH 10/44] ruff reasons --- src/finchlite/codegen/c.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/finchlite/codegen/c.py b/src/finchlite/codegen/c.py index 04101328..47561709 100644 --- a/src/finchlite/codegen/c.py +++ b/src/finchlite/codegen/c.py @@ -1048,8 +1048,7 @@ def struct_c_type_wrapper(fmt: AssemblyStructFType): t = struct_c_type(fmt) if fmt.is_mutable: return ctypes.POINTER(t) - else: - return t + return t register_property( @@ -1064,8 +1063,7 @@ def struct_c_getattr(fmt: AssemblyStructFType, ctx, obj, attr): if fmt.is_mutable: # we are passing things in as a pointer (reference c_type_wrapper) return f"{obj}->{attr}" - else: - return f"{obj}.{attr}" + return f"{obj}.{attr}" register_property( From 2c443f69541b5384daac1270896c18f17f92d0d6 Mon Sep 17 00:00:00 2001 From: Juni Kim Date: Wed, 26 Nov 2025 23:23:48 -0500 Subject: [PATCH 11/44] load instruction completed --- src/finchlite/codegen/hashtable.py | 83 +++++++++++++----------------- 1 file changed, 35 insertions(+), 48 deletions(-) diff --git a/src/finchlite/codegen/hashtable.py b/src/finchlite/codegen/hashtable.py index 7f5aca8c..bc857209 100644 --- a/src/finchlite/codegen/hashtable.py +++ b/src/finchlite/codegen/hashtable.py @@ -11,7 +11,9 @@ CMapFType, CStackFType, c_type, + construct_from_c, load_shared_lib, + serialize_to_c, ) from finchlite.codegen.numba_backend import NumbaContext, NumbaMapFType, NumbaStackFType from finchlite.finch_assembly.map import Map, MapFType @@ -101,8 +103,8 @@ def gen_code( key_len = len(key_type.struct_fields) value_len = len(value_type.struct_fields) # dereference both key and value types; as given, they are both pointers. - keytype_c = ctx.ctype_name(c_type(key_type)._type_) - valuetype_c = ctx.ctype_name(c_type(value_type)._type_) + keytype_c = ctx.ctype_name(c_type(key_type)) + valuetype_c = ctx.ctype_name(c_type(value_type)) hmap_t = ctx.freshen(f"hmap", key_len, value_len) ctx.add_header(f"#define T {hmap_t}, {keytype_c}, {valuetype_c}") @@ -124,22 +126,22 @@ def gen_code( # can be copied. # Yeah, so which API's should we use for load and store? lib_code = f""" -void* {methods['init']}() {{ +inline void* {methods['init']}() {{ void* ptr = malloc(sizeof({hmap_t})); memset(ptr, 0, sizeof({hmap_t})); return ptr; }} -bool {methods['exists']}({hmap_t} *map, {keytype_c}* key) {{ - return {hmap_t}_contains(map, *key); +inline bool {methods['exists']}({hmap_t} *map, {keytype_c} key) {{ + return {hmap_t}_contains(map, key); }} -void {methods['load']}({hmap_t} *map, {keytype_c}* key, {valuetype_c}* value) {{ - const {valuetype_c}* internal_val = {hmap_t}_at(map, *key); - *value = *internal_val; +inline {valuetype_c} {methods['load']}({hmap_t} *map, {keytype_c} key) {{ + const {valuetype_c}* internal_val = {hmap_t}_at(map, key); + return *internal_val; }} -void {methods['store']}({hmap_t} *map, {keytype_c}* key, {valuetype_c}* value) {{ - {hmap_t}_insert_or_assign(map, *key, *value); +inline void {methods['store']}({hmap_t} *map, {keytype_c} key, {valuetype_c} value) {{ + {hmap_t}_insert_or_assign(map, key, value); }} -void {methods['cleanup']}(void* ptr) {{ +inline void {methods['cleanup']}(void* ptr) {{ {hmap_t}* hptr = ptr; {hmap_t}_drop(hptr); free(hptr); @@ -165,33 +167,32 @@ def compile(cls, key_len: int, value_len: int) -> CHashTableLibrary: lib = load_shared_lib(code) # get keystruct and value types - KeyStruct = c_type(key_type)._type_ - ValueStruct = c_type(value_type)._type_ + KeyStruct = c_type(key_type) + ValueStruct = c_type(value_type) init_func = getattr(lib, methods["init"]) init_func.argtypes = [] init_func.restype = ctypes.c_void_p - # Exists: Takes (map*, key*) -> returns bool + # Exists: Takes (map*, key) -> returns bool exists_func = getattr(lib, methods["exists"]) - exists_func.argtypes = [ctypes.c_void_p, ctypes.POINTER(KeyStruct)] + exists_func.argtypes = [ctypes.c_void_p, KeyStruct] exists_func.restype = ctypes.c_bool - # Load: Takes (map*, key*, out_val*) -> returns void + # Load: Takes (map*, key) -> returns value load_func = getattr(lib, methods["load"]) load_func.argtypes = [ ctypes.c_void_p, - ctypes.POINTER(KeyStruct), - ctypes.POINTER(ValueStruct), + KeyStruct, ] - load_func.restype = None + load_func.restype = ValueStruct - # Store: Takes (map*, key*, val*) -> returns void + # Store: Takes (map*, key, val) -> returns void store_func = getattr(lib, methods["store"]) store_func.argtypes = [ ctypes.c_void_p, - ctypes.POINTER(KeyStruct), - ctypes.POINTER(ValueStruct), + KeyStruct, + ValueStruct, ] store_func.restype = None @@ -233,32 +234,29 @@ def __del__(self): def exists(self, idx: tuple) -> bool: assert _is_integer_tuple(idx, self.key_len) - KeyStruct = c_type(self.ftype.key_type)._type_ + KeyStruct = c_type(self.ftype.key_type) c_key = KeyStruct(*idx) func = getattr(self.lib.library, self.lib.methods["exists"]) - func.restype = ctypes.c_bool - return func(self.map, ctypes.byref(c_key)) + return func(self.map, c_key) def load(self, idx): assert _is_integer_tuple(idx, self.key_len) - KeyStruct = c_type(self.ftype.key_type)._type_ - ValueStruct = c_type(self.ftype.value_type)._type_ + KeyStruct = c_type(self.ftype.key_type) c_key = KeyStruct(*idx) - c_value = ValueStruct() - getattr(self.lib.library, self.lib.methods["load"])( - self.map, ctypes.byref(c_key), ctypes.byref(c_value) + c_value = getattr(self.lib.library, self.lib.methods["load"])( + self.map, c_key ) - return tuple(getattr(c_value, f) for f, _ in c_value._fields_) + return tuple(getattr(c_value, f) for f in self.ftype.value_type.struct_fieldnames) def store(self, idx, val): assert _is_integer_tuple(idx, self.key_len) assert _is_integer_tuple(val, self.value_len) - KeyStruct = c_type(self.ftype.key_type)._type_ - ValueStruct = c_type(self.ftype.value_type)._type_ + KeyStruct = c_type(self.ftype.key_type) + ValueStruct = c_type(self.ftype.value_type) c_key = KeyStruct(*idx) c_value = ValueStruct(*val) getattr(self.lib.library, self.lib.methods["store"])( - self.map, ctypes.byref(c_key), ctypes.byref(c_value) + self.map, c_key, c_value ) def __str__(self): @@ -292,7 +290,7 @@ def __str__(self): return f"chashtable_t({self.key_len}, {self.value_len})" def __repr__(self): - return f"cHashTableFType({self.key_len}, {self.value_len})" + return f"CHashTableFType({self.key_len}, {self.value_len})" @property def key_type(self): @@ -326,7 +324,6 @@ def c_type(self): return ctypes.POINTER(CHashMapStruct) def c_existsmap(self, ctx: "CContext", map: "Stack", idx: "AssemblyExpression"): - # TODO: call in the methods from the c library. assert isinstance(map.obj, CMapFields) methods: CHashMethods = ctx.datastructures[self] return f"{ctx.feed}{methods['exists']}({map.obj.map}, {ctx(idx)})" @@ -345,21 +342,11 @@ def c_storemap( def c_loadmap(self, ctx: "CContext", map: "Stack", idx: "AssemblyExpression"): """ Get an expression where we can get the value corresponding to a key. - - TODO: Do we want to use pointers to tuples (standard across everything - but requires lifecycle management) - Or do we want to just use tuple values? - - This load is incomplete without this design decision. """ assert isinstance(map.obj, CMapFields) methods: CHashMethods = ctx.datastructures[self] - valuetype_c = ctx.ctype_name(c_type(self.value_type)._type_) - value = ctx.freshen("value") - ctx.exec(f"{ctx.feed}{valuetype_c} {value};") - ctx.exec(f"{ctx.feed}{methods['load']}({map.obj.map}, {ctx(idx)}, &{value});") - return value + return (f"{methods['load']}({map.obj.map}, {ctx(idx)})") def c_unpack(self, ctx: "CContext", var_n: str, val: AssemblyExpression): """ @@ -585,7 +572,7 @@ def construct_from_numba(self, numba_map): if __name__ == "__main__": - table = CHashTable(2, 3, {}) + table = CHashTable(2, 3, {(1, 2): (1, 4, 3)}) table.store((2, 3), (3, 2, 3)) print(table.exists((2, 3))) print(table.load((2, 3))) From 55dea611961e547ddb461c6f6c0053abe998a378 Mon Sep 17 00:00:00 2001 From: Juni Kim Date: Thu, 27 Nov 2025 10:24:51 -0500 Subject: [PATCH 12/44] c.py invocation and serialization errors fixed --- src/finchlite/codegen/c.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/finchlite/codegen/c.py b/src/finchlite/codegen/c.py index aac87244..f84a5b29 100644 --- a/src/finchlite/codegen/c.py +++ b/src/finchlite/codegen/c.py @@ -143,7 +143,7 @@ def construct_from_c(fmt, c_obj): return fmt.construct_from_c(c_obj) try: return query_property(fmt, "construct_from_c", "__attr__", c_obj) - except NotImplementedError: + except AttributeError: return fmt(c_obj) @@ -257,11 +257,9 @@ def __call__(self, *args): self.argtypes, args, serial_args, strict=False ): deserialize_from_c(type_, arg, serial_arg) - if hasattr(self.ret_type, "construct_from_c"): - return construct_from_c(res.ftype, res) if self.ret_type is type(None): return None - return self.ret_type(res) + return construct_from_c(self.ret_type, res) class CModule: @@ -318,7 +316,7 @@ def __call__(self, prgm): for func in prgm.funcs: match func: case asm.Function(asm.Variable(func_name, return_t), args, _): - return_t = c_type(return_t) + #return_t = c_type(return_t) arg_ts = [arg.result_format for arg in args] kern = CKernel(getattr(lib, func_name), return_t, arg_ts) kernels[func_name] = kern @@ -1168,11 +1166,16 @@ def serialize_tuple_to_c(fmt, obj): "__attr__", serialize_tuple_to_c, ) + +def tuple_construct_from_c(fmt: TupleFType, c_struct): + args = [getattr(c_struct, name) for name in fmt.struct_fieldnames] + return tuple(args) + register_property( TupleFType, "construct_from_c", "__attr__", - lambda fmt, obj, c_tuple: tuple(c_tuple), + tuple_construct_from_c, ) register_property( From 7f8c2b08ffa41482b1c7f4894d3baa96de174135 Mon Sep 17 00:00:00 2001 From: Juni Kim Date: Thu, 27 Nov 2025 11:55:19 -0500 Subject: [PATCH 13/44] need to work through numba stuff --- src/finchlite/codegen/hashtable.py | 45 ++++++++++++++++------------ tests/test_codegen.py | 47 +++++++++++++++++++++++------- 2 files changed, 62 insertions(+), 30 deletions(-) diff --git a/src/finchlite/codegen/hashtable.py b/src/finchlite/codegen/hashtable.py index bc857209..82f1627b 100644 --- a/src/finchlite/codegen/hashtable.py +++ b/src/finchlite/codegen/hashtable.py @@ -15,7 +15,7 @@ load_shared_lib, serialize_to_c, ) -from finchlite.codegen.numba_backend import NumbaContext, NumbaMapFType, NumbaStackFType +from finchlite.codegen.numba_backend import NumbaContext, NumbaMapFType, NumbaStackFType, numba_type from finchlite.finch_assembly.map import Map, MapFType from finchlite.finch_assembly.nodes import AssemblyExpression, Stack from finchlite.finch_assembly.struct import TupleFType @@ -95,6 +95,7 @@ def gen_code( ctx: "CContext", key_type: "TupleFType", value_type: "TupleFType", + inline: bool = False, ) -> tuple[CHashMethods, str]: assert isinstance(key_type, TupleFType) @@ -107,10 +108,13 @@ def gen_code( valuetype_c = ctx.ctype_name(c_type(value_type)) hmap_t = ctx.freshen(f"hmap", key_len, value_len) - ctx.add_header(f"#define T {hmap_t}, {keytype_c}, {valuetype_c}") - ctx.add_header("#define i_eq c_memcmp_eq") ctx.add_header("#include ") - ctx.add_header(f'#include "{hashmap_h}"') + + # these headers should just be added to the headers list. + # deduplication is catastrohpic here. + ctx.headers.append(f"#define T {hmap_t}, {keytype_c}, {valuetype_c}") + ctx.headers.append("#define i_eq c_memcmp_eq") + ctx.headers.append(f'#include "{hashmap_h}"') methods: CHashMethods = { "init": ctx.freshen("finch_hmap_init", key_len, value_len), @@ -121,27 +125,28 @@ def gen_code( } # register these methods in the datastructures. ctx.datastructures[CHashTableFType(key_len, value_len)] = methods + inline_s = "static inline " if inline else "" # basically for the load functions, you need to provide a variable that # can be copied. # Yeah, so which API's should we use for load and store? lib_code = f""" -inline void* {methods['init']}() {{ +{inline_s}void* {methods['init']}() {{ void* ptr = malloc(sizeof({hmap_t})); memset(ptr, 0, sizeof({hmap_t})); return ptr; }} -inline bool {methods['exists']}({hmap_t} *map, {keytype_c} key) {{ +{inline_s}bool {methods['exists']}({hmap_t} *map, {keytype_c} key) {{ return {hmap_t}_contains(map, key); }} -inline {valuetype_c} {methods['load']}({hmap_t} *map, {keytype_c} key) {{ +{inline_s}{valuetype_c} {methods['load']}({hmap_t} *map, {keytype_c} key) {{ const {valuetype_c}* internal_val = {hmap_t}_at(map, key); return *internal_val; }} -inline void {methods['store']}({hmap_t} *map, {keytype_c} key, {valuetype_c} value) {{ +{inline_s}void {methods['store']}({hmap_t} *map, {keytype_c} key, {valuetype_c} value) {{ {hmap_t}_insert_or_assign(map, key, value); }} -inline void {methods['cleanup']}(void* ptr) {{ +{inline_s}void {methods['cleanup']}(void* ptr) {{ {hmap_t}* hptr = ptr; {hmap_t}_drop(hptr); free(hptr); @@ -243,10 +248,10 @@ def load(self, idx): assert _is_integer_tuple(idx, self.key_len) KeyStruct = c_type(self.ftype.key_type) c_key = KeyStruct(*idx) - c_value = getattr(self.lib.library, self.lib.methods["load"])( - self.map, c_key + c_value = getattr(self.lib.library, self.lib.methods["load"])(self.map, c_key) + return tuple( + getattr(c_value, f) for f in self.ftype.value_type.struct_fieldnames ) - return tuple(getattr(c_value, f) for f in self.ftype.value_type.struct_fieldnames) def store(self, idx, val): assert _is_integer_tuple(idx, self.key_len) @@ -255,9 +260,7 @@ def store(self, idx, val): ValueStruct = c_type(self.ftype.value_type) c_key = KeyStruct(*idx) c_value = ValueStruct(*val) - getattr(self.lib.library, self.lib.methods["store"])( - self.map, c_key, c_value - ) + getattr(self.lib.library, self.lib.methods["store"])(self.map, c_key, c_value) def __str__(self): return f"c_hashtable({self.map})" @@ -337,7 +340,7 @@ def c_storemap( ): assert isinstance(map.obj, CMapFields) methods: CHashMethods = ctx.datastructures[self] - ctx.exec(f"{methods['store']}({map.obj.map}, {ctx(idx)}, {ctx(value)});") + ctx.exec(f"{ctx.feed}{methods['store']}({map.obj.map}, {ctx(idx)}, {ctx(value)});") def c_loadmap(self, ctx: "CContext", map: "Stack", idx: "AssemblyExpression"): """ @@ -346,7 +349,7 @@ def c_loadmap(self, ctx: "CContext", map: "Stack", idx: "AssemblyExpression"): assert isinstance(map.obj, CMapFields) methods: CHashMethods = ctx.datastructures[self] - return (f"{methods['load']}({map.obj.map}, {ctx(idx)})") + return f"{methods['load']}({map.obj.map}, {ctx(idx)})" def c_unpack(self, ctx: "CContext", var_n: str, val: AssemblyExpression): """ @@ -356,7 +359,10 @@ def c_unpack(self, ctx: "CContext", var_n: str, val: AssemblyExpression): data = ctx.freshen(var_n, "data") # Add all the stupid header stuff from above. ctx.add_datastructure( - self, lambda ctx: CHashTable.gen_code(ctx, self.key_type, self.value_type) + self, + lambda ctx: CHashTable.gen_code( + ctx, self.key_type, self.value_type, inline=True + ), ) ctx.exec(f"{ctx.feed}void* {data} = {ctx(val)}->map;") @@ -510,7 +516,8 @@ def numba_jitclass_type(self) -> numba.types.Type: return numba.types.ListType(numba.types.DictType(key_t, value_t)) def numba_type(self): - return list[dict] + return self.numba_jitclass_type() + # return list[dict[key_t, val_t]] def numba_existsmap( self, ctx: "NumbaContext", map: "Stack", idx: "AssemblyExpression" diff --git a/tests/test_codegen.py b/tests/test_codegen.py index 44fe7066..c9d180cd 100644 --- a/tests/test_codegen.py +++ b/tests/test_codegen.py @@ -24,7 +24,7 @@ NumpyBufferFType, SafeBuffer, ) -from finchlite.codegen.c import construct_from_c, deserialize_from_c, serialize_to_c +from finchlite.codegen.c import c_type, construct_from_c, deserialize_from_c, serialize_to_c from finchlite.codegen.malloc_buffer import MallocBuffer from finchlite.codegen.numba_backend import ( construct_from_numba, @@ -940,7 +940,7 @@ def test_hashtable_c(): module = asm.Module( ( asm.Function( - asm.Variable("setidx", np.intp), + asm.Variable("setidx", val_type), (table_v, key_v, val_v), asm.Block( ( @@ -950,18 +950,38 @@ def test_hashtable_c(): key_v, val_v, ), - asm.Return(asm.Literal(0)), + asm.Return( + asm.LoadMap( + table_slt, + key_v + ) + ), ) ), ), + asm.Function( + asm.Variable("exists", bool), + (table_v, key_v), + asm.Block( + ( + asm.Unpack(table_slt, table_v), + asm.Return( + asm.ExistsMap( + table_slt, + key_v + ) + ) + ) + ) + ) ) ) compiled = CCompiler()(module) - key = key_type(1, 2) - val = key_type(2, 3, 4) - compiled.setidx(table, key, val) - assert table.exists((1, 2)) - assert not table.exists((1, 3)) + assert compiled.setidx(table, (1, 2), (2, 3, 4)) == (2, 3, 4) + assert compiled.setidx(table, (1, 4), (3, 4, 1)) == (3, 4, 1) + assert compiled.exists(table, (1, 2)) + assert not compiled.exists(table, (1, 3)) + assert not compiled.exists(table, (2, 3)) def test_hashtable_numba(): @@ -978,7 +998,7 @@ def test_hashtable_numba(): module = asm.Module( ( asm.Function( - asm.Variable("setidx", np.intp), + asm.Variable("setidx", val_type), (table_v, key_v, val_v), asm.Block( ( @@ -988,7 +1008,12 @@ def test_hashtable_numba(): key_v, val_v, ), - asm.Return(asm.Literal(0)), + asm.Return( + asm.LoadMap( + table_slt, + key_v + ) + ), ) ), ), @@ -997,6 +1022,6 @@ def test_hashtable_numba(): compiled = NumbaCompiler()(module) key = key_type(1, 2) val = key_type(2, 3, 4) - compiled.setidx(table, key, val) + result = compiled.setidx(table, key, val) assert table.exists((1, 2)) assert not table.exists((1, 3)) From 41134698b3c59c4a1272e3087c7461fe72faaf7b Mon Sep 17 00:00:00 2001 From: Juni Kim Date: Thu, 27 Nov 2025 10:24:51 -0500 Subject: [PATCH 14/44] c.py invocation and serialization errors fixed --- src/finchlite/codegen/c.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/finchlite/codegen/c.py b/src/finchlite/codegen/c.py index 47561709..d67e2aad 100644 --- a/src/finchlite/codegen/c.py +++ b/src/finchlite/codegen/c.py @@ -140,7 +140,7 @@ def construct_from_c(fmt, c_obj): return fmt.construct_from_c(c_obj) try: return query_property(fmt, "construct_from_c", "__attr__", c_obj) - except NotImplementedError: + except AttributeError: return fmt(c_obj) @@ -254,11 +254,9 @@ def __call__(self, *args): self.argtypes, args, serial_args, strict=False ): deserialize_from_c(type_, arg, serial_arg) - if hasattr(self.ret_type, "construct_from_c"): - return construct_from_c(res.ftype, res) if self.ret_type is type(None): return None - return self.ret_type(res) + return construct_from_c(self.ret_type, res) class CModule: @@ -315,7 +313,7 @@ def __call__(self, prgm): for func in prgm.funcs: match func: case asm.Function(asm.Variable(func_name, return_t), args, _): - return_t = c_type(return_t) + #return_t = c_type(return_t) arg_ts = [arg.result_format for arg in args] kern = CKernel(getattr(lib, func_name), return_t, arg_ts) kernels[func_name] = kern @@ -1113,11 +1111,16 @@ def serialize_tuple_to_c(fmt, obj): "__attr__", serialize_tuple_to_c, ) + +def tuple_construct_from_c(fmt: TupleFType, c_struct): + args = [getattr(c_struct, name) for name in fmt.struct_fieldnames] + return tuple(args) + register_property( TupleFType, "construct_from_c", "__attr__", - lambda fmt, obj, c_tuple: tuple(c_tuple), + tuple_construct_from_c, ) register_property( From 7f1431e3da1ec1fce0a1e4f02bcf710c9ac68a2f Mon Sep 17 00:00:00 2001 From: Juni Kim Date: Thu, 27 Nov 2025 11:57:27 -0500 Subject: [PATCH 15/44] ruff format --- src/finchlite/codegen/c.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/finchlite/codegen/c.py b/src/finchlite/codegen/c.py index d67e2aad..ba1d8c60 100644 --- a/src/finchlite/codegen/c.py +++ b/src/finchlite/codegen/c.py @@ -313,7 +313,7 @@ def __call__(self, prgm): for func in prgm.funcs: match func: case asm.Function(asm.Variable(func_name, return_t), args, _): - #return_t = c_type(return_t) + # return_t = c_type(return_t) arg_ts = [arg.result_format for arg in args] kern = CKernel(getattr(lib, func_name), return_t, arg_ts) kernels[func_name] = kern @@ -1112,10 +1112,12 @@ def serialize_tuple_to_c(fmt, obj): serialize_tuple_to_c, ) + def tuple_construct_from_c(fmt: TupleFType, c_struct): args = [getattr(c_struct, name) for name in fmt.struct_fieldnames] return tuple(args) + register_property( TupleFType, "construct_from_c", From 21f281593d15409cbf5202715cc3132478daf2b5 Mon Sep 17 00:00:00 2001 From: Juni Kim Date: Thu, 27 Nov 2025 14:02:00 -0500 Subject: [PATCH 16/44] numpy serialization also fixed --- src/finchlite/codegen/c.py | 14 +++++++++++++- tests/scripts/safebufferaccess.py | 21 +++++++++++---------- tests/test_codegen.py | 2 +- 3 files changed, 25 insertions(+), 12 deletions(-) diff --git a/src/finchlite/codegen/c.py b/src/finchlite/codegen/c.py index ba1d8c60..f79f6a05 100644 --- a/src/finchlite/codegen/c.py +++ b/src/finchlite/codegen/c.py @@ -204,12 +204,24 @@ def construct_from_c(fmt, c_obj): register_property(t, "construct_from_c", "__attr__", lambda fmt, c_value: c_value) register_property(t, "numba_type", "__attr__", lambda t: t) + +def scalar_to_ctypes_copy(fmt, obj): + """ + This hack is required because it turns out that scalars don't own memory or smth + """ + arr = np.array([obj], dtype=obj.dtype, copy=True) + scalar_ctype = np.ctypeslib.as_ctypes_type(obj.dtype) + ptr_ctype = ctypes.POINTER(scalar_ctype) + return arr.ctypes.data_as(ptr_ctype).contents + + register_property( np.generic, "serialize_to_c", "__attr__", - lambda fmt, obj: np.ctypeslib.as_ctypes(obj), + scalar_to_ctypes_copy, ) + # pass by value -> no op register_property( np.generic, diff --git a/tests/scripts/safebufferaccess.py b/tests/scripts/safebufferaccess.py index af133cce..9e279e03 100755 --- a/tests/scripts/safebufferaccess.py +++ b/tests/scripts/safebufferaccess.py @@ -9,7 +9,6 @@ """ import argparse -import ctypes import numpy as np @@ -25,22 +24,22 @@ subparser = parser.add_subparsers(required=True, dest="subparser_name") load = subparser.add_parser("load", help="attempt to load some element") -load.add_argument("index", type=int, help="the index to load") +load.add_argument("index", type=np.intp, help="the index to load") store = subparser.add_parser("store", help="attempt to store into some element") -store.add_argument("index", type=int, help="the index to load") -store.add_argument("value", type=int, help="the value to store") +store.add_argument("index", type=np.intp, help="the index to load") +store.add_argument("value", type=np.int64, help="the value to store") args = parser.parse_args() -a = np.array(range(args.size), dtype=ctypes.c_int64) +a = np.array(range(args.size), dtype=np.int64) ab = NumpyBuffer(a) ab_safe = SafeBuffer(ab) ab_v = asm.Variable("a", ab_safe.ftype) ab_slt = asm.Slot("a_", ab_safe.ftype) -idx = asm.Variable("idx", ctypes.c_size_t) -val = asm.Variable("val", ctypes.c_int64) +idx = asm.Variable("idx", np.intp) +val = asm.Variable("val", np.int64) res_var = asm.Variable("val", ab_safe.ftype.element_type) res_var2 = asm.Variable("val2", ab_safe.ftype.element_type) @@ -64,6 +63,7 @@ res_var2, asm.Load(ab_slt, idx), ), + asm.Repack(ab_slt), asm.Return(res_var), ) ), @@ -79,7 +79,8 @@ idx, val, ), - asm.Return(asm.Literal(ctypes.c_int64(0))), + asm.Repack(ab_slt), + asm.Return(asm.Literal(0)), ) ), ), @@ -91,8 +92,8 @@ match args.subparser_name: case "load": - print(access(ab_safe, ctypes.c_size_t(args.index)).value) + print(access(ab_safe, args.index)) case "store": - change(ab_safe, ctypes.c_size_t(args.index), ctypes.c_int64(args.value)) + change(ab_safe, args.index, args.value) arr = [str(ab_safe.load(i)) for i in range(args.size)] print(f"[{' '.join(arr)}]") diff --git a/tests/test_codegen.py b/tests/test_codegen.py index 202c406e..a06e22a5 100644 --- a/tests/test_codegen.py +++ b/tests/test_codegen.py @@ -260,7 +260,7 @@ def test_malloc_resize(new_size): ) ) mod = CCompiler()(prgm) - assert mod.length(ab).value == new_size + assert mod.length(ab) == new_size assert ab.length() == new_size for i in range(new_size): assert ab.load(i) == 0 if i >= len(a) else a[i] From 23d4703777598677827b7230c903510e3e7a993e Mon Sep 17 00:00:00 2001 From: Juni Kim Date: Thu, 27 Nov 2025 14:13:39 -0500 Subject: [PATCH 17/44] format and mypy errors --- src/finchlite/codegen/c.py | 6 ++-- src/finchlite/codegen/hashtable.py | 28 ++++++++-------- src/finchlite/codegen/numba_backend.py | 1 + src/finchlite/codegen/numpy_buffer.py | 8 ++++- src/finchlite/finch_assembly/__init__.py | 6 ++-- src/finchlite/finch_assembly/map.py | 6 ++-- src/finchlite/finch_assembly/nodes.py | 13 ++++---- src/finchlite/finch_assembly/type_checker.py | 2 +- tests/test_codegen.py | 35 ++++++++------------ 9 files changed, 55 insertions(+), 50 deletions(-) diff --git a/src/finchlite/codegen/c.py b/src/finchlite/codegen/c.py index da04164c..27b75f43 100644 --- a/src/finchlite/codegen/c.py +++ b/src/finchlite/codegen/c.py @@ -6,15 +6,15 @@ import tempfile from abc import ABC, abstractmethod from collections import namedtuple +from collections.abc import Callable from functools import lru_cache from pathlib import Path from types import NoneType -from typing import Any, Callable +from typing import Any import numpy as np from finchlite.finch_assembly.map import FType, MapFType -from finchlite.finch_assembly.nodes import AssemblyExpression from .. import finch_assembly as asm from ..algebra import query_property, register_property @@ -1178,10 +1178,12 @@ def serialize_tuple_to_c(fmt, obj): serialize_tuple_to_c, ) + def tuple_construct_from_c(fmt: TupleFType, c_struct): args = [getattr(c_struct, name) for name in fmt.struct_fieldnames] return tuple(args) + register_property( TupleFType, "construct_from_c", diff --git a/src/finchlite/codegen/hashtable.py b/src/finchlite/codegen/hashtable.py index 82f1627b..b4d0a6e1 100644 --- a/src/finchlite/codegen/hashtable.py +++ b/src/finchlite/codegen/hashtable.py @@ -4,19 +4,20 @@ from typing import NamedTuple, TypedDict import numba -import numpy as np from finchlite.codegen.c import ( CContext, CMapFType, CStackFType, c_type, - construct_from_c, load_shared_lib, - serialize_to_c, ) -from finchlite.codegen.numba_backend import NumbaContext, NumbaMapFType, NumbaStackFType, numba_type -from finchlite.finch_assembly.map import Map, MapFType +from finchlite.codegen.numba_backend import ( + NumbaContext, + NumbaMapFType, + NumbaStackFType, +) +from finchlite.finch_assembly.map import Map from finchlite.finch_assembly.nodes import AssemblyExpression, Stack from finchlite.finch_assembly.struct import TupleFType @@ -97,7 +98,6 @@ def gen_code( value_type: "TupleFType", inline: bool = False, ) -> tuple[CHashMethods, str]: - assert isinstance(key_type, TupleFType) assert isinstance(value_type, TupleFType) @@ -106,7 +106,7 @@ def gen_code( # dereference both key and value types; as given, they are both pointers. keytype_c = ctx.ctype_name(c_type(key_type)) valuetype_c = ctx.ctype_name(c_type(value_type)) - hmap_t = ctx.freshen(f"hmap", key_len, value_len) + hmap_t = ctx.freshen("hmap", key_len, value_len) ctx.add_header("#include ") @@ -131,22 +131,22 @@ def gen_code( # can be copied. # Yeah, so which API's should we use for load and store? lib_code = f""" -{inline_s}void* {methods['init']}() {{ +{inline_s}void* {methods["init"]}() {{ void* ptr = malloc(sizeof({hmap_t})); memset(ptr, 0, sizeof({hmap_t})); return ptr; }} -{inline_s}bool {methods['exists']}({hmap_t} *map, {keytype_c} key) {{ +{inline_s}bool {methods["exists"]}({hmap_t} *map, {keytype_c} key) {{ return {hmap_t}_contains(map, key); }} -{inline_s}{valuetype_c} {methods['load']}({hmap_t} *map, {keytype_c} key) {{ +{inline_s}{valuetype_c} {methods["load"]}({hmap_t} *map, {keytype_c} key) {{ const {valuetype_c}* internal_val = {hmap_t}_at(map, key); return *internal_val; }} -{inline_s}void {methods['store']}({hmap_t} *map, {keytype_c} key, {valuetype_c} value) {{ +{inline_s}void {methods["store"]}({hmap_t} *map, {keytype_c} key, {valuetype_c} value) {{ {hmap_t}_insert_or_assign(map, key, value); }} -{inline_s}void {methods['cleanup']}(void* ptr) {{ +{inline_s}void {methods["cleanup"]}(void* ptr) {{ {hmap_t}* hptr = ptr; {hmap_t}_drop(hptr); free(hptr); @@ -340,7 +340,9 @@ def c_storemap( ): assert isinstance(map.obj, CMapFields) methods: CHashMethods = ctx.datastructures[self] - ctx.exec(f"{ctx.feed}{methods['store']}({map.obj.map}, {ctx(idx)}, {ctx(value)});") + ctx.exec( + f"{ctx.feed}{methods['store']}({map.obj.map}, {ctx(idx)}, {ctx(value)});" + ) def c_loadmap(self, ctx: "CContext", map: "Stack", idx: "AssemblyExpression"): """ diff --git a/src/finchlite/codegen/numba_backend.py b/src/finchlite/codegen/numba_backend.py index 1b9bc8e6..2003aedb 100644 --- a/src/finchlite/codegen/numba_backend.py +++ b/src/finchlite/codegen/numba_backend.py @@ -289,6 +289,7 @@ def numba_storemap(self, ctx, buffer, idx, value): """ ... + class NumbaBufferFType(BufferFType, NumbaArgumentFType, ABC): @abstractmethod def numba_length(self, ctx: "NumbaContext", buffer): diff --git a/src/finchlite/codegen/numpy_buffer.py b/src/finchlite/codegen/numpy_buffer.py index 3c22842e..c567e595 100644 --- a/src/finchlite/codegen/numpy_buffer.py +++ b/src/finchlite/codegen/numpy_buffer.py @@ -137,7 +137,13 @@ def c_load(self, ctx: "CContext", buf: "Stack", idx: "AssemblyExpression"): assert isinstance(buf.obj, CBufferFields) return f"({buf.obj.data})[{ctx(idx)}]" - def c_store(self, ctx: "CContext", buf: "Stack", idx: "AssemblyExpression", value: "AssemblyExpression"): + def c_store( + self, + ctx: "CContext", + buf: "Stack", + idx: "AssemblyExpression", + value: "AssemblyExpression", + ): assert isinstance(buf.obj, CBufferFields) ctx.exec(f"{ctx.feed}({buf.obj.data})[{ctx(idx)}] = {ctx(value)};") diff --git a/src/finchlite/finch_assembly/__init__.py b/src/finchlite/finch_assembly/__init__.py index 5b0ddd98..e9644bc6 100644 --- a/src/finchlite/finch_assembly/__init__.py +++ b/src/finchlite/finch_assembly/__init__.py @@ -14,6 +14,7 @@ Break, BufferLoop, Call, + ExistsMap, ForLoop, Function, GetAttr, @@ -22,6 +23,7 @@ Length, Literal, Load, + LoadMap, Module, Print, Repack, @@ -31,13 +33,11 @@ Slot, Stack, Store, + StoreMap, TaggedVariable, Unpack, Variable, WhileLoop, - StoreMap, - ExistsMap, - LoadMap ) from .struct import AssemblyStructFType, NamedTupleFType, TupleFType from .type_checker import AssemblyTypeChecker, AssemblyTypeError, assembly_check_types diff --git a/src/finchlite/finch_assembly/map.py b/src/finchlite/finch_assembly/map.py index d6adc548..1ddcd105 100644 --- a/src/finchlite/finch_assembly/map.py +++ b/src/finchlite/finch_assembly/map.py @@ -1,4 +1,5 @@ from abc import ABC, abstractmethod + from ..symbolic import FType, FTyped @@ -10,8 +11,9 @@ class Map(FTyped, ABC): """ @abstractmethod - def __init__(self, key_len: int, value_len: int, map: "dict[tuple,tuple] | None"): - ... + def __init__( + self, key_len: int, value_len: int, map: "dict[tuple,tuple] | None" + ): ... @property def element_type(self): diff --git a/src/finchlite/finch_assembly/nodes.py b/src/finchlite/finch_assembly/nodes.py index b5f4f92b..5384ff12 100644 --- a/src/finchlite/finch_assembly/nodes.py +++ b/src/finchlite/finch_assembly/nodes.py @@ -373,8 +373,7 @@ class ExistsMap(AssemblyExpression, AssemblyTree): Attributes: map: The map to load from. - index1: The first integer in the pair - index2: The second integer in the pair + index: The key to check for existence. """ map: Slot | Stack @@ -770,15 +769,17 @@ def __call__(self, prgm: AssemblyNode): return None case Load(buf, idx): return f"load({self(buf)}, {self(idx)})" - case LoadMap(map, idx1, idx2): - return f"loadmap({self(map)}, {self(idx1)}, {self(idx2)})" + case LoadMap(map, idx): + return f"loadmap({self(map)}, {self(idx)})" + case ExistsMap(map, idx): + return f"existsmap({self(map)}, {self(idx)})" case Slot(name, type_): return f"slot({name}, {qual_str(type_)})" case Store(buf, idx, val): self.exec(f"{feed}store({self(buf)}, {self(idx)}, {self(val)})") return None - case StoreMap(map, idx1, idx2, val): - self.exec(f"{feed}storemap({self(map)}, {self(idx1)}, {self(idx2)}, {self(val)})") + case StoreMap(map, idx, val): + self.exec(f"{feed}storemap({self(map)}, {self(idx)}, {self(val)})") return None case Resize(buf, size): self.exec(f"{feed}resize({self(buf)}, {self(size)})") diff --git a/src/finchlite/finch_assembly/type_checker.py b/src/finchlite/finch_assembly/type_checker.py index 06fb7541..adaca765 100644 --- a/src/finchlite/finch_assembly/type_checker.py +++ b/src/finchlite/finch_assembly/type_checker.py @@ -6,8 +6,8 @@ from ..symbolic import FType, ScopedDict, ftype from . import nodes as asm from .buffer import BufferFType -from .struct import AssemblyStructFType from .map import MapFType +from .struct import AssemblyStructFType class AssemblyTypeError(Exception): diff --git a/tests/test_codegen.py b/tests/test_codegen.py index e20e35d2..a48831cf 100644 --- a/tests/test_codegen.py +++ b/tests/test_codegen.py @@ -12,7 +12,6 @@ from numpy.testing import assert_equal import finchlite -from finchlite.codegen.hashtable import CHashTable, NumbaHashTable import finchlite.finch_assembly as asm from finchlite import ftype from finchlite.codegen import ( @@ -24,7 +23,12 @@ NumpyBufferFType, SafeBuffer, ) -from finchlite.codegen.c import c_type, construct_from_c, deserialize_from_c, serialize_to_c +from finchlite.codegen.c import ( + construct_from_c, + deserialize_from_c, + serialize_to_c, +) +from finchlite.codegen.hashtable import CHashTable, NumbaHashTable from finchlite.codegen.malloc_buffer import MallocBuffer from finchlite.codegen.numba_backend import ( construct_from_numba, @@ -32,6 +36,7 @@ serialize_to_numba, ) + def test_add_function(): c_code = """ #include @@ -926,6 +931,7 @@ def test_e2e_numba(): finchlite.set_default_scheduler(ctx=ctx) + def test_hashtable_c(): table = CHashTable(2, 3) @@ -950,12 +956,7 @@ def test_hashtable_c(): key_v, val_v, ), - asm.Return( - asm.LoadMap( - table_slt, - key_v - ) - ), + asm.Return(asm.LoadMap(table_slt, key_v)), ) ), ), @@ -965,15 +966,10 @@ def test_hashtable_c(): asm.Block( ( asm.Unpack(table_slt, table_v), - asm.Return( - asm.ExistsMap( - table_slt, - key_v - ) - ) + asm.Return(asm.ExistsMap(table_slt, key_v)), ) - ) - ) + ), + ), ) ) compiled = CCompiler()(module) @@ -1008,12 +1004,7 @@ def test_hashtable_numba(): key_v, val_v, ), - asm.Return( - asm.LoadMap( - table_slt, - key_v - ) - ), + asm.Return(asm.LoadMap(table_slt, key_v)), ) ), ), From 57e86b6ee2f722217979c6a3d2f85f3955cbda05 Mon Sep 17 00:00:00 2001 From: Juni Kim Date: Fri, 28 Nov 2025 16:21:06 -0500 Subject: [PATCH 18/44] numba and assembly bugs fixed too --- src/finchlite/codegen/numba_backend.py | 24 ++++++++++++++++++++- src/finchlite/finch_assembly/interpreter.py | 2 +- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/finchlite/codegen/numba_backend.py b/src/finchlite/codegen/numba_backend.py index 2003aedb..e579b83f 100644 --- a/src/finchlite/codegen/numba_backend.py +++ b/src/finchlite/codegen/numba_backend.py @@ -36,7 +36,10 @@ def numba_type(t): """ if hasattr(t, "numba_type"): return t.numba_type() - return query_property(t, "numba_type", "__attr__") + try: + return query_property(t, "numba_type", "__attr__") + except AttributeError: + return t def numba_jitclass_type(t): @@ -797,6 +800,25 @@ def serialize_tuple_to_numba(fmt, obj): serialize_tuple_to_numba, ) +# trivial ser/deser +for t in ( + int, bool, float +): + register_property( + t, + "construct_from_numba", + "__attr__", + lambda fmt, obj: obj, + ) + + register_property( + t, + "serialize_to_numba", + "__attr__", + lambda fmt, obj: obj, + ) + + register_property( operator.add, "numba_literal", diff --git a/src/finchlite/finch_assembly/interpreter.py b/src/finchlite/finch_assembly/interpreter.py index 48bbfef2..0d6c7996 100644 --- a/src/finchlite/finch_assembly/interpreter.py +++ b/src/finchlite/finch_assembly/interpreter.py @@ -325,7 +325,7 @@ def my_func(*args_e): ctx_2(body) if ctx_2.function_state.should_halt: ret_e = ctx_2.function_state.return_value - if not check_isinstance(ret_e, ret_t): + if not fisinstance(ret_e, ret_t): raise TypeError( f"Return value {ret_e} is not of type {ret_t} " f"for function '{func_n}'." From 65040f2d73860f1fa7e44aaf2247377beea2d59c Mon Sep 17 00:00:00 2001 From: Juni Kim Date: Fri, 28 Nov 2025 17:04:48 -0500 Subject: [PATCH 19/44] generate tuplify and re-conversion code for numba How is the numba compiler backend even more horrible than C --- src/finchlite/codegen/hashtable.py | 106 ++++++++++++++++++++--------- tests/test_codegen.py | 62 +++++------------ 2 files changed, 90 insertions(+), 78 deletions(-) diff --git a/src/finchlite/codegen/hashtable.py b/src/finchlite/codegen/hashtable.py index b4d0a6e1..54b8cfa6 100644 --- a/src/finchlite/codegen/hashtable.py +++ b/src/finchlite/codegen/hashtable.py @@ -4,22 +4,17 @@ from typing import NamedTuple, TypedDict import numba - -from finchlite.codegen.c import ( - CContext, - CMapFType, - CStackFType, - c_type, - load_shared_lib, -) -from finchlite.codegen.numba_backend import ( - NumbaContext, - NumbaMapFType, - NumbaStackFType, -) +import numba.typed + +from finchlite.codegen.c import (CContext, CMapFType, CStackFType, c_type, construct_from_c, + load_shared_lib) +from finchlite.codegen.numba_backend import (NumbaContext, NumbaMapFType, + NumbaStackFType, + construct_from_numba, numba_type, + serialize_to_numba) from finchlite.finch_assembly.map import Map from finchlite.finch_assembly.nodes import AssemblyExpression, Stack -from finchlite.finch_assembly.struct import TupleFType +from finchlite.finch_assembly.struct import AssemblyStructFType, TupleFType stcpath = Path(__file__).parent / "stc" hashmap_h = stcpath / "stc" / "hashmap.h" @@ -59,6 +54,13 @@ def _is_integer_tuple(tup, size): def _int_tuple_ftype(size: int): return TupleFType.from_tuple(tuple(int for _ in range(size))) +def _tuplify(ftype: AssemblyStructFType, obj): + assert isinstance(ftype, AssemblyStructFType) + return tuple([ + ftype.struct_getattr(obj, attr) + for attr in ftype.struct_fieldnames + ]) + class CHashMapStruct(ctypes.Structure): _fields_ = [ @@ -237,23 +239,25 @@ def __init__( def __del__(self): getattr(self.lib.library, self.lib.methods["cleanup"])(self.map) - def exists(self, idx: tuple) -> bool: + def exists(self, idx) -> bool: + idx = _tuplify(self.ftype.key_type, idx) assert _is_integer_tuple(idx, self.key_len) KeyStruct = c_type(self.ftype.key_type) c_key = KeyStruct(*idx) - func = getattr(self.lib.library, self.lib.methods["exists"]) - return func(self.map, c_key) + c_value = getattr(self.lib.library, self.lib.methods["exists"])(self.map, c_key) + return bool(c_value) def load(self, idx): + idx = _tuplify(self.ftype.key_type, idx) assert _is_integer_tuple(idx, self.key_len) KeyStruct = c_type(self.ftype.key_type) c_key = KeyStruct(*idx) c_value = getattr(self.lib.library, self.lib.methods["load"])(self.map, c_key) - return tuple( - getattr(c_value, f) for f in self.ftype.value_type.struct_fieldnames - ) + return construct_from_c(self.ftype.value_type, c_value) def store(self, idx, val): + idx = _tuplify(self.ftype.key_type, idx) + val = _tuplify(self.ftype.value_type, val) assert _is_integer_tuple(idx, self.key_len) assert _is_integer_tuple(val, self.value_len) KeyStruct = c_type(self.ftype.key_type) @@ -420,9 +424,22 @@ def __init__(self, key_len, value_len, map: "dict[tuple,tuple] | None" = None): Constructor for the Hash Table, which maps integer tuples to integer tuples. Takes three arguments: in_len: The """ + self.key_len = key_len + self.value_len = value_len + + self._key_type = _int_tuple_ftype(key_len) + self._value_type = _int_tuple_ftype(value_len) + + self._numba_key_type = numba.types.UniTuple(numba.types.int64, key_len) + self._numba_value_type = numba.types.UniTuple(numba.types.int64, value_len) + + if map is None: map = {} - self.map = {} + self.map = numba.typed.Dict.empty( + key_type=self._numba_key_type, + value_type=self._numba_value_type + ) for key, value in map.items(): if not _is_integer_tuple(key, key_len): raise TypeError( @@ -433,8 +450,6 @@ def __init__(self, key_len, value_len, map: "dict[tuple,tuple] | None" = None): f"Supplied value {key} is not a tuple of {value_len} integers" ) self.map[key] = value - self.key_len = key_len - self.value_len = value_len @property def ftype(self): @@ -444,14 +459,23 @@ def ftype(self): return NumbaHashTableFType(self.key_len, self.value_len) def exists(self, idx) -> bool: + """ + Exists function of the numba hash table. + It will accept an object with TupleFType and return a bool. + """ + idx = _tuplify(self.ftype.key_type, idx) assert _is_integer_tuple(idx, self.key_len) return idx in self.map def load(self, idx): + idx = _tuplify(self.ftype.key_type, idx) assert _is_integer_tuple(idx, self.key_len) - return self.map[idx] + result = self.map[idx] + return self.ftype.value_type(*result) def store(self, idx, val): + idx = _tuplify(self.ftype.key_type, idx) + val = _tuplify(self.ftype.value_type, val) assert _is_integer_tuple(idx, self.key_len) assert _is_integer_tuple(val, self.value_len) self.map[idx] = val @@ -513,25 +537,32 @@ def __hash__(self): """ def numba_jitclass_type(self) -> numba.types.Type: - key_t = numba.types.UniTuple(numba.types.int64, self.key_len) - value_t = numba.types.UniTuple(numba.types.int64, self.value_len) - return numba.types.ListType(numba.types.DictType(key_t, value_t)) + return numba.types.ListType(numba.types.DictType(self._numba_key_type, self._numba_value_type)) def numba_type(self): - return self.numba_jitclass_type() - # return list[dict[key_t, val_t]] + return list def numba_existsmap( self, ctx: "NumbaContext", map: "Stack", idx: "AssemblyExpression" ): assert isinstance(map.obj, NumbaMapFields) - return f"({ctx(idx)}) in {map.obj.map}" + tuple_fields = ",".join( + f"{ctx(idx)}.{field}" + for field in self.key_type.struct_fieldnames + ) + return f"tuple(({tuple_fields})) in {map.obj.map}" def numba_loadmap( self, ctx: "NumbaContext", map: "Stack", idx: "AssemblyExpression" ): assert isinstance(map.obj, NumbaMapFields) - return f"{map.obj.map}[{ctx(idx)}]" + tuple_fields = ",".join( + f"{ctx(idx)}.{field}" + for field in self.key_type.struct_fieldnames + ) + value_v = ctx.freshen("value") + ctx.exec(f"{ctx.feed}{value_v} = {map.obj.map}[tuple(({tuple_fields}))]") + return f"{ctx.full_name(numba_type(self.value_type))}(*{value_v})" def numba_storemap( self, @@ -541,7 +572,15 @@ def numba_storemap( value: "AssemblyExpression", ): assert isinstance(map.obj, NumbaMapFields) - ctx.exec(f"{ctx.feed}{map.obj.map}[{ctx(idx)}] = {ctx(value)}") + idx_fields = ",".join( + f"{ctx(idx)}.{field}" + for field in self.key_type.struct_fieldnames + ) + val_fields = ",".join( + f"{ctx(value)}.{field}" + for field in self.value_type.struct_fieldnames + ) + ctx.exec(f"{ctx.feed}{map.obj.map}[tuple(({idx_fields}))] = tuple(({val_fields}))") def numba_unpack( self, ctx: "NumbaContext", var_n: str, val: "AssemblyExpression" @@ -581,7 +620,8 @@ def construct_from_numba(self, numba_map): if __name__ == "__main__": - table = CHashTable(2, 3, {(1, 2): (1, 4, 3)}) + table = NumbaHashTable(2, 3, {(1, 2): (1, 4, 3)}) + print(numba_type(table.ftype.key_type)) table.store((2, 3), (3, 2, 3)) print(table.exists((2, 3))) print(table.load((2, 3))) diff --git a/tests/test_codegen.py b/tests/test_codegen.py index a48831cf..60a4931a 100644 --- a/tests/test_codegen.py +++ b/tests/test_codegen.py @@ -932,8 +932,17 @@ def test_e2e_numba(): finchlite.set_default_scheduler(ctx=ctx) -def test_hashtable_c(): - table = CHashTable(2, 3) +@pytest.mark.parametrize( + ["compiler", "tabletype"], + [ + (CCompiler(), CHashTable), + (asm.AssemblyInterpreter(), CHashTable), + (NumbaCompiler(), NumbaHashTable), + (asm.AssemblyInterpreter(), NumbaHashTable), + ], +) +def test_hashtable(compiler, tabletype): + table = tabletype(2, 3) table_v = asm.Variable("a", ftype(table)) table_slt = asm.Slot("a_", ftype(table)) @@ -972,47 +981,10 @@ def test_hashtable_c(): ), ) ) - compiled = CCompiler()(module) - assert compiled.setidx(table, (1, 2), (2, 3, 4)) == (2, 3, 4) - assert compiled.setidx(table, (1, 4), (3, 4, 1)) == (3, 4, 1) - assert compiled.exists(table, (1, 2)) - assert not compiled.exists(table, (1, 3)) - assert not compiled.exists(table, (2, 3)) - - -def test_hashtable_numba(): - table = NumbaHashTable(2, 3) - - table_v = asm.Variable("a", ftype(table)) - table_slt = asm.Slot("a_", ftype(table)) + compiled = compiler(module) + assert compiled.setidx(table, key_type(1, 2), val_type(2, 3, 4)) == val_type(2, 3, 4) + assert compiled.setidx(table, key_type(1, 4), val_type(3, 4, 1)) == val_type(3, 4, 1) + assert compiled.exists(table, key_type(1, 2)) - key_type = table.ftype.key_type - val_type = table.ftype.value_type - key_v = asm.Variable("key", key_type) - val_v = asm.Variable("val", val_type) - - module = asm.Module( - ( - asm.Function( - asm.Variable("setidx", val_type), - (table_v, key_v, val_v), - asm.Block( - ( - asm.Unpack(table_slt, table_v), - asm.StoreMap( - table_slt, - key_v, - val_v, - ), - asm.Return(asm.LoadMap(table_slt, key_v)), - ) - ), - ), - ) - ) - compiled = NumbaCompiler()(module) - key = key_type(1, 2) - val = key_type(2, 3, 4) - result = compiled.setidx(table, key, val) - assert table.exists((1, 2)) - assert not table.exists((1, 3)) + assert not compiled.exists(table, key_type(1, 3)) + assert not compiled.exists(table, val_type(2, 3)) From 9fe02efbe9659d54ef161b3be55890e97ef568af Mon Sep 17 00:00:00 2001 From: Juni Kim Date: Fri, 28 Nov 2025 17:14:39 -0500 Subject: [PATCH 20/44] All interface types fixed!!! --- src/finchlite/codegen/hashtable.py | 130 ++++++++++++++----------- src/finchlite/codegen/numba_backend.py | 4 +- tests/test_codegen.py | 8 +- 3 files changed, 78 insertions(+), 64 deletions(-) diff --git a/src/finchlite/codegen/hashtable.py b/src/finchlite/codegen/hashtable.py index 54b8cfa6..6a6db045 100644 --- a/src/finchlite/codegen/hashtable.py +++ b/src/finchlite/codegen/hashtable.py @@ -1,17 +1,26 @@ import ctypes from dataclasses import dataclass from pathlib import Path +from textwrap import dedent from typing import NamedTuple, TypedDict import numba import numba.typed -from finchlite.codegen.c import (CContext, CMapFType, CStackFType, c_type, construct_from_c, - load_shared_lib) -from finchlite.codegen.numba_backend import (NumbaContext, NumbaMapFType, - NumbaStackFType, - construct_from_numba, numba_type, - serialize_to_numba) +from finchlite.codegen.c import ( + CContext, + CMapFType, + CStackFType, + c_type, + construct_from_c, + load_shared_lib, +) +from finchlite.codegen.numba_backend import ( + NumbaContext, + NumbaMapFType, + NumbaStackFType, + numba_type, +) from finchlite.finch_assembly.map import Map from finchlite.finch_assembly.nodes import AssemblyExpression, Stack from finchlite.finch_assembly.struct import AssemblyStructFType, TupleFType @@ -45,21 +54,16 @@ class CMapFields(NamedTuple): def _is_integer_tuple(tup, size): if not isinstance(tup, tuple) or len(tup) != size: return False - for elt in tup: - if not isinstance(elt, int): - return False - return True + return all(isinstance(elt, int) for elt in tup) def _int_tuple_ftype(size: int): return TupleFType.from_tuple(tuple(int for _ in range(size))) + def _tuplify(ftype: AssemblyStructFType, obj): assert isinstance(ftype, AssemblyStructFType) - return tuple([ - ftype.struct_getattr(obj, attr) - for attr in ftype.struct_fieldnames - ]) + return tuple([ftype.struct_getattr(obj, attr) for attr in ftype.struct_fieldnames]) class CHashMapStruct(ctypes.Structure): @@ -113,7 +117,7 @@ def gen_code( ctx.add_header("#include ") # these headers should just be added to the headers list. - # deduplication is catastrohpic here. + # deduplication is catastrophic here. ctx.headers.append(f"#define T {hmap_t}, {keytype_c}, {valuetype_c}") ctx.headers.append("#define i_eq c_memcmp_eq") ctx.headers.append(f'#include "{hashmap_h}"') @@ -132,28 +136,45 @@ def gen_code( # basically for the load functions, you need to provide a variable that # can be copied. # Yeah, so which API's should we use for load and store? - lib_code = f""" -{inline_s}void* {methods["init"]}() {{ - void* ptr = malloc(sizeof({hmap_t})); - memset(ptr, 0, sizeof({hmap_t})); - return ptr; -}} -{inline_s}bool {methods["exists"]}({hmap_t} *map, {keytype_c} key) {{ - return {hmap_t}_contains(map, key); -}} -{inline_s}{valuetype_c} {methods["load"]}({hmap_t} *map, {keytype_c} key) {{ - const {valuetype_c}* internal_val = {hmap_t}_at(map, key); - return *internal_val; -}} -{inline_s}void {methods["store"]}({hmap_t} *map, {keytype_c} key, {valuetype_c} value) {{ - {hmap_t}_insert_or_assign(map, key, value); -}} -{inline_s}void {methods["cleanup"]}(void* ptr) {{ - {hmap_t}* hptr = ptr; - {hmap_t}_drop(hptr); - free(hptr); -}} - """ + lib_code = dedent(f""" + {inline_s}void* + {methods["init"]}() {{ + void* ptr = malloc(sizeof({hmap_t})); + memset(ptr, 0, sizeof({hmap_t})); + return ptr; + }} + + {inline_s}bool + {methods["exists"]}( + {hmap_t} *map, {keytype_c} key + ) {{ + return {hmap_t}_contains(map, key); + }} + + {inline_s}{valuetype_c} + {methods["load"]}( + {hmap_t} *map, {keytype_c} key + ) {{ + const {valuetype_c}* internal_val = {hmap_t}_at(map, key); + return *internal_val; + }} + + {inline_s}void + {methods["store"]}( + {hmap_t} *map, {keytype_c} key, {valuetype_c} value + ) {{ + {hmap_t}_insert_or_assign(map, key, value); + }} + + {inline_s}void + {methods["cleanup"]}( + void* ptr + ) {{ + {hmap_t}* hptr = ptr; + {hmap_t}_drop(hptr); + free(hptr); + }} + """) ctx.add_header(lib_code) return methods, hmap_t @@ -389,8 +410,8 @@ def serialize_to_c(self, obj: CHashTable): map = ctypes.c_void_p(obj.map) struct = CHashMapStruct(map, obj) # We NEED this for stupid ownership reasons. - obj._self_obj = ctypes.py_object(obj) # type: ignore - obj._struct = struct # type: ignore + obj._self_obj = ctypes.py_object(obj) + obj._struct = struct return ctypes.pointer(struct) def deserialize_from_c(self, obj: CHashTable, res): @@ -407,9 +428,6 @@ def construct_from_c(self, c_map): Construct a CHashTable from a C-compatible structure. c_map is a pointer to a CHashMapStruct - - I am going to refrain from doing this because lifecycle management is horrible. - Should we move? """ raise NotImplementedError @@ -420,10 +438,6 @@ class NumbaHashTable(Map): """ def __init__(self, key_len, value_len, map: "dict[tuple,tuple] | None" = None): - """ - Constructor for the Hash Table, which maps integer tuples to integer tuples. Takes three arguments: - in_len: The - """ self.key_len = key_len self.value_len = value_len @@ -433,12 +447,10 @@ def __init__(self, key_len, value_len, map: "dict[tuple,tuple] | None" = None): self._numba_key_type = numba.types.UniTuple(numba.types.int64, key_len) self._numba_value_type = numba.types.UniTuple(numba.types.int64, value_len) - if map is None: map = {} self.map = numba.typed.Dict.empty( - key_type=self._numba_key_type, - value_type=self._numba_value_type + key_type=self._numba_key_type, value_type=self._numba_value_type ) for key, value in map.items(): if not _is_integer_tuple(key, key_len): @@ -537,7 +549,9 @@ def __hash__(self): """ def numba_jitclass_type(self) -> numba.types.Type: - return numba.types.ListType(numba.types.DictType(self._numba_key_type, self._numba_value_type)) + return numba.types.ListType( + numba.types.DictType(self._numba_key_type, self._numba_value_type) + ) def numba_type(self): return list @@ -547,8 +561,7 @@ def numba_existsmap( ): assert isinstance(map.obj, NumbaMapFields) tuple_fields = ",".join( - f"{ctx(idx)}.{field}" - for field in self.key_type.struct_fieldnames + f"{ctx(idx)}.{field}" for field in self.key_type.struct_fieldnames ) return f"tuple(({tuple_fields})) in {map.obj.map}" @@ -557,8 +570,7 @@ def numba_loadmap( ): assert isinstance(map.obj, NumbaMapFields) tuple_fields = ",".join( - f"{ctx(idx)}.{field}" - for field in self.key_type.struct_fieldnames + f"{ctx(idx)}.{field}" for field in self.key_type.struct_fieldnames ) value_v = ctx.freshen("value") ctx.exec(f"{ctx.feed}{value_v} = {map.obj.map}[tuple(({tuple_fields}))]") @@ -573,14 +585,14 @@ def numba_storemap( ): assert isinstance(map.obj, NumbaMapFields) idx_fields = ",".join( - f"{ctx(idx)}.{field}" - for field in self.key_type.struct_fieldnames + f"{ctx(idx)}.{field}" for field in self.key_type.struct_fieldnames ) val_fields = ",".join( - f"{ctx(value)}.{field}" - for field in self.value_type.struct_fieldnames + f"{ctx(value)}.{field}" for field in self.value_type.struct_fieldnames + ) + ctx.exec( + f"{ctx.feed}{map.obj.map}[tuple(({idx_fields}))] = tuple(({val_fields}))" ) - ctx.exec(f"{ctx.feed}{map.obj.map}[tuple(({idx_fields}))] = tuple(({val_fields}))") def numba_unpack( self, ctx: "NumbaContext", var_n: str, val: "AssemblyExpression" diff --git a/src/finchlite/codegen/numba_backend.py b/src/finchlite/codegen/numba_backend.py index e579b83f..35cad32a 100644 --- a/src/finchlite/codegen/numba_backend.py +++ b/src/finchlite/codegen/numba_backend.py @@ -801,9 +801,7 @@ def serialize_tuple_to_numba(fmt, obj): ) # trivial ser/deser -for t in ( - int, bool, float -): +for t in (int, bool, float): register_property( t, "construct_from_numba", diff --git a/tests/test_codegen.py b/tests/test_codegen.py index 60a4931a..117be983 100644 --- a/tests/test_codegen.py +++ b/tests/test_codegen.py @@ -982,8 +982,12 @@ def test_hashtable(compiler, tabletype): ) ) compiled = compiler(module) - assert compiled.setidx(table, key_type(1, 2), val_type(2, 3, 4)) == val_type(2, 3, 4) - assert compiled.setidx(table, key_type(1, 4), val_type(3, 4, 1)) == val_type(3, 4, 1) + assert compiled.setidx(table, key_type(1, 2), val_type(2, 3, 4)) == val_type( + 2, 3, 4 + ) + assert compiled.setidx(table, key_type(1, 4), val_type(3, 4, 1)) == val_type( + 3, 4, 1 + ) assert compiled.exists(table, key_type(1, 2)) assert not compiled.exists(table, key_type(1, 3)) From f2f09acc5fbb836965f6ce16cf33b344abe9a92a Mon Sep 17 00:00:00 2001 From: Juni Kim Date: Fri, 28 Nov 2025 17:34:32 -0500 Subject: [PATCH 21/44] docs update --- src/finchlite/finch_assembly/dev_doc.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/finchlite/finch_assembly/dev_doc.md b/src/finchlite/finch_assembly/dev_doc.md index 66ee299f..8327cb9d 100644 --- a/src/finchlite/finch_assembly/dev_doc.md +++ b/src/finchlite/finch_assembly/dev_doc.md @@ -11,7 +11,7 @@ The following is a rough grammar for FinchAssembly, written in terms of the curr EXPR := LITERAL | VARIABLE | SLOT | STACK | GETATTR | CALL | LOAD | LENGTH STMT := UNPACK | REPACK | ASSIGN | SETATTR | STORE | RESIZE | FORLOOP | BUFFERLOOP | WHILELOOP | IF | IFELSE | FUNCTION | RETURN | BREAK - | BLOCK | MODULE + | BLOCK | MODULE | LOADMAP | STOREMAP | EXISTSMAP NODE := EXPR | STMT LITERAL := Literal(val=VALUE) @@ -27,6 +27,9 @@ LOAD := Load(buffer=SLOT | STACK, index=EXPR) STORE := Store(buffer=SLOT | STACK, index=EXPR, value=EXPR) RESIZE := Resize(buffer=SLOT | STACK, new_size=EXPR) LENGTH := Length(buffer=SLOT | STACK) +LOADMAP := LoadMap(map=SLOT | STACK, index=EXPR) +STOREMAP := StoreMap(map=SLOT | STACK, index=EXPR, value=EXPR) +EXISTSMAP := ExistMap(map=SLOT | STACK, index=EXPR) STACK := Stack(obj=ANY, type=TYPE) FORLOOP := ForLoop(var=VARIABLE, start=EXPR, end=EXPR, body=NODE) BUFFERLOOP := BufferLoop(buffer=EXPR, var=VARIABLE, body=NODE) From 94cd39a0aefc2e382dfda30d3ea41be85be789d2 Mon Sep 17 00:00:00 2001 From: Juni Kim Date: Fri, 28 Nov 2025 19:18:50 -0500 Subject: [PATCH 22/44] tried some type fixing --- src/finchlite/codegen/hashtable.py | 10 ++++++++-- tests/test_codegen.py | 1 + 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/finchlite/codegen/hashtable.py b/src/finchlite/codegen/hashtable.py index 6a6db045..75c2f93a 100644 --- a/src/finchlite/codegen/hashtable.py +++ b/src/finchlite/codegen/hashtable.py @@ -2,7 +2,7 @@ from dataclasses import dataclass from pathlib import Path from textwrap import dedent -from typing import NamedTuple, TypedDict +from typing import Any, NamedTuple, TypedDict import numba import numba.typed @@ -243,6 +243,10 @@ def __init__( self.key_len = key_len self.value_len = value_len + # these are blank fields we need when serializing or smth + self._struct: Any = None + self._self_obj: Any = None + if map is None: map = {} self.map = getattr(self.lib.library, self.lib.methods["init"])() @@ -399,7 +403,7 @@ def c_repack(self, ctx: "CContext", lhs: str, obj: "CMapFields"): """ Repack the map out of C context. """ - ctx.exec(f"{ctx.feed}{lhs}->map = {obj.map}") + ctx.exec(f"{ctx.feed}{lhs}->map = {obj.map};") def serialize_to_c(self, obj: CHashTable): """ @@ -506,6 +510,8 @@ def __init__(self, key_len: int, value_len: int): self.value_len = value_len self._key_type = _int_tuple_ftype(key_len) self._value_type = _int_tuple_ftype(value_len) + self._numba_key_type = numba.types.UniTuple(numba.types.int64, key_len) + self._numba_value_type = numba.types.UniTuple(numba.types.int64, value_len) def __eq__(self, other): if not isinstance(other, NumbaHashTableFType): diff --git a/tests/test_codegen.py b/tests/test_codegen.py index 117be983..81c92a79 100644 --- a/tests/test_codegen.py +++ b/tests/test_codegen.py @@ -965,6 +965,7 @@ def test_hashtable(compiler, tabletype): key_v, val_v, ), + asm.Repack(table_slt), asm.Return(asm.LoadMap(table_slt, key_v)), ) ), From 570bad1f0121b919d41564d57c4241318d5697ec Mon Sep 17 00:00:00 2001 From: Juni Kim Date: Sat, 29 Nov 2025 23:10:26 -0500 Subject: [PATCH 23/44] type checker tests too --- src/finchlite/codegen/c.py | 14 ++-- src/finchlite/codegen/hashtable.py | 5 +- src/finchlite/finch_assembly/__init__.py | 2 + src/finchlite/finch_assembly/map.py | 20 +++-- tests/test_assembly_type_checker.py | 98 ++++++++++++++++++++++++ 5 files changed, 119 insertions(+), 20 deletions(-) diff --git a/src/finchlite/codegen/c.py b/src/finchlite/codegen/c.py index 27b75f43..f2651b81 100644 --- a/src/finchlite/codegen/c.py +++ b/src/finchlite/codegen/c.py @@ -6,7 +6,7 @@ import tempfile from abc import ABC, abstractmethod from collections import namedtuple -from collections.abc import Callable +from collections.abc import Callable, Hashable from functools import lru_cache from pathlib import Path from types import NoneType @@ -14,11 +14,9 @@ import numpy as np -from finchlite.finch_assembly.map import FType, MapFType - from .. import finch_assembly as asm from ..algebra import query_property, register_property -from ..finch_assembly import AssemblyStructFType, BufferFType, TupleFType +from ..finch_assembly import AssemblyStructFType, BufferFType, MapFType, TupleFType from ..symbolic import Context, Namespace, ScopedDict, fisinstance, ftype from ..util import config from ..util.cache import file_cache @@ -612,22 +610,22 @@ def __init__( self.fptr = fptr self.types = types self.slots = slots - self.datastructures: dict[FType, Any] = {} + self.datastructures: dict[Hashable, Any] = {} def add_header(self, header): if header not in self._headerset: self.headers.append(header) self._headerset.add(header) - def add_datastructure(self, ftype: FType, handler: "Callable[[CContext], Any]"): + def add_datastructure(self, key: Hashable, handler: "Callable[[CContext], Any]"): """ Code to add a datastructure declaration. This is the minimum required to prevent redundancy. """ - if ftype in self.datastructures: + if key in self.datastructures: return # at least mark something is there. - self.datastructures[ftype] = None + self.datastructures[key] = None handler(self) def emit_global(self): diff --git a/src/finchlite/codegen/hashtable.py b/src/finchlite/codegen/hashtable.py index 75c2f93a..536bd3e8 100644 --- a/src/finchlite/codegen/hashtable.py +++ b/src/finchlite/codegen/hashtable.py @@ -390,7 +390,7 @@ def c_unpack(self, ctx: "CContext", var_n: str, val: AssemblyExpression): data = ctx.freshen(var_n, "data") # Add all the stupid header stuff from above. ctx.add_datastructure( - self, + ("CHashTableFType", self.key_len, self.value_len), lambda ctx: CHashTable.gen_code( ctx, self.key_type, self.value_type, inline=True ), @@ -445,9 +445,6 @@ def __init__(self, key_len, value_len, map: "dict[tuple,tuple] | None" = None): self.key_len = key_len self.value_len = value_len - self._key_type = _int_tuple_ftype(key_len) - self._value_type = _int_tuple_ftype(value_len) - self._numba_key_type = numba.types.UniTuple(numba.types.int64, key_len) self._numba_value_type = numba.types.UniTuple(numba.types.int64, value_len) diff --git a/src/finchlite/finch_assembly/__init__.py b/src/finchlite/finch_assembly/__init__.py index e9644bc6..4ed20944 100644 --- a/src/finchlite/finch_assembly/__init__.py +++ b/src/finchlite/finch_assembly/__init__.py @@ -6,6 +6,7 @@ ) from .dataflow import AssemblyCopyPropagation, assembly_copy_propagation from .interpreter import AssemblyInterpreter, AssemblyInterpreterKernel +from .map import MapFType from .nodes import ( AssemblyNode, Assert, @@ -69,6 +70,7 @@ "Literal", "Load", "LoadMap", + "MapFType", "Module", "NamedTupleFType", "Print", diff --git a/src/finchlite/finch_assembly/map.py b/src/finchlite/finch_assembly/map.py index 1ddcd105..fe354539 100644 --- a/src/finchlite/finch_assembly/map.py +++ b/src/finchlite/finch_assembly/map.py @@ -16,20 +16,24 @@ def __init__( ): ... @property - def element_type(self): + @abstractmethod + def ftype(self) -> "MapFType": ... + + @property + def value_type(self): """ - Return the type of elements stored in the hash table. - This is typically the same as the dtype used to create the map. + Return type of values stored in the hash table + (probably some TupleFType) """ - return self.ftype.element_type() + return self.ftype.value_type @property - def length_type(self): + def key_type(self): """ - Return the type of indices used to access elements in the hash table. - This is typically an integer type. + Return type of keys stored in the hash table + (probably some TupleFType) """ - return self.ftype.length_type() + return self.ftype.key_type @abstractmethod def load(self, idx: tuple): diff --git a/tests/test_assembly_type_checker.py b/tests/test_assembly_type_checker.py index 7ecf0105..fdce656a 100644 --- a/tests/test_assembly_type_checker.py +++ b/tests/test_assembly_type_checker.py @@ -7,7 +7,9 @@ import finchlite.finch_assembly as asm from finchlite.codegen import NumpyBuffer +from finchlite.codegen.hashtable import CHashTable, NumbaHashTable from finchlite.finch_assembly import assembly_check_types +from finchlite.finch_assembly.struct import TupleFType from finchlite.symbolic import FType, ftype @@ -684,3 +686,99 @@ def test_simple_struct(): ) assembly_check_types(mod) + + +@pytest.mark.parametrize( + ["tabletype"], + [ + (CHashTable,), + (NumbaHashTable,), + ], +) +def test_hashtable(tabletype): + table = tabletype(2, 3) + + table_v = asm.Variable("a", ftype(table)) + table_slt = asm.Slot("a_", ftype(table)) + + key_type = table.ftype.key_type + val_type = table.ftype.value_type + key_v = asm.Variable("key", key_type) + val_v = asm.Variable("val", val_type) + + mod = asm.Module( + ( + asm.Function( + asm.Variable( + "setidx", TupleFType.from_tuple(tuple(int for _ in range(3))) + ), + (table_v, key_v, val_v), + asm.Block( + ( + asm.Unpack(table_slt, table_v), + asm.StoreMap( + table_slt, + key_v, + val_v, + ), + asm.Repack(table_slt), + asm.Return(asm.LoadMap(table_slt, key_v)), + ) + ), + ), + asm.Function( + asm.Variable("exists", bool), + (table_v, key_v), + asm.Block( + ( + asm.Unpack(table_slt, table_v), + asm.Return(asm.ExistsMap(table_slt, key_v)), + ) + ), + ), + ) + ) + assembly_check_types(mod) + + +@pytest.mark.parametrize( + ["tabletype"], + [ + (CHashTable,), + (NumbaHashTable,), + ], +) +def test_hashtable_fail(tabletype): + table = tabletype(2, 3) + + table_v = asm.Variable("a", ftype(table)) + table_slt = asm.Slot("a_", ftype(table)) + + key_type = table.ftype.key_type + val_type = table.ftype.value_type + key_v = asm.Variable("key", key_type) + val_v = asm.Variable("val", val_type) + mod = asm.Module( + ( + asm.Function( + asm.Variable( + "setidx", TupleFType.from_tuple(tuple(int for _ in range(2))) + ), + (table_v, key_v, val_v), + asm.Block( + ( + asm.Unpack(table_slt, table_v), + asm.StoreMap( + table_slt, + key_v, + val_v, + ), + asm.Repack(table_slt), + asm.Return(asm.LoadMap(table_slt, key_v)), + ) + ), + ), + ) + ) + with pytest.raises(asm.AssemblyTypeError): + assembly_check_types(mod) From f22c59b02b7e790cab1c715f75df400aa38e3f36 Mon Sep 17 00:00:00 2001 From: Juni Kim Date: Sat, 29 Nov 2025 23:17:56 -0500 Subject: [PATCH 24/44] node fix --- src/finchlite/finch_assembly/nodes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/finchlite/finch_assembly/nodes.py b/src/finchlite/finch_assembly/nodes.py index 5384ff12..c76ee089 100644 --- a/src/finchlite/finch_assembly/nodes.py +++ b/src/finchlite/finch_assembly/nodes.py @@ -405,7 +405,7 @@ def children(self): return [self.map, self.index] def result_format(self): - return element_format(self.map.result_format) + return self.map.result_format.value_type @dataclass(eq=True, frozen=True) From ef84d947ef47aa162fbd733f2e2adac982d5b413 Mon Sep 17 00:00:00 2001 From: Juni Kim Date: Mon, 1 Dec 2025 15:19:45 -0500 Subject: [PATCH 25/44] turn all map -> dict --- src/finchlite/codegen/c.py | 24 ++--- src/finchlite/codegen/hashtable.py | 100 +++++++++---------- src/finchlite/codegen/numba_backend.py | 28 +++--- src/finchlite/finch_assembly/__init__.py | 17 ++-- src/finchlite/finch_assembly/interpreter.py | 18 ++-- src/finchlite/finch_assembly/map.py | 6 +- src/finchlite/finch_assembly/nodes.py | 18 ++-- src/finchlite/finch_assembly/type_checker.py | 20 ++-- tests/test_assembly_type_checker.py | 10 +- tests/test_codegen.py | 6 +- 10 files changed, 124 insertions(+), 123 deletions(-) diff --git a/src/finchlite/codegen/c.py b/src/finchlite/codegen/c.py index f2651b81..c1c43b28 100644 --- a/src/finchlite/codegen/c.py +++ b/src/finchlite/codegen/c.py @@ -16,7 +16,7 @@ from .. import finch_assembly as asm from ..algebra import query_property, register_property -from ..finch_assembly import AssemblyStructFType, BufferFType, MapFType, TupleFType +from ..finch_assembly import AssemblyStructFType, BufferFType, DictFType, TupleFType from ..symbolic import Context, Namespace, ScopedDict, fisinstance, ftype from ..util import config from ..util.cache import file_cache @@ -817,15 +817,15 @@ def __call__(self, prgm: asm.AssemblyNode): case asm.Length(buf): buf = self.resolve(buf) return buf.result_format.c_length(self, buf) - case asm.LoadMap(map, idx): + case asm.LoadDict(map, idx): map = self.resolve(map) - return map.result_format.c_loadmap(self, map, idx) - case asm.ExistsMap(map, idx): + return map.result_format.c_loaddict(self, map, idx) + case asm.ExistsDict(map, idx): map = self.resolve(map) - return map.result_format.c_existsmap(self, map, idx) - case asm.StoreMap(map, idx, val): + return map.result_format.c_existsdict(self, map, idx) + case asm.StoreDict(map, idx, val): map = self.resolve(map) - return map.result_format.c_storemap(self, map, idx, val) + return map.result_format.c_storedict(self, map, idx, val) case asm.Block(bodies): ctx_2 = self.block() for body in bodies: @@ -970,28 +970,28 @@ def construct_from_c(self, res): """ -class CMapFType(MapFType, CArgumentFType, ABC): +class CDictFType(DictFType, CArgumentFType, ABC): """ - Abstract base class for the ftype of datastructures. The ftype defines how + Abstract base class for the ftype of dictionaries. The ftype defines how the data in a Map is organized and accessed. """ @abstractmethod - def c_existsmap(self, ctx, map, idx): + def c_existsdict(self, ctx, map, idx): """ Return C code which checks whether a given key exists in a map. """ ... @abstractmethod - def c_loadmap(self, ctx, map, idx): + def c_loaddict(self, ctx, map, idx): """ Return C code which gets a value corresponding to a certain key. """ ... @abstractmethod - def c_storemap(self, ctx, buffer, idx, value): + def c_storedict(self, ctx, buffer, idx, value): """ Return C code which stores a certain value given a certain integer tuple key. """ diff --git a/src/finchlite/codegen/hashtable.py b/src/finchlite/codegen/hashtable.py index 536bd3e8..1a7de691 100644 --- a/src/finchlite/codegen/hashtable.py +++ b/src/finchlite/codegen/hashtable.py @@ -9,7 +9,7 @@ from finchlite.codegen.c import ( CContext, - CMapFType, + CDictFType, CStackFType, c_type, construct_from_c, @@ -17,11 +17,11 @@ ) from finchlite.codegen.numba_backend import ( NumbaContext, - NumbaMapFType, + NumbaDictFType, NumbaStackFType, numba_type, ) -from finchlite.finch_assembly.map import Map +from finchlite.finch_assembly.map import Dict from finchlite.finch_assembly.nodes import AssemblyExpression, Stack from finchlite.finch_assembly.struct import AssemblyStructFType, TupleFType @@ -29,7 +29,7 @@ hashmap_h = stcpath / "stc" / "hashmap.h" -class NumbaMapFields(NamedTuple): +class NumbaDictFields(NamedTuple): """ This is a field that extracts out the map from the obj variable. Its purpose is so that we can extract out map from obj in unpack, do @@ -40,7 +40,7 @@ class NumbaMapFields(NamedTuple): obj: str -class CMapFields(NamedTuple): +class CDictFields(NamedTuple): """ TODO: for the C backend, we will pulling in a completely different library to do the actual hash function implementation. Should we even try to @@ -66,7 +66,7 @@ def _tuplify(ftype: AssemblyStructFType, obj): return tuple([ftype.struct_getattr(obj, attr) for attr in ftype.struct_fieldnames]) -class CHashMapStruct(ctypes.Structure): +class CHashTableStruct(ctypes.Structure): _fields_ = [ ("map", ctypes.c_void_p), ("obj", ctypes.py_object), @@ -89,7 +89,7 @@ class CHashTableLibrary: # implement the hash table datastructures -class CHashTable(Map): +class CHashTable(Dict): """ CHashTable class that basically connects up to an STC library. """ @@ -249,7 +249,7 @@ def __init__( if map is None: map = {} - self.map = getattr(self.lib.library, self.lib.methods["init"])() + self.dct = getattr(self.lib.library, self.lib.methods["init"])() for key, value in map.items(): if not _is_integer_tuple(key, key_len): raise TypeError( @@ -262,14 +262,14 @@ def __init__( self.store(key, value) def __del__(self): - getattr(self.lib.library, self.lib.methods["cleanup"])(self.map) + getattr(self.lib.library, self.lib.methods["cleanup"])(self.dct) def exists(self, idx) -> bool: idx = _tuplify(self.ftype.key_type, idx) assert _is_integer_tuple(idx, self.key_len) KeyStruct = c_type(self.ftype.key_type) c_key = KeyStruct(*idx) - c_value = getattr(self.lib.library, self.lib.methods["exists"])(self.map, c_key) + c_value = getattr(self.lib.library, self.lib.methods["exists"])(self.dct, c_key) return bool(c_value) def load(self, idx): @@ -277,7 +277,7 @@ def load(self, idx): assert _is_integer_tuple(idx, self.key_len) KeyStruct = c_type(self.ftype.key_type) c_key = KeyStruct(*idx) - c_value = getattr(self.lib.library, self.lib.methods["load"])(self.map, c_key) + c_value = getattr(self.lib.library, self.lib.methods["load"])(self.dct, c_key) return construct_from_c(self.ftype.value_type, c_value) def store(self, idx, val): @@ -289,17 +289,17 @@ def store(self, idx, val): ValueStruct = c_type(self.ftype.value_type) c_key = KeyStruct(*idx) c_value = ValueStruct(*val) - getattr(self.lib.library, self.lib.methods["store"])(self.map, c_key, c_value) + getattr(self.lib.library, self.lib.methods["store"])(self.dct, c_key, c_value) def __str__(self): - return f"c_hashtable({self.map})" + return f"c_hashtable({self.dct})" @property def ftype(self): return CHashTableFType(self.key_len, self.value_len) -class CHashTableFType(CMapFType, CStackFType): +class CHashTableFType(CDictFType, CStackFType): """ An implementation of Hash Tables using the stc library. """ @@ -353,31 +353,31 @@ def __hash__(self): """ def c_type(self): - return ctypes.POINTER(CHashMapStruct) + return ctypes.POINTER(CHashTableStruct) - def c_existsmap(self, ctx: "CContext", map: "Stack", idx: "AssemblyExpression"): - assert isinstance(map.obj, CMapFields) + def c_existsdict(self, ctx: "CContext", map: "Stack", idx: "AssemblyExpression"): + assert isinstance(map.obj, CDictFields) methods: CHashMethods = ctx.datastructures[self] return f"{ctx.feed}{methods['exists']}({map.obj.map}, {ctx(idx)})" - def c_storemap( + def c_storedict( self, ctx: "CContext", map: "Stack", idx: "AssemblyExpression", value: "AssemblyExpression", ): - assert isinstance(map.obj, CMapFields) + assert isinstance(map.obj, CDictFields) methods: CHashMethods = ctx.datastructures[self] ctx.exec( f"{ctx.feed}{methods['store']}({map.obj.map}, {ctx(idx)}, {ctx(value)});" ) - def c_loadmap(self, ctx: "CContext", map: "Stack", idx: "AssemblyExpression"): + def c_loaddict(self, ctx: "CContext", map: "Stack", idx: "AssemblyExpression"): """ Get an expression where we can get the value corresponding to a key. """ - assert isinstance(map.obj, CMapFields) + assert isinstance(map.obj, CDictFields) methods: CHashMethods = ctx.datastructures[self] return f"{methods['load']}({map.obj.map}, {ctx(idx)})" @@ -397,9 +397,9 @@ def c_unpack(self, ctx: "CContext", var_n: str, val: AssemblyExpression): ) ctx.exec(f"{ctx.feed}void* {data} = {ctx(val)}->map;") - return CMapFields(data, var_n) + return CDictFields(data, var_n) - def c_repack(self, ctx: "CContext", lhs: str, obj: "CMapFields"): + def c_repack(self, ctx: "CContext", lhs: str, obj: "CDictFields"): """ Repack the map out of C context. """ @@ -411,8 +411,8 @@ def serialize_to_c(self, obj: CHashTable): This datatype will then immediately get turned into a struct. """ assert isinstance(obj, CHashTable) - map = ctypes.c_void_p(obj.map) - struct = CHashMapStruct(map, obj) + map = ctypes.c_void_p(obj.dct) + struct = CHashTableStruct(map, obj) # We NEED this for stupid ownership reasons. obj._self_obj = ctypes.py_object(obj) obj._struct = struct @@ -422,10 +422,10 @@ def deserialize_from_c(self, obj: CHashTable, res): """ Update our hash table based on how the C call modified the CHashMapStruct. """ - assert isinstance(res, ctypes.POINTER(CHashMapStruct)) + assert isinstance(res, ctypes.POINTER(CHashTableStruct)) assert isinstance(res.contents.obj, CHashTable) - obj.map = res.contents.map + obj.dct = res.contents.map def construct_from_c(self, c_map): """ @@ -436,24 +436,24 @@ def construct_from_c(self, c_map): raise NotImplementedError -class NumbaHashTable(Map): +class NumbaHashTable(Dict): """ A Hash Table that maps Z^{in_len} to Z^{out_len} """ - def __init__(self, key_len, value_len, map: "dict[tuple,tuple] | None" = None): + def __init__(self, key_len, value_len, dct: "dict[tuple,tuple] | None" = None): self.key_len = key_len self.value_len = value_len self._numba_key_type = numba.types.UniTuple(numba.types.int64, key_len) self._numba_value_type = numba.types.UniTuple(numba.types.int64, value_len) - if map is None: - map = {} - self.map = numba.typed.Dict.empty( + if dct is None: + dct = {} + self.dct = numba.typed.Dict.empty( key_type=self._numba_key_type, value_type=self._numba_value_type ) - for key, value in map.items(): + for key, value in dct.items(): if not _is_integer_tuple(key, key_len): raise TypeError( f"Supplied key {key} is not a tuple of {key_len} integers" @@ -462,7 +462,7 @@ def __init__(self, key_len, value_len, map: "dict[tuple,tuple] | None" = None): raise TypeError( f"Supplied value {key} is not a tuple of {value_len} integers" ) - self.map[key] = value + self.dct[key] = value @property def ftype(self): @@ -478,12 +478,12 @@ def exists(self, idx) -> bool: """ idx = _tuplify(self.ftype.key_type, idx) assert _is_integer_tuple(idx, self.key_len) - return idx in self.map + return idx in self.dct def load(self, idx): idx = _tuplify(self.ftype.key_type, idx) assert _is_integer_tuple(idx, self.key_len) - result = self.map[idx] + result = self.dct[idx] return self.ftype.value_type(*result) def store(self, idx, val): @@ -491,13 +491,13 @@ def store(self, idx, val): val = _tuplify(self.ftype.value_type, val) assert _is_integer_tuple(idx, self.key_len) assert _is_integer_tuple(val, self.value_len) - self.map[idx] = val + self.dct[idx] = val def __str__(self): - return f"numba_hashtable({self.map})" + return f"numba_hashtable({self.dct})" -class NumbaHashTableFType(NumbaMapFType, NumbaStackFType): +class NumbaHashTableFType(NumbaDictFType, NumbaStackFType): """ An implementation of Hash Tables using the stc library. """ @@ -559,19 +559,19 @@ def numba_jitclass_type(self) -> numba.types.Type: def numba_type(self): return list - def numba_existsmap( + def numba_existsdict( self, ctx: "NumbaContext", map: "Stack", idx: "AssemblyExpression" ): - assert isinstance(map.obj, NumbaMapFields) + assert isinstance(map.obj, NumbaDictFields) tuple_fields = ",".join( f"{ctx(idx)}.{field}" for field in self.key_type.struct_fieldnames ) return f"tuple(({tuple_fields})) in {map.obj.map}" - def numba_loadmap( + def numba_loaddict( self, ctx: "NumbaContext", map: "Stack", idx: "AssemblyExpression" ): - assert isinstance(map.obj, NumbaMapFields) + assert isinstance(map.obj, NumbaDictFields) tuple_fields = ",".join( f"{ctx(idx)}.{field}" for field in self.key_type.struct_fieldnames ) @@ -579,14 +579,14 @@ def numba_loadmap( ctx.exec(f"{ctx.feed}{value_v} = {map.obj.map}[tuple(({tuple_fields}))]") return f"{ctx.full_name(numba_type(self.value_type))}(*{value_v})" - def numba_storemap( + def numba_storedict( self, ctx: "NumbaContext", map: "Stack", idx: "AssemblyExpression", value: "AssemblyExpression", ): - assert isinstance(map.obj, NumbaMapFields) + assert isinstance(map.obj, NumbaDictFields) idx_fields = ",".join( f"{ctx(idx)}.{field}" for field in self.key_type.struct_fieldnames ) @@ -599,7 +599,7 @@ def numba_storemap( def numba_unpack( self, ctx: "NumbaContext", var_n: str, val: "AssemblyExpression" - ) -> NumbaMapFields: + ) -> NumbaDictFields: """ Unpack the map into numba context. """ @@ -607,9 +607,9 @@ def numba_unpack( map = ctx.freshen(var_n, "map") ctx.exec(f"{ctx.feed}{map} = {ctx(val)}[0]") - return NumbaMapFields(map, var_n) + return NumbaDictFields(map, var_n) - def numba_repack(self, ctx: "NumbaContext", lhs: str, obj: "NumbaMapFields"): + def numba_repack(self, ctx: "NumbaContext", lhs: str, obj: "NumbaDictFields"): """ Repack the map from Numba context. """ @@ -622,10 +622,10 @@ def serialize_to_numba(self, obj: "NumbaHashTable"): We will supply the input and output length """ - return numba.typed.List([obj.map]) + return numba.typed.List([obj.dct]) def deserialize_from_numba(self, obj: "NumbaHashTable", numba_map: "list[dict]"): - obj.map = numba_map[0] + obj.dct = numba_map[0] def construct_from_numba(self, numba_map): """ diff --git a/src/finchlite/codegen/numba_backend.py b/src/finchlite/codegen/numba_backend.py index 35cad32a..3da2c6e5 100644 --- a/src/finchlite/codegen/numba_backend.py +++ b/src/finchlite/codegen/numba_backend.py @@ -8,7 +8,7 @@ import numba -from finchlite.finch_assembly.map import MapFType # type: ignore[import-untyped] +from finchlite.finch_assembly.map import DictFType # type: ignore[import-untyped] from .. import finch_assembly as asm from ..algebra import query_property, register_property @@ -265,28 +265,28 @@ def construct_from_numba(fmt, numba_obj): ) -class NumbaMapFType(MapFType, NumbaArgumentFType, ABC): +class NumbaDictFType(DictFType, NumbaArgumentFType, ABC): """ Abstract base class for the ftype of datastructures. The ftype defines how the data in a Map is organized and accessed. """ @abstractmethod - def numba_existsmap(self, ctx: "NumbaContext", map, idx): + def numba_existsdict(self, ctx: "NumbaContext", map, idx): """ Return numba code which checks whether a given key exists in a map. """ ... @abstractmethod - def numba_loadmap(self, ctx, buffer, idx): + def numba_loaddict(self, ctx, buffer, idx): """ Return numba code which gets a value corresponding to a certain key. """ ... @abstractmethod - def numba_storemap(self, ctx, buffer, idx, value): + def numba_storedict(self, ctx, buffer, idx, value): """ Return C code which stores a certain value given a certain integer tuple key. """ @@ -588,15 +588,15 @@ def __call__(self, prgm: asm.AssemblyNode): case asm.Length(buf): buf = self.resolve(buf) return buf.result_format.numba_length(self, buf) - case asm.LoadMap(map, idx): - map = self.resolve(map) - return map.result_format.numba_loadmap(self, map, idx) - case asm.ExistsMap(map, idx): - map = self.resolve(map) - return map.result_format.numba_existsmap(self, map, idx) - case asm.StoreMap(map, idx, val): - map = self.resolve(map) - return map.result_format.numba_storemap(self, map, idx, val) + case asm.LoadDict(dct, idx): + dct = self.resolve(dct) + return dct.result_format.numba_loaddict(self, dct, idx) + case asm.ExistsDict(dct, idx): + dct = self.resolve(dct) + return dct.result_format.numba_existsdict(self, dct, idx) + case asm.StoreDict(dct, idx, val): + dct = self.resolve(dct) + return dct.result_format.numba_storedict(self, dct, idx, val) case asm.Block(bodies): ctx_2 = self.block() for body in bodies: diff --git a/src/finchlite/finch_assembly/__init__.py b/src/finchlite/finch_assembly/__init__.py index 4ed20944..d7a6a8aa 100644 --- a/src/finchlite/finch_assembly/__init__.py +++ b/src/finchlite/finch_assembly/__init__.py @@ -6,7 +6,7 @@ ) from .dataflow import AssemblyCopyPropagation, assembly_copy_propagation from .interpreter import AssemblyInterpreter, AssemblyInterpreterKernel -from .map import MapFType +from .map import Dict, DictFType from .nodes import ( AssemblyNode, Assert, @@ -15,7 +15,7 @@ Break, BufferLoop, Call, - ExistsMap, + ExistsDict, ForLoop, Function, GetAttr, @@ -24,7 +24,7 @@ Length, Literal, Load, - LoadMap, + LoadDict, Module, Print, Repack, @@ -34,7 +34,7 @@ Slot, Stack, Store, - StoreMap, + StoreDict, TaggedVariable, Unpack, Variable, @@ -60,7 +60,7 @@ "BufferFType", "BufferLoop", "Call", - "ExistsMap", + "ExistsDict", "ForLoop", "Function", "GetAttr", @@ -69,8 +69,9 @@ "Length", "Literal", "Load", - "LoadMap", - "MapFType", + "LoadDict", + "Dict", + "DictFType", "Module", "NamedTupleFType", "Print", @@ -81,7 +82,7 @@ "Slot", "Stack", "Store", - "StoreMap", + "StoreDict", "TaggedVariable", "TupleFType", "Unpack", diff --git a/src/finchlite/finch_assembly/interpreter.py b/src/finchlite/finch_assembly/interpreter.py index 0d6c7996..8dd08104 100644 --- a/src/finchlite/finch_assembly/interpreter.py +++ b/src/finchlite/finch_assembly/interpreter.py @@ -212,14 +212,14 @@ def __call__(self, prgm: asm.AssemblyNode): buf_e = self(buf) idx_e = self(idx) return buf_e.load(idx_e) - case asm.LoadMap(map, idx): - assert isinstance(map, asm.Slot) - map_e = self(map) + case asm.LoadDict(dct, idx): + assert isinstance(dct, asm.Slot) + map_e = self(dct) idx_e = self(idx) return map_e.load(idx_e) - case asm.ExistsMap(map, idx): - assert isinstance(map, asm.Slot) - map_e = self(map) + case asm.ExistsDict(dct, idx): + assert isinstance(dct, asm.Slot) + map_e = self(dct) idx_e = self(idx) return map_e.exists(idx_e) case asm.Store(buf, idx, val): @@ -229,9 +229,9 @@ def __call__(self, prgm: asm.AssemblyNode): val_e = self(val) buf_e.store(idx_e, val_e) return None - case asm.StoreMap(map, idx, val): - assert isinstance(map, asm.Slot) - map_e = self(map) + case asm.StoreDict(dct, idx, val): + assert isinstance(dct, asm.Slot) + map_e = self(dct) idx_e = self(idx) val_e = self(val) return map_e.store(idx_e, val_e) diff --git a/src/finchlite/finch_assembly/map.py b/src/finchlite/finch_assembly/map.py index fe354539..be233a0e 100644 --- a/src/finchlite/finch_assembly/map.py +++ b/src/finchlite/finch_assembly/map.py @@ -3,7 +3,7 @@ from ..symbolic import FType, FTyped -class Map(FTyped, ABC): +class Dict(FTyped, ABC): """ Abstract base class for a map data structure. Hash tables should be such that their bucket size can be resized, with Tree @@ -17,7 +17,7 @@ def __init__( @property @abstractmethod - def ftype(self) -> "MapFType": ... + def ftype(self) -> "DictFType": ... @property def value_type(self): @@ -58,7 +58,7 @@ def store(self, idx: tuple, val): ... -class MapFType(FType): +class DictFType(FType): """ Abstract base class for an ftype corresponding to a map. """ diff --git a/src/finchlite/finch_assembly/nodes.py b/src/finchlite/finch_assembly/nodes.py index c76ee089..b380c228 100644 --- a/src/finchlite/finch_assembly/nodes.py +++ b/src/finchlite/finch_assembly/nodes.py @@ -367,7 +367,7 @@ def children(self): @dataclass(eq=True, frozen=True) -class ExistsMap(AssemblyExpression, AssemblyTree): +class ExistsDict(AssemblyExpression, AssemblyTree): """ Represents checking whether an integer tuple key is in a map. @@ -388,7 +388,7 @@ def result_format(self): @dataclass(eq=True, frozen=True) -class LoadMap(AssemblyExpression, AssemblyTree): +class LoadDict(AssemblyExpression, AssemblyTree): """ Represents loading a value from a map given an integer tuple key. @@ -397,19 +397,19 @@ class LoadMap(AssemblyExpression, AssemblyTree): index: The key value """ - map: Slot | Stack + dct: Slot | Stack index: AssemblyExpression @property def children(self): - return [self.map, self.index] + return [self.dct, self.index] def result_format(self): - return self.map.result_format.value_type + return self.dct.result_format.value_type @dataclass(eq=True, frozen=True) -class StoreMap(AssemblyTree, AssemblyStatement): +class StoreDict(AssemblyTree, AssemblyStatement): """ Represents storing a value into a buffer given an integer tuple key. @@ -769,16 +769,16 @@ def __call__(self, prgm: AssemblyNode): return None case Load(buf, idx): return f"load({self(buf)}, {self(idx)})" - case LoadMap(map, idx): + case LoadDict(map, idx): return f"loadmap({self(map)}, {self(idx)})" - case ExistsMap(map, idx): + case ExistsDict(map, idx): return f"existsmap({self(map)}, {self(idx)})" case Slot(name, type_): return f"slot({name}, {qual_str(type_)})" case Store(buf, idx, val): self.exec(f"{feed}store({self(buf)}, {self(idx)}, {self(val)})") return None - case StoreMap(map, idx, val): + case StoreDict(map, idx, val): self.exec(f"{feed}storemap({self(map)}, {self(idx)}, {self(val)})") return None case Resize(buf, size): diff --git a/src/finchlite/finch_assembly/type_checker.py b/src/finchlite/finch_assembly/type_checker.py index adaca765..aab93982 100644 --- a/src/finchlite/finch_assembly/type_checker.py +++ b/src/finchlite/finch_assembly/type_checker.py @@ -6,7 +6,7 @@ from ..symbolic import FType, ScopedDict, ftype from . import nodes as asm from .buffer import BufferFType -from .map import MapFType +from .map import DictFType from .struct import AssemblyStructFType @@ -82,9 +82,9 @@ def check_in_ctxt(self, var_n, var_t): f"The variable '{var_n}' is not defined in the current context." ) from KeyError - def check_map(self, map): - map_type = self.check_expr(map) - if isinstance(map_type, MapFType): + def check_dict(self, dct): + map_type = self.check_expr(dct) + if isinstance(map_type, DictFType): return map_type raise AssemblyTypeError(f"Expected map, got {map_type}.") @@ -141,13 +141,13 @@ def check_expr(self, expr: asm.AssemblyExpression): case asm.Length(buffer): buffer_type = self.check_buffer(buffer) return buffer_type.length_type - case asm.ExistsMap(map, index): - map_type = self.check_map(map) + case asm.ExistsDict(dct, index): + map_type = self.check_dict(dct) index_type = self.check_expr(index) check_type_match(map_type.key_type, index_type) return bool - case asm.LoadMap(map, index): - map_type = self.check_map(map) + case asm.LoadDict(dct, index): + map_type = self.check_dict(dct) index_type = self.check_expr(index) check_type_match(map_type.key_type, index_type) return map_type.value_type @@ -198,8 +198,8 @@ def check_stmt(self, stmt: asm.AssemblyStatement): value_type = self.check_expr(value) check_type_match(buffer_type.element_type, value_type) return None - case asm.StoreMap(map, index, value): - map_type = self.check_map(map) + case asm.StoreDict(map, index, value): + map_type = self.check_dict(map) index_type = self.check_expr(index) value_type = self.check_expr(value) check_type_match(map_type.key_type, index_type) diff --git a/tests/test_assembly_type_checker.py b/tests/test_assembly_type_checker.py index fdce656a..6ce50a8f 100644 --- a/tests/test_assembly_type_checker.py +++ b/tests/test_assembly_type_checker.py @@ -716,13 +716,13 @@ def test_hashtable(tabletype): asm.Block( ( asm.Unpack(table_slt, table_v), - asm.StoreMap( + asm.StoreDict( table_slt, key_v, val_v, ), asm.Repack(table_slt), - asm.Return(asm.LoadMap(table_slt, key_v)), + asm.Return(asm.LoadDict(table_slt, key_v)), ) ), ), @@ -732,7 +732,7 @@ def test_hashtable(tabletype): asm.Block( ( asm.Unpack(table_slt, table_v), - asm.Return(asm.ExistsMap(table_slt, key_v)), + asm.Return(asm.ExistsDict(table_slt, key_v)), ) ), ), @@ -768,13 +768,13 @@ def test_hashtable_fail(tabletype): asm.Block( ( asm.Unpack(table_slt, table_v), - asm.StoreMap( + asm.StoreDict( table_slt, key_v, val_v, ), asm.Repack(table_slt), - asm.Return(asm.LoadMap(table_slt, key_v)), + asm.Return(asm.LoadDict(table_slt, key_v)), ) ), ), diff --git a/tests/test_codegen.py b/tests/test_codegen.py index 81c92a79..7ca75fa8 100644 --- a/tests/test_codegen.py +++ b/tests/test_codegen.py @@ -960,13 +960,13 @@ def test_hashtable(compiler, tabletype): asm.Block( ( asm.Unpack(table_slt, table_v), - asm.StoreMap( + asm.StoreDict( table_slt, key_v, val_v, ), asm.Repack(table_slt), - asm.Return(asm.LoadMap(table_slt, key_v)), + asm.Return(asm.LoadDict(table_slt, key_v)), ) ), ), @@ -976,7 +976,7 @@ def test_hashtable(compiler, tabletype): asm.Block( ( asm.Unpack(table_slt, table_v), - asm.Return(asm.ExistsMap(table_slt, key_v)), + asm.Return(asm.ExistsDict(table_slt, key_v)), ) ), ), From 8cd031ec1d2a8be29a88418073815dbae100a7dd Mon Sep 17 00:00:00 2001 From: Juni Kim Date: Mon, 1 Dec 2025 20:52:50 -0500 Subject: [PATCH 26/44] introduce immutable and mutable structs Even though literally everything should be immutable right now --- src/finchlite/codegen/c.py | 69 +++++++++++++----------- src/finchlite/finch_assembly/__init__.py | 16 +++--- src/finchlite/finch_assembly/struct.py | 21 +++++++- 3 files changed, 66 insertions(+), 40 deletions(-) diff --git a/src/finchlite/codegen/c.py b/src/finchlite/codegen/c.py index f79f6a05..1600060c 100644 --- a/src/finchlite/codegen/c.py +++ b/src/finchlite/codegen/c.py @@ -13,9 +13,16 @@ import numpy as np +from finchlite.finch_assembly.struct import MutableStructFType + from .. import finch_assembly as asm from ..algebra import query_property, register_property -from ..finch_assembly import AssemblyStructFType, BufferFType, TupleFType +from ..finch_assembly import ( + AssemblyStructFType, + BufferFType, + ImmutableStructFType, + TupleFType, +) from ..symbolic import Context, Namespace, ScopedDict, fisinstance, ftype from ..util import config from ..util.cache import file_cache @@ -1045,57 +1052,55 @@ def struct_c_type(fmt: AssemblyStructFType): return new_struct -def struct_c_type_wrapper(fmt: AssemblyStructFType): - """ - C type decider for struct types. Serialization actually ensures that before - crossing the FFI boundary, all serialized structs are structs, not - pointers. - - The reason why we have this method is that ctypes can intelligently infer - whether we are working with a pointer arg type (pass by reference) or a - non-pointer type (pass by value) - """ - t = struct_c_type(fmt) - if fmt.is_mutable: - return ctypes.POINTER(t) - return t +""" +Note: When serializing any struct to C, it will get serialized to a struct with +no indirection. +When you pass a struct into a kernel that expects a struct pointer, ctypes can +intelligently infer whether we are working with a pointer arg type (pass by +reference) or a non-pointer type (in which case it will immediately apply +indirection) +""" register_property( - AssemblyStructFType, + MutableStructFType, "c_type", "__attr__", - struct_c_type_wrapper, + lambda fmt: ctypes.POINTER(struct_c_type(fmt)), ) +register_property( + ImmutableStructFType, "c_type", "__attr__", lambda fmt: struct_c_type(fmt) +) -def struct_c_getattr(fmt: AssemblyStructFType, ctx, obj, attr): - if fmt.is_mutable: - # we are passing things in as a pointer (reference c_type_wrapper) - return f"{obj}->{attr}" - return f"{obj}.{attr}" +register_property( + MutableStructFType, + "c_getattr", + "__attr__", + lambda fmt, ctx, obj, attr: f"{obj}->{attr}", +) register_property( - AssemblyStructFType, + ImmutableStructFType, "c_getattr", "__attr__", - struct_c_getattr, + lambda fmt, ctx, obj, attr: f"{obj}.{attr}", ) -def struct_c_setattr(fmt: AssemblyStructFType, ctx, obj, attr, val): - if fmt.is_mutable: - ctx.emit(f"{ctx.feed}{obj}->{attr} = {val};") - else: - ctx.emit(f"{ctx.feed}{obj}.{attr} = {val};") +def struct_mutable_setattr(fmt: AssemblyStructFType, ctx, obj, attr, val): + ctx.emit(f"{ctx.feed}{obj}->{attr} = {val};") +# the equivalent for immutable is f"{ctx.feed}{obj}.{attr} = {val};" +# but we will not include that because it's bad. + register_property( - AssemblyStructFType, + MutableStructFType, "c_setattr", "__attr__", - struct_c_setattr, + struct_mutable_setattr, ) @@ -1141,5 +1146,5 @@ def tuple_construct_from_c(fmt: TupleFType, c_struct): TupleFType, "c_type", "__attr__", - lambda fmt: struct_c_type_wrapper(asm.NamedTupleFType("CTuple", fmt.struct_fields)), + lambda fmt: struct_c_type(asm.NamedTupleFType("CTuple", fmt.struct_fields)), ) diff --git a/src/finchlite/finch_assembly/__init__.py b/src/finchlite/finch_assembly/__init__.py index a2417d22..293510f6 100644 --- a/src/finchlite/finch_assembly/__init__.py +++ b/src/finchlite/finch_assembly/__init__.py @@ -1,9 +1,5 @@ from .buffer import Buffer, BufferFType, element_type, length_type -from .cfg_builder import ( - AssemblyCFGBuilder, - assembly_build_cfg, - assembly_number_uses, -) +from .cfg_builder import AssemblyCFGBuilder, assembly_build_cfg, assembly_number_uses from .dataflow import AssemblyCopyPropagation, assembly_copy_propagation from .interpreter import AssemblyInterpreter, AssemblyInterpreterKernel from .nodes import ( @@ -36,7 +32,13 @@ Variable, WhileLoop, ) -from .struct import AssemblyStructFType, NamedTupleFType, TupleFType +from .struct import ( + AssemblyStructFType, + ImmutableStructFType, + MutableStructFType, + NamedTupleFType, + TupleFType, +) from .type_checker import AssemblyTypeChecker, AssemblyTypeError, assembly_check_types __all__ = [ @@ -61,10 +63,12 @@ "GetAttr", "If", "IfElse", + "ImmutableStructFType", "Length", "Literal", "Load", "Module", + "MutableStructFType", "NamedTupleFType", "Print", "Repack", diff --git a/src/finchlite/finch_assembly/struct.py b/src/finchlite/finch_assembly/struct.py index b855f87e..f2bb138e 100644 --- a/src/finchlite/finch_assembly/struct.py +++ b/src/finchlite/finch_assembly/struct.py @@ -45,7 +45,24 @@ def struct_attrtype(self, attr: str) -> Any: return dict(self.struct_fields)[attr] -class NamedTupleFType(AssemblyStructFType): +class ImmutableStructFType(AssemblyStructFType): + @property + def is_mutable(self) -> bool: + return False + + +class MutableStructFType(AssemblyStructFType): + """ + Class for a mutable assembly struct type. + It is currently not used anywhere, but maybe it will be useful in the future? + """ + + @property + def is_mutable(self) -> bool: + return True + + +class NamedTupleFType(ImmutableStructFType): def __init__(self, struct_name, struct_fields): self._struct_name = struct_name self._struct_fields = struct_fields @@ -79,7 +96,7 @@ def __call__(self, *args): return namedtuple(self.struct_name, self.struct_fieldnames)(args) -class TupleFType(AssemblyStructFType): +class TupleFType(ImmutableStructFType): def __init__(self, struct_name, struct_formats): self._struct_name = struct_name self._struct_formats = struct_formats From 1abae190d3b6e9c17ce9289e866b87de30a37b42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Sok=C3=B3=C5=82?= Date: Tue, 2 Dec 2025 13:21:23 +0100 Subject: [PATCH 27/44] Remove `numba.typed` import --- src/finchlite/codegen/hashtable.py | 1 - src/finchlite/finch_assembly/__init__.py | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/finchlite/codegen/hashtable.py b/src/finchlite/codegen/hashtable.py index 1a7de691..c523339a 100644 --- a/src/finchlite/codegen/hashtable.py +++ b/src/finchlite/codegen/hashtable.py @@ -5,7 +5,6 @@ from typing import Any, NamedTuple, TypedDict import numba -import numba.typed from finchlite.codegen.c import ( CContext, diff --git a/src/finchlite/finch_assembly/__init__.py b/src/finchlite/finch_assembly/__init__.py index d7a6a8aa..e7dc681a 100644 --- a/src/finchlite/finch_assembly/__init__.py +++ b/src/finchlite/finch_assembly/__init__.py @@ -60,6 +60,8 @@ "BufferFType", "BufferLoop", "Call", + "Dict", + "DictFType", "ExistsDict", "ForLoop", "Function", @@ -70,8 +72,6 @@ "Literal", "Load", "LoadDict", - "Dict", - "DictFType", "Module", "NamedTupleFType", "Print", From 3065ed03982ac8eaf086a34d74d18d45d896e72b Mon Sep 17 00:00:00 2001 From: Juni Kim Date: Tue, 2 Dec 2025 09:24:59 -0500 Subject: [PATCH 28/44] remove STC --- src/finchlite/codegen/stc/c11/fmt.h | 301 --------- src/finchlite/codegen/stc/stc/algorithm.h | 11 - src/finchlite/codegen/stc/stc/arc.h | 254 ------- src/finchlite/codegen/stc/stc/box.h | 168 ----- src/finchlite/codegen/stc/stc/cbits.h | 336 --------- src/finchlite/codegen/stc/stc/common.h | 359 ---------- src/finchlite/codegen/stc/stc/coption.h | 180 ----- src/finchlite/codegen/stc/stc/coroutine.h | 584 ---------------- src/finchlite/codegen/stc/stc/cregex.h | 168 ----- src/finchlite/codegen/stc/stc/cspan.h | 480 ------------- src/finchlite/codegen/stc/stc/cstr.h | 51 -- src/finchlite/codegen/stc/stc/csview.h | 243 ------- src/finchlite/codegen/stc/stc/ctest.h | 639 ------------------ src/finchlite/codegen/stc/stc/deque.h | 205 ------ src/finchlite/codegen/stc/stc/hashmap.h | 43 -- src/finchlite/codegen/stc/stc/hashset.h | 44 -- src/finchlite/codegen/stc/stc/hmap.h | 517 -------------- src/finchlite/codegen/stc/stc/hset.h | 43 -- src/finchlite/codegen/stc/stc/list.h | 431 ------------ src/finchlite/codegen/stc/stc/pqueue.h | 185 ----- src/finchlite/codegen/stc/stc/priv/cstr_prv.h | 420 ------------ src/finchlite/codegen/stc/stc/priv/linkage.h | 77 --- src/finchlite/codegen/stc/stc/priv/linkage2.h | 42 -- .../codegen/stc/stc/priv/queue_prv.h | 291 -------- src/finchlite/codegen/stc/stc/priv/sort_prv.h | 136 ---- src/finchlite/codegen/stc/stc/priv/template.h | 301 --------- .../codegen/stc/stc/priv/template2.h | 72 -- src/finchlite/codegen/stc/stc/priv/utf8_prv.h | 192 ------ src/finchlite/codegen/stc/stc/queue.h | 39 -- src/finchlite/codegen/stc/stc/random.h | 248 ------- src/finchlite/codegen/stc/stc/rc.h | 38 -- src/finchlite/codegen/stc/stc/smap.h | 612 ----------------- src/finchlite/codegen/stc/stc/sort.h | 109 --- src/finchlite/codegen/stc/stc/sortedmap.h | 46 -- src/finchlite/codegen/stc/stc/sortedset.h | 47 -- src/finchlite/codegen/stc/stc/sset.h | 46 -- src/finchlite/codegen/stc/stc/stack.h | 285 -------- src/finchlite/codegen/stc/stc/sys/crange.h | 118 ---- src/finchlite/codegen/stc/stc/sys/filter.h | 185 ----- src/finchlite/codegen/stc/stc/sys/finalize.h | 5 - src/finchlite/codegen/stc/stc/sys/sumtype.h | 172 ----- src/finchlite/codegen/stc/stc/sys/utility.h | 188 ------ src/finchlite/codegen/stc/stc/types.h | 223 ------ src/finchlite/codegen/stc/stc/utf8.h | 37 - src/finchlite/codegen/stc/stc/vec.h | 397 ----------- src/finchlite/codegen/stc/stc/zsview.h | 173 ----- 46 files changed, 9741 deletions(-) delete mode 100644 src/finchlite/codegen/stc/c11/fmt.h delete mode 100644 src/finchlite/codegen/stc/stc/algorithm.h delete mode 100644 src/finchlite/codegen/stc/stc/arc.h delete mode 100644 src/finchlite/codegen/stc/stc/box.h delete mode 100644 src/finchlite/codegen/stc/stc/cbits.h delete mode 100644 src/finchlite/codegen/stc/stc/common.h delete mode 100644 src/finchlite/codegen/stc/stc/coption.h delete mode 100644 src/finchlite/codegen/stc/stc/coroutine.h delete mode 100644 src/finchlite/codegen/stc/stc/cregex.h delete mode 100644 src/finchlite/codegen/stc/stc/cspan.h delete mode 100644 src/finchlite/codegen/stc/stc/cstr.h delete mode 100644 src/finchlite/codegen/stc/stc/csview.h delete mode 100644 src/finchlite/codegen/stc/stc/ctest.h delete mode 100644 src/finchlite/codegen/stc/stc/deque.h delete mode 100644 src/finchlite/codegen/stc/stc/hashmap.h delete mode 100644 src/finchlite/codegen/stc/stc/hashset.h delete mode 100644 src/finchlite/codegen/stc/stc/hmap.h delete mode 100644 src/finchlite/codegen/stc/stc/hset.h delete mode 100644 src/finchlite/codegen/stc/stc/list.h delete mode 100644 src/finchlite/codegen/stc/stc/pqueue.h delete mode 100644 src/finchlite/codegen/stc/stc/priv/cstr_prv.h delete mode 100644 src/finchlite/codegen/stc/stc/priv/linkage.h delete mode 100644 src/finchlite/codegen/stc/stc/priv/linkage2.h delete mode 100644 src/finchlite/codegen/stc/stc/priv/queue_prv.h delete mode 100644 src/finchlite/codegen/stc/stc/priv/sort_prv.h delete mode 100644 src/finchlite/codegen/stc/stc/priv/template.h delete mode 100644 src/finchlite/codegen/stc/stc/priv/template2.h delete mode 100644 src/finchlite/codegen/stc/stc/priv/utf8_prv.h delete mode 100644 src/finchlite/codegen/stc/stc/queue.h delete mode 100644 src/finchlite/codegen/stc/stc/random.h delete mode 100644 src/finchlite/codegen/stc/stc/rc.h delete mode 100644 src/finchlite/codegen/stc/stc/smap.h delete mode 100644 src/finchlite/codegen/stc/stc/sort.h delete mode 100644 src/finchlite/codegen/stc/stc/sortedmap.h delete mode 100644 src/finchlite/codegen/stc/stc/sortedset.h delete mode 100644 src/finchlite/codegen/stc/stc/sset.h delete mode 100644 src/finchlite/codegen/stc/stc/stack.h delete mode 100644 src/finchlite/codegen/stc/stc/sys/crange.h delete mode 100644 src/finchlite/codegen/stc/stc/sys/filter.h delete mode 100644 src/finchlite/codegen/stc/stc/sys/finalize.h delete mode 100644 src/finchlite/codegen/stc/stc/sys/sumtype.h delete mode 100644 src/finchlite/codegen/stc/stc/sys/utility.h delete mode 100644 src/finchlite/codegen/stc/stc/types.h delete mode 100644 src/finchlite/codegen/stc/stc/utf8.h delete mode 100644 src/finchlite/codegen/stc/stc/vec.h delete mode 100644 src/finchlite/codegen/stc/stc/zsview.h diff --git a/src/finchlite/codegen/stc/c11/fmt.h b/src/finchlite/codegen/stc/c11/fmt.h deleted file mode 100644 index 93f8d234..00000000 --- a/src/finchlite/codegen/stc/c11/fmt.h +++ /dev/null @@ -1,301 +0,0 @@ -#ifndef FMT_H_INCLUDED -#define FMT_H_INCLUDED -/* -VER 2.4 API: -void fmt_print(fmt, ...); -void fmt_println(fmt, ...); -void fmt_printd(dst, fmt, ...); -const char* fmt_time(fmt, const struct tm* tm, int MAXLEN); -void fmt_close(fmt_stream* ss); - - dst - destination, one of: - FILE* fp Write to a file - char* strbuf Write to a pre-allocated string buffer - fmt_stream* ss Write to a string-stream (auto allocated). - Set ss->overwrite=1 for overwrite-mode. - Call fmt_close(ss) after usage. - - fmt - format string (const char*) - {} Auto-detected format. If :MOD is not specified, - float will use ".8g" format, and double ".16g". - {:MODS} Format modifiers: '<' left align (replaces '-'). Default for char* and char. - '>' right align. Default for numbers. - Other than that MODS can be regular printf() format modifiers. - {{ }} % Print the '{', '}', and '%' characters. - -* C11 or higher required. -* MAX 12 arguments after fmt string. -* Define FMT_IMPLEMENT, STC_IMPLEMENT or i_implement prior to #include in one translation unit. -* (c) operamint, 2022-2025, MIT License. ------------------------------------------------------------------------------------ -#define i_implement -#include "c11/fmt.h" - -int main(void) { - const double pi = 3.141592653589793; - const size_t x = 1234567890; - const char* string = "Hello world"; - const wchar_t* wstr = L"The whole"; - const char z = 'z'; - _Bool flag = 1; - unsigned char r = 123, g = 214, b = 90, w = 110; - char buffer[64]; - - fmt_print("Color: ({} {} {}), {}\n", r, g, b, flag); - fmt_println("Wide: {}, {}", wstr, L"wide world"); - fmt_println("{:10} {:10} {:10.2f}", 42ull, 43, pi); - fmt_println("{:>10} {:>10} {:>10}", z, z, w); - fmt_printd(stdout, "{:10} {:10} {:10}\n", "Hello", "Mad", "World"); - fmt_printd(stderr, "100%: {:<20} {:.*} {}\n", string, 4, pi, x); - fmt_printd(buffer, "Precision: {} {:.10} {}", string, pi, x); - fmt_println("{}", buffer); - fmt_println("Vector: ({}, {}, {})", 3.2, 3.3, pi); - - fmt_stream ss[1] = {0}; - fmt_printd(ss, "{} {}", "Pi is:", pi); - fmt_print("{}, len={}, cap={}\n", ss->data, ss->len, ss->cap); - fmt_printd(ss, "{} {}", ", Pi squared is:", pi*pi); - fmt_print("{}, len={}, cap={}\n", ss->data, ss->len, ss->cap); - fmt_close(ss); - - time_t now = time(NULL); - struct tm t1 = *localtime(&now), t2 = t1; - t2.tm_hour += 48; - mktime(&t2); - fmt_print("Dates: {} and {}\n", fmt_time("%Y-%m-%d %X %Z", &t1, 63), - fmt_time("%Y-%m-%d %X %Z", &t2, 63)); -} -*/ -#include // IWYU pragma: keep -#include -#include -#include -#if defined _WIN32 - #include -#elif defined __APPLE__ - #include -#else - #include -#endif -#define fmt_OVERLOAD(name, ...) \ - fmt_JOIN(name ## _,fmt_NUMARGS(__VA_ARGS__))(__VA_ARGS__) -#define fmt_JOIN(a, b) _fmt_JOIN0(a, b) -#define fmt_NUMARGS(...) _fmt_APPLY_ARG_N((__VA_ARGS__, _fmt_RSEQ_N)) -#define _fmt_JOIN0(a, b) a ## b -#define _fmt_APPLY_ARG_N(args) _fmt_ARG_N args -#define _fmt_RSEQ_N 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, -#define _fmt_ARG_N(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,N,...) N - -#if defined FMT_NDEBUG || defined STC_NDEBUG || defined NDEBUG -# define fmt_OK(exp) (void)(exp) -#else -# define fmt_OK(exp) assert(exp) -#endif - -typedef struct { - char* data; - ptrdiff_t cap, len; - _Bool overwrite; -} fmt_stream; - -#if defined FMT_STATIC || defined STC_STATIC || defined i_static - #define FMT_API static - #define FMT_DEF static -#elif defined FMT_IMPLEMENT || defined STC_IMPLEMENT || defined i_implement - #define FMT_API extern - #define FMT_DEF -#else - #define FMT_API -#endif - -struct tm; -FMT_API const char* _fmt_time(const char *fmt, const struct tm* tm, char* buf, int bufsize); -FMT_API void fmt_close(fmt_stream* ss); -FMT_API int _fmt_parse(char* p, int nargs, const char *fmt, ...); -FMT_API void _fmt_sprint(fmt_stream*, const char* fmt, ...); -#define _fmt_init(_fs, _fmt, fmt, nargs) \ - const char* _fmt = fmt; \ - char* _fs = (char*)alloca(strlen(_fmt) + nargs*3 + 1) // "{}" => "%.16g" => +3 - -#define fmt_print(...) fmt_printd(stdout, __VA_ARGS__) -#define fmt_println(...) fmt_printd((fmt_stream*)0, __VA_ARGS__) -#define fmt_printd(...) fmt_OVERLOAD(fmt_printd, __VA_ARGS__) -#define fmt_time(fmt, tm, MAXLEN) _fmt_time(fmt, tm, (char[MAXLEN + 1]){0}, MAXLEN + 1) - -#define fmt_sv "{:.*s}" -#define fmt_svarg(sv) (int)(sv).size, (sv).buf - -/* Primary function. */ -#define fmt_printd_2(to, fmt) \ - do { _fmt_init(_fs, _fmt, fmt, 0); int _n = _fmt_parse(_fs, 0, _fmt); \ - fmt_OK(_n == 0); _fmt_fn(to)(to, _fmt); } while (0) -#define fmt_printd_3(to, fmt, a) \ - do { _fmt_init(_fs, _fmt, fmt, 1); int _n = _fmt_parse(_fs, 1, _fmt, _fc(a)); \ - fmt_OK(_n == 1); _fmt_fn(to)(to, _fs, a); } while (0) -#define fmt_printd_4(to, fmt, a, b) \ - do { _fmt_init(_fs, _fmt, fmt, 2); int _n = _fmt_parse(_fs, 2, _fmt, _fc(a), _fc(b)); \ - fmt_OK(_n == 2); _fmt_fn(to)(to, _fs, a, b); } while (0) -#define fmt_printd_5(to, fmt, a, b, c) \ - do { _fmt_init(_fs, _fmt, fmt, 3); int _n = _fmt_parse(_fs, 3, _fmt, _fc(a), _fc(b), _fc(c)); \ - fmt_OK(_n == 3); _fmt_fn(to)(to, _fs, a, b, c); } while (0) -#define fmt_printd_6(to, fmt, a, b, c, d) \ - do { _fmt_init(_fs, _fmt, fmt, 4); int _n = _fmt_parse(_fs, 4, _fmt, _fc(a), _fc(b), _fc(c), _fc(d)); \ - fmt_OK(_n == 4); _fmt_fn(to)(to, _fs, a, b, c, d); } while (0) -#define fmt_printd_7(to, fmt, a, b, c, d, e) \ - do { _fmt_init(_fs, _fmt, fmt, 5); int _n = _fmt_parse(_fs, 5, _fmt, _fc(a), _fc(b), _fc(c), _fc(d), _fc(e)); \ - fmt_OK(_n == 5); _fmt_fn(to)(to, _fs, a, b, c, d, e); } while (0) -#define fmt_printd_8(to, fmt, a, b, c, d, e, f) \ - do { _fmt_init(_fs, _fmt, fmt, 6); int _n = _fmt_parse(_fs, 6, _fmt, _fc(a), _fc(b), _fc(c), _fc(d), _fc(e), _fc(f)); \ - fmt_OK(_n == 6); _fmt_fn(to)(to, _fs, a, b, c, d, e, f); } while (0) -#define fmt_printd_9(to, fmt, a, b, c, d, e, f, g) \ - do { _fmt_init(_fs, _fmt, fmt, 7); int _n = _fmt_parse(_fs, 7, _fmt, _fc(a), _fc(b), _fc(c), _fc(d), _fc(e), _fc(f), _fc(g)); \ - fmt_OK(_n == 7); _fmt_fn(to)(to, _fs, a, b, c, d, e, f, g); } while (0) -#define fmt_printd_10(to, fmt, a, b, c, d, e, f, g, h) \ - do { _fmt_init(_fs, _fmt, fmt, 8); int _n = _fmt_parse(_fs, 8, _fmt, _fc(a), _fc(b), _fc(c), _fc(d), _fc(e), _fc(f), _fc(g), _fc(h)); \ - fmt_OK(_n == 8); _fmt_fn(to)(to, _fs, a, b, c, d, e, f, g, h); } while (0) -#define fmt_printd_11(to, fmt, a, b, c, d, e, f, g, h, i) \ - do { _fmt_init(_fs, _fmt, fmt, 9); int _n = _fmt_parse(_fs, 9, _fmt, _fc(a), _fc(b), _fc(c), _fc(d), _fc(e), _fc(f), _fc(g), _fc(h), \ - _fc(i)); \ - fmt_OK(_n == 9); _fmt_fn(to)(to, _fs, a, b, c, d, e, f, g, h, i); } while (0) -#define fmt_printd_12(to, fmt, a, b, c, d, e, f, g, h, i, j) \ - do { _fmt_init(_fs, _fmt, fmt, 10); int _n = _fmt_parse(_fs, 10, _fmt, _fc(a), _fc(b), _fc(c), _fc(d), _fc(e), _fc(f), _fc(g), _fc(h), \ - _fc(i), _fc(j)); \ - fmt_OK(_n == 10); _fmt_fn(to)(to, _fs, a, b, c, d, e, f, g, h, i, j); } while (0) -#define fmt_printd_13(to, fmt, a, b, c, d, e, f, g, h, i, j, k) \ - do { _fmt_init(_fs, _fmt, fmt, 11); int _n = _fmt_parse(_fs, 11, _fmt, _fc(a), _fc(b), _fc(c), _fc(d), _fc(e), _fc(f), _fc(g), _fc(h), \ - _fc(i), _fc(j), _fc(k)); \ - fmt_OK(_n == 11); _fmt_fn(to)(to, _fs, a, b, c, d, e, f, g, h, i, j, k); } while (0) -#define fmt_printd_14(to, fmt, a, b, c, d, e, f, g, h, i, j, k, l) \ - do { _fmt_init(_fs, _fmt, fmt, 12); int _n = _fmt_parse(_fs, 12, _fmt, _fc(a), _fc(b), _fc(c), _fc(d), _fc(e), _fc(f), _fc(g), _fc(h), \ - _fc(i), _fc(j), _fc(k), _fc(l)); \ - fmt_OK(_n == 12); _fmt_fn(to)(to, _fs, a, b, c, d, e, f, g, h, i, j, k, l); } while (0) - -#define _fmt_fn(x) _Generic ((x), \ - FILE*: fprintf, \ - char*: sprintf, \ - fmt_stream*: _fmt_sprint) - -#if defined(_MSC_VER) && !defined(__clang__) - #define _signed_char_hhd -#else - #define _signed_char_hhd signed char: "c", -#endif - -#if defined(__GNUC__) || defined(__clang__) || defined(__INTEL_LLVM_COMPILER) - #define FMT_UNUSED __attribute__((unused)) -#else - #define FMT_UNUSED -#endif - -#define _fc(x) _Generic (x, \ - _Bool: "d", \ - unsigned char: "hhu", \ - _signed_char_hhd \ - char: "c", \ - short: "hd", \ - unsigned short: "hu", \ - int: "d", \ - unsigned: "u", \ - long: "ld", \ - unsigned long: "lu", \ - long long: "lld", \ - unsigned long long: "llu", \ - float: "g", \ - double: "@g", \ - long double: "@Lg", \ - char*: "s", \ - wchar_t*: "ls", \ - void*: "p", \ - const char*: "s", \ - const wchar_t*: "ls", \ - const void*: "p") - -#if defined FMT_DEF - -#include -#include -#include - -FMT_DEF FMT_UNUSED void fmt_close(fmt_stream* ss) { - free(ss->data); -} - -FMT_DEF FMT_UNUSED -const char* _fmt_time(const char *fmt, const struct tm* tm, char* buf, int bufsize) { - strftime(buf, (size_t)bufsize, fmt, tm); - return buf; -} - -FMT_DEF void _fmt_sprint(fmt_stream* ss, const char* fmt, ...) { - va_list args, args2; - va_start(args, fmt); - if (ss == NULL) { - vprintf(fmt, args); putchar('\n'); - goto done1; - } - va_copy(args2, args); - const int n = vsnprintf(NULL, 0U, fmt, args); - if (n < 0) goto done2; - const ptrdiff_t pos = ss->overwrite ? 0 : ss->len; - ss->len = pos + n; - if (ss->len > ss->cap) { - ss->cap = ss->len + ss->cap/2; - ss->data = (char*)realloc(ss->data, (size_t)ss->cap + 1U); - } - vsnprintf(ss->data + pos, (size_t)n+1, fmt, args2); - done2: va_end(args2); - done1: va_end(args); -} - -FMT_DEF int _fmt_parse(char* p, int nargs, const char *fmt, ...) { - char *arg, *p0, ch; - int n = 0, empty; - va_list args; - va_start(args, fmt); - do { - switch ((ch = *fmt)) { - case '%': - *p++ = '%'; - break; - case '}': - if (*++fmt == '}') break; /* ok */ - n = 99; - continue; - case '{': - if (*++fmt == '{') break; /* ok */ - if (++n > nargs) continue; - if (*fmt != ':' && *fmt != '}') n = 99; - fmt += (*fmt == ':'); - empty = *fmt == '}'; - arg = va_arg(args, char *); - *p++ = '%', p0 = p; - while (1) switch (*fmt) { - case '\0': n = 99; /* FALLTHRU */ - case '}': goto done; - case '<': *p++ = '-', ++fmt; break; - case '>': p0 = NULL; /* FALLTHRU */ - case '-': ++fmt; break; - case '*': if (++n <= nargs) arg = va_arg(args, char *); /* FALLTHRU */ - default: *p++ = *fmt++; - } - done: - switch (*arg) { - case 'g': if (empty) memcpy(p, ".8", 2), p += 2; break; - case '@': ++arg; if (empty) memcpy(p, ".16", 3), p += 3; break; - } - if (strchr("csdioxXufFeEaAgGnp", fmt[-1]) == NULL) - while (*arg) *p++ = *arg++; - if (p0 && (p[-1] == 's' || p[-1] == 'c')) /* left-align str */ - memmove(p0 + 1, p0, (size_t)(p++ - p0)), *p0 = '-'; - fmt += *fmt == '}'; - continue; - } - *p++ = *fmt++; - } while (ch); - va_end(args); - return n; -} -#endif -#endif -#undef i_implement -#undef i_static diff --git a/src/finchlite/codegen/stc/stc/algorithm.h b/src/finchlite/codegen/stc/stc/algorithm.h deleted file mode 100644 index a517a155..00000000 --- a/src/finchlite/codegen/stc/stc/algorithm.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef STC_ALGORITHM_H_INCLUDED -#define STC_ALGORITHM_H_INCLUDED - -// IWYU pragma: begin_exports -#include "sys/crange.h" -#include "sys/filter.h" -#include "sys/utility.h" -#include "sys/sumtype.h" -// IWYU pragma: end_exports - -#endif // STC_ALGORITHM_H_INCLUDED diff --git a/src/finchlite/codegen/stc/stc/arc.h b/src/finchlite/codegen/stc/stc/arc.h deleted file mode 100644 index f703bb67..00000000 --- a/src/finchlite/codegen/stc/stc/arc.h +++ /dev/null @@ -1,254 +0,0 @@ -/* MIT License - * - * Copyright (c) 2025 Tyge Løvset - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -/* arc: atomic reference counted shared_ptr (new implementation) - * - * The difference between arc and arc2 is that arc only takes up one pointer, - * whereas arc2 uses two. arc cannot be constructed from an already allocated pointer, - * which arc2 may. To use arc2, specify the `(c_arc2)` option after the key type, e.g.: - * #define T MyArc, MyType, (c_arc2 | c_no_atomic) - */ -/* -#include - -typedef struct { cstr name, last; } Person; - -Person Person_make(const char* name, const char* last) { - return (Person){.name = cstr_from(name), .last = cstr_from(last)}; -} -Person Person_clone(Person p) { - p.name = cstr_clone(p.name); - p.last = cstr_clone(p.last); - return p; -} -void Person_drop(Person* p) { - printf("drop: %s %s\n", cstr_str(&p->name), cstr_str(&p->last)); - cstr_drop(&p->name); - cstr_drop(&p->last); -} - -#define T ArcPers, Person, (c_keyclass) // clone, drop, cmp, hash -#include - -int main(void) { - ArcPers p = ArcPers_from(Person_make("John", "Smiths")); - ArcPers q = ArcPers_clone(p); // share the pointer - - printf("%s %s. uses: %ld\n", cstr_str(&q.get->name), cstr_str(&q.get->last), ArcPers_use_count(q)); - c_drop(ArcPers, &p, &q); -} -*/ -#include "priv/linkage.h" -#include "types.h" - -#ifndef STC_ARC_H_INCLUDED -#define STC_ARC_H_INCLUDED -#include "common.h" -#include - -#if defined __GNUC__ || defined __clang__ || defined _MSC_VER || defined i_no_atomic - typedef long catomic_long; -#else // try with C11 - typedef _Atomic(long) catomic_long; -#endif -#if defined _MSC_VER - #include - #define c_atomic_inc(v) (void)_InterlockedIncrement(v) - #define c_atomic_dec_and_test(v) !_InterlockedDecrement(v) -#elif defined __GNUC__ || defined __clang__ - #define c_atomic_inc(v) (void)__atomic_add_fetch(v, 1, __ATOMIC_SEQ_CST) - #define c_atomic_dec_and_test(v) !__atomic_sub_fetch(v, 1, __ATOMIC_SEQ_CST) -#else // try with C11 - #include - #define c_atomic_inc(v) (void)atomic_fetch_add(v, 1) - #define c_atomic_dec_and_test(v) (atomic_fetch_sub(v, 1) == 1) -#endif -#endif // STC_ARC_H_INCLUDED - -#ifndef _i_prefix - #define _i_prefix arc_ -#endif -#define _i_is_arc -#include "priv/template.h" -typedef i_keyraw _m_raw; - -#if c_OPTION(c_no_atomic) - #define i_no_atomic -#endif -#if !defined i_no_atomic - #define _i_atomic_inc(v) c_atomic_inc(v) - #define _i_atomic_dec_and_test(v) c_atomic_dec_and_test(v) -#else - #define _i_atomic_inc(v) (void)(++*(v)) - #define _i_atomic_dec_and_test(v) !(--*(v)) -#endif - -#if c_OPTION(c_arc2) - #define i_arc2 -#endif -#if !(defined i_arc2 || defined STC_USE_ARC2) -// ------------ Arc1 size of one pointer (union) ------------- - -#ifndef i_declared -_c_DEFTYPES(declare_arc, Self, i_key); -#endif -struct _c_MEMB(_ctrl) { - _m_value value; - catomic_long counter; -}; - -// c++: std::make_shared<_m_value>(val) -STC_INLINE Self _c_MEMB(_make)(_m_value val) { - Self arc = {.ctrl=_i_new_n(_c_MEMB(_ctrl), 1)}; - arc.ctrl->value = val; - arc.ctrl->counter = 1; - return arc; -} - -STC_INLINE Self _c_MEMB(_toarc)(_m_value* arc_raw) - { Self arc = {.ctrl=(_c_MEMB(_ctrl) *)arc_raw}; return arc; } - -// destructor -STC_INLINE void _c_MEMB(_drop)(const Self* self) { - if (self->ctrl && _i_atomic_dec_and_test(&self->ctrl->counter)) { - i_keydrop(self->get); - i_free(self->ctrl, c_sizeof *self->ctrl); - } -} - -#else // ------------ Arc2 size of two pointers ------------- - -#ifndef i_declared -_c_DEFTYPES(declare_arc2, Self, i_key); -#endif -struct _c_MEMB(_ctrl) { - catomic_long counter; // nb! counter <-> value order is swapped. - _m_value value; -}; -#define ctrl ctrl2 - -// c++: std::make_shared<_m_value>(val) -STC_INLINE Self _c_MEMB(_make)(_m_value val) { - Self out = {.ctrl2=_i_new_n(_c_MEMB(_ctrl), 1)}; - out.ctrl2->counter = 1; - out.get = &out.ctrl2->value; - *out.get = val; - return out; -} - -STC_INLINE Self _c_MEMB(_from_ptr)(_m_value* ptr) { - Self out = {.get=ptr}; - if (ptr) { - enum {OFFSET = offsetof(_c_MEMB(_ctrl), value)}; - // Adds 2 dummy bytes to ensure that the second if-test in _drop() is safe. - catomic_long* _rc = (catomic_long*)i_malloc(OFFSET + 2); - out.ctrl2 = (_c_MEMB(_ctrl)*) _rc; - out.ctrl2->counter = 1; - } - return out; -} - -// destructor -STC_INLINE void _c_MEMB(_drop)(const Self* self) { - if (self->ctrl2 && _i_atomic_dec_and_test(&self->ctrl2->counter)) { - enum {OFFSET = offsetof(_c_MEMB(_ctrl), value)}; - i_keydrop(self->get); - - if ((char*)self->ctrl2 + OFFSET == (char*)self->get) { - i_free((void*)self->ctrl2, c_sizeof *self->ctrl2); // _make() - } else { - i_free((void*)self->ctrl2, OFFSET + 2); // _from_ptr() - i_free(self->get, c_sizeof *self->get); - } - } -} - -// take ownership of pointer p -STC_INLINE void _c_MEMB(_reset_to)(Self* self, _m_value* ptr) { - _c_MEMB(_drop)(self); - *self = _c_MEMB(_from_ptr)(ptr); -} - -#endif // ---------- end Arc2 with two pointers ------------ - -STC_INLINE long _c_MEMB(_use_count)(Self arc) - { return arc.ctrl ? arc.ctrl->counter : 0; } - -STC_INLINE Self _c_MEMB(_init)(void) - { return c_literal(Self){0}; } - -STC_INLINE Self _c_MEMB(_from)(_m_raw raw) - { return _c_MEMB(_make)(i_keyfrom(raw)); } - -STC_INLINE _m_raw _c_MEMB(_toraw)(const Self* self) - { return i_keytoraw(self->get); } - -// move ownership to receiving arc -STC_INLINE Self _c_MEMB(_move)(Self* self) { - Self arc = *self; - *self = (Self){0}; - return arc; // now unowned -} - -// take ownership of unowned arc -STC_INLINE void _c_MEMB(_take)(Self* self, Self unowned) { - _c_MEMB(_drop)(self); - *self = unowned; // now owned -} - -// make shared ownership with owned arc -STC_INLINE void _c_MEMB(_assign)(Self* self, const Self* owned) { - if (owned->ctrl) - _i_atomic_inc(&owned->ctrl->counter); - _c_MEMB(_drop)(self); - *self = *owned; -} - -// clone by sharing. Does not use i_keyclone, so OK to always define. -STC_INLINE Self _c_MEMB(_clone)(Self owned) { - if (owned.ctrl) - _i_atomic_inc(&owned.ctrl->counter); - return owned; -} - -#if defined _i_has_cmp - STC_INLINE int _c_MEMB(_raw_cmp)(const _m_raw* rx, const _m_raw* ry) - { return i_cmp(rx, ry); } -#endif - -#if defined _i_has_eq - STC_INLINE bool _c_MEMB(_raw_eq)(const _m_raw* rx, const _m_raw* ry) - { return i_eq(rx, ry); } -#endif - -#if !defined i_no_hash && defined _i_has_eq - STC_INLINE size_t _c_MEMB(_raw_hash)(const _m_raw* rx) - { return i_hash(rx); } -#endif // i_no_hash - -#undef ctrl -#undef i_no_atomic -#undef i_arc2 -#undef _i_atomic_inc -#undef _i_atomic_dec_and_test -#undef _i_is_arc -#include "sys/finalize.h" diff --git a/src/finchlite/codegen/stc/stc/box.h b/src/finchlite/codegen/stc/stc/box.h deleted file mode 100644 index 366bda58..00000000 --- a/src/finchlite/codegen/stc/stc/box.h +++ /dev/null @@ -1,168 +0,0 @@ - -/* MIT License - * - * Copyright (c) 2025 Tyge Løvset - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/* cbox: heap allocated boxed type -#include - -typedef struct { cstr name, email; } Person; - -Person Person_from(const char* name, const char* email) { - return (Person){.name = cstr_from(name), .email = cstr_from(email)}; -} -Person Person_clone(Person p) { - p.name = cstr_clone(p.name); - p.email = cstr_clone(p.email); - return p; -} -void Person_drop(Person* p) { - printf("drop: %s %s\n", cstr_str(&p->name), cstr_str(&p->email)); - c_drop(cstr, &p->name, &p->email); -} - -#define T PBox, Person, (c_keyclass) // bind Person clone+drop fn's -#include - -int main(void) { - PBox p = PBox_from(Person_from("John Smiths", "josmiths@gmail.com")); - PBox q = PBox_clone(p); - cstr_assign(&q.get->name, "Joe Smiths"); - - printf("%s %s.\n", cstr_str(&p.get->name), cstr_str(&p.get->email)); - printf("%s %s.\n", cstr_str(&q.get->name), cstr_str(&q.get->email)); - - c_drop(PBox, &p, &q); -} -*/ -#include "priv/linkage.h" -#include "types.h" - -#ifndef STC_BOX_H_INCLUDED -#define STC_BOX_H_INCLUDED -#include "common.h" -#include - -#define cbox_null {0} -#endif // STC_BOX_H_INCLUDED - -#ifndef _i_prefix - #define _i_prefix box_ -#endif -#define _i_is_box -#include "priv/template.h" -typedef i_keyraw _m_raw; - -#ifndef i_declared -_c_DEFTYPES(declare_box, Self, i_key); -#endif - -// constructors (take ownership) -STC_INLINE Self _c_MEMB(_init)(void) - { return c_literal(Self){0}; } - -STC_INLINE long _c_MEMB(_use_count)(const Self* self) - { return (long)(self->get != NULL); } - - -// c++: std::make_unique(val) -STC_INLINE Self _c_MEMB(_make)(_m_value val) { - Self box = {_i_new_n(_m_value, 1)}; - *box.get = val; - return box; -} - -STC_INLINE Self _c_MEMB(_from_ptr)(_m_value* p) - { return c_literal(Self){p}; } - -STC_INLINE Self _c_MEMB(_from)(_m_raw raw) - { return _c_MEMB(_make)(i_keyfrom(raw)); } - -STC_INLINE _m_raw _c_MEMB(_toraw)(const Self* self) - { return i_keytoraw(self->get); } - -// destructor -STC_INLINE void _c_MEMB(_drop)(const Self* self) { - if (self->get) { - i_keydrop(self->get); - i_free(self->get, c_sizeof *self->get); - } -} - -// move ownership to receiving box -STC_INLINE Self _c_MEMB(_move)(Self* self) { - Self box = *self; - self->get = NULL; - return box; -} - -// release owned pointer, must be manually freed by receiver -STC_INLINE _m_value* _c_MEMB(_release)(Self* self) - { return _c_MEMB(_move)(self).get; } - -// take ownership of pointer p -STC_INLINE void _c_MEMB(_reset_to)(Self* self, _m_value* p) { - _c_MEMB(_drop)(self); - self->get = p; -} - -// take ownership of unowned box -STC_INLINE void _c_MEMB(_take)(Self* self, Self unowned) { - _c_MEMB(_drop)(self); - *self = unowned; -} - -// transfer ownership from other; set other to NULL -STC_INLINE void _c_MEMB(_assign)(Self* self, Self* owned) { - if (owned->get == self->get) - return; - _c_MEMB(_drop)(self); - *self = *owned; - owned->get = NULL; -} - -#ifndef i_no_clone - STC_INLINE Self _c_MEMB(_clone)(Self other) { - if (other.get == NULL) return other; - Self out = {_i_new_n(_m_value, 1)}; - *out.get = i_keyclone((*other.get)); - return out; - } -#endif // !i_no_clone - - -#if defined _i_has_cmp - STC_INLINE int _c_MEMB(_raw_cmp)(const _m_raw* rx, const _m_raw* ry) - { return i_cmp(rx, ry); } -#endif - -#if defined _i_has_eq - STC_INLINE bool _c_MEMB(_raw_eq)(const _m_raw* rx, const _m_raw* ry) - { return i_eq(rx, ry); } -#endif - -#if !defined i_no_hash && defined _i_has_eq - STC_INLINE size_t _c_MEMB(_raw_hash)(const _m_raw* rx) - { return i_hash(rx); } -#endif // i_no_hash -#undef _i_is_box -#include "sys/finalize.h" diff --git a/src/finchlite/codegen/stc/stc/cbits.h b/src/finchlite/codegen/stc/stc/cbits.h deleted file mode 100644 index ac424ba4..00000000 --- a/src/finchlite/codegen/stc/stc/cbits.h +++ /dev/null @@ -1,336 +0,0 @@ -/* MIT License - * - * Copyright (c) 2025 Tyge Løvset - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -/* -Similar to boost::dynamic_bitset / std::bitset - -#include -#include "cbits.h" - -int main(void) { - cbits bset = cbits_with_size(23, true); - cbits_reset(&bset, 9); - cbits_resize(&bset, 43, false); - - printf("%4d: ", (int)cbits_size(&bset)); - for (c_range(i, cbits_size(&bset))) - printf("%d", cbits_at(&bset, i)); - puts(""); - cbits_set(&bset, 28); - cbits_resize(&bset, 77, true); - cbits_resize(&bset, 93, false); - cbits_resize(&bset, 102, true); - cbits_set_value(&bset, 99, false); - - printf("%4d: ", (int)cbits_size(&bset)); - for (c_range(i, cbits_size(&bset))) - printf("%d", cbits_at(&bset, i)); - puts(""); - - cbits_drop(&bset); -} -*/ -#include "priv/linkage.h" -#ifndef STC_CBITS_H_INCLUDED -#define STC_CBITS_H_INCLUDED -#include "common.h" -#include - -#if INTPTR_MAX == INT64_MAX -#define _gnu_popc(x) __builtin_popcountll(x) -#define _msc_popc(x) (int)__popcnt64(x) -#else -#define _gnu_popc(x) __builtin_popcount(x) -#define _msc_popc(x) (int)__popcnt(x) -#endif -#define _cbits_WS c_sizeof(uintptr_t) -#define _cbits_WB (8*_cbits_WS) -#define _cbits_bit(i) ((uintptr_t)1 << ((i) & (_cbits_WB - 1))) -#define _cbits_words(n) (isize)(((n) + (_cbits_WB - 1))/_cbits_WB) -#define _cbits_bytes(n) (_cbits_words(n)*_cbits_WS) - -#if defined _MSC_VER - #include - STC_INLINE int c_popcount(uintptr_t x) { return _msc_popc(x); } -#elif defined __GNUC__ || defined __clang__ - STC_INLINE int c_popcount(uintptr_t x) { return _gnu_popc(x); } -#else - STC_INLINE int c_popcount(uintptr_t x) { /* http://en.wikipedia.org/wiki/Hamming_weight */ - x -= (x >> 1) & (uintptr_t)0x5555555555555555; - x = (x & (uintptr_t)0x3333333333333333) + ((x >> 2) & (uintptr_t)0x3333333333333333); - x = (x + (x >> 4)) & (uintptr_t)0x0f0f0f0f0f0f0f0f; - return (int)((x*(uintptr_t)0x0101010101010101) >> (_cbits_WB - 8)); - } -#endif -#if defined __GNUC__ && !defined __clang__ && !defined __cplusplus -#pragma GCC diagnostic ignored "-Walloc-size-larger-than=" // gcc 11.4 -#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" // gcc 11.4 -#endif - -#define cbits_print(...) c_MACRO_OVERLOAD(cbits_print, __VA_ARGS__) -#define cbits_print_1(self) cbits_print_4(self, stdout, 0, -1) -#define cbits_print_2(self, stream) cbits_print_4(self, stream, 0, -1) -#define cbits_print_4(self, stream, start, end) cbits_print_5(cbits, self, stream, start, end) -#define cbits_print_3(SetType, self, stream) cbits_print_5(SetType, self, stream, 0, -1) -#define cbits_print_5(SetType, self, stream, start, end) do { \ - const SetType* _cb_set = self; \ - isize _cb_start = start, _cb_end = end; \ - if (_cb_end == -1) _cb_end = SetType##_size(_cb_set); \ - for (c_range_3(_cb_i, _cb_start, _cb_end)) \ - fputc(SetType##_test(_cb_set, _cb_i) ? '1' : '0', stream); \ -} while (0) - -STC_INLINE isize _cbits_count(const uintptr_t* set, const isize sz) { - const isize n = sz/_cbits_WB; - isize count = 0; - for (isize i = 0; i < n; ++i) - count += c_popcount(set[i]); - if (sz & (_cbits_WB - 1)) - count += c_popcount(set[n] & (_cbits_bit(sz) - 1)); - return count; -} - -STC_INLINE char* _cbits_to_str(const uintptr_t* set, const isize sz, - char* out, isize start, isize stop) { - if (stop > sz) stop = sz; - c_assert(start <= stop); - - c_memset(out, '0', stop - start); - for (isize i = start; i < stop; ++i) - if ((set[i/_cbits_WB] & _cbits_bit(i)) != 0) - out[i - start] = '1'; - out[stop - start] = '\0'; - return out; -} - -#define _cbits_OPR(OPR, VAL) \ - const isize n = sz/_cbits_WB; \ - for (isize i = 0; i < n; ++i) \ - if ((set[i] OPR other[i]) != VAL) \ - return false; \ - if ((sz & (_cbits_WB - 1)) == 0) \ - return true; \ - const uintptr_t i = (uintptr_t)n, m = _cbits_bit(sz) - 1; \ - return ((set[i] OPR other[i]) & m) == (VAL & m) - -STC_INLINE bool _cbits_subset_of(const uintptr_t* set, const uintptr_t* other, const isize sz) - { _cbits_OPR(|, set[i]); } - -STC_INLINE bool _cbits_disjoint(const uintptr_t* set, const uintptr_t* other, const isize sz) - { _cbits_OPR(&, 0); } - -#endif // STC_CBITS_H_INCLUDED - -#if defined T && !defined i_type - #define i_type T -#endif -#if defined i_type - #define Self c_GETARG(1, i_type) - #define _i_length c_GETARG(2, i_type) -#else - #define Self cbits -#endif -#ifndef i_allocator - #define i_allocator c -#endif -#define _i_MEMB(name) c_JOIN(Self, name) - - -#if !defined _i_length // DYNAMIC SIZE BITARRAY - -typedef struct { uintptr_t *buffer; isize _size; } Self; -#define _i_assert(x) c_assert(x) - -STC_INLINE void cbits_drop(cbits* self) { i_free(self->buffer, _cbits_bytes(self->_size)); } -STC_INLINE isize cbits_size(const cbits* self) { return self->_size; } - -STC_INLINE cbits* cbits_take(cbits* self, cbits other) { - if (self->buffer != other.buffer) { - cbits_drop(self); - *self = other; - } - return self; -} - -STC_INLINE cbits cbits_clone(cbits other) { - cbits set = other; - const isize bytes = _cbits_bytes(other._size); - set.buffer = (uintptr_t *)c_safe_memcpy(i_malloc(bytes), other.buffer, bytes); - return set; -} - -STC_INLINE cbits* cbits_copy(cbits* self, const cbits* other) { - if (self->buffer == other->buffer) - return self; - if (self->_size != other->_size) - return cbits_take(self, cbits_clone(*other)); - c_memcpy(self->buffer, other->buffer, _cbits_bytes(other->_size)); - return self; -} - -STC_INLINE bool cbits_resize(cbits* self, const isize size, const bool value) { - const isize new_w = _cbits_words(size), osize = self->_size, old_w = _cbits_words(osize); - uintptr_t* b = (uintptr_t *)i_realloc(self->buffer, old_w*_cbits_WS, new_w*_cbits_WS); - if (b == NULL) return false; - self->buffer = b; self->_size = size; - if (size > osize) { - c_memset(self->buffer + old_w, -(int)value, (new_w - old_w)*_cbits_WS); - if (osize & (_cbits_WB - 1)) { - uintptr_t mask = _cbits_bit(osize) - 1; - if (value) self->buffer[old_w - 1] |= ~mask; - else self->buffer[old_w - 1] &= mask; - } - } - return true; -} - -STC_INLINE void cbits_set_all(cbits *self, const bool value); -STC_INLINE void cbits_set_pattern(cbits *self, const uintptr_t pattern); - -STC_INLINE cbits cbits_move(cbits* self) { - cbits tmp = *self; - self->buffer = NULL, self->_size = 0; - return tmp; -} - -STC_INLINE cbits cbits_with_size(const isize size, const bool value) { - cbits set = {(uintptr_t *)i_malloc(_cbits_bytes(size)), size}; - cbits_set_all(&set, value); - return set; -} - -STC_INLINE cbits cbits_with_pattern(const isize size, const uintptr_t pattern) { - cbits set = {(uintptr_t *)i_malloc(_cbits_bytes(size)), size}; - cbits_set_pattern(&set, pattern); - return set; -} - -#else // _i_length: FIXED SIZE BITARRAY - -#define _i_assert(x) (void)0 - -typedef struct { uintptr_t buffer[(_i_length - 1)/_cbits_WB + 1]; } Self; - -STC_INLINE void _i_MEMB(_drop)(Self* self) { (void)self; } -STC_INLINE isize _i_MEMB(_size)(const Self* self) { (void)self; return _i_length; } -STC_INLINE Self _i_MEMB(_move)(Self* self) { return *self; } -STC_INLINE Self* _i_MEMB(_take)(Self* self, Self other) { *self = other; return self; } -STC_INLINE Self _i_MEMB(_clone)(Self other) { return other; } -STC_INLINE void _i_MEMB(_copy)(Self* self, const Self* other) { *self = *other; } -STC_INLINE void _i_MEMB(_set_all)(Self *self, const bool value); -STC_INLINE void _i_MEMB(_set_pattern)(Self *self, const uintptr_t pattern); - -STC_INLINE Self _i_MEMB(_with_size)(const isize size, const bool value) { - c_assert(size <= _i_length); - Self set; _i_MEMB(_set_all)(&set, value); - return set; -} - -STC_INLINE Self _i_MEMB(_with_pattern)(const isize size, const uintptr_t pattern) { - c_assert(size <= _i_length); - Self set; _i_MEMB(_set_pattern)(&set, pattern); - return set; -} -#endif // _i_length - -// COMMON: - -STC_INLINE void _i_MEMB(_set_all)(Self *self, const bool value) - { c_memset(self->buffer, -(int)value, _cbits_bytes(_i_MEMB(_size)(self))); } - -STC_INLINE void _i_MEMB(_set_pattern)(Self *self, const uintptr_t pattern) { - isize n = _cbits_words(_i_MEMB(_size)(self)); - while (n--) self->buffer[n] = pattern; -} - -STC_INLINE bool _i_MEMB(_test)(const Self* self, const isize i) - { return (self->buffer[i/_cbits_WB] & _cbits_bit(i)) != 0; } - -STC_INLINE bool _i_MEMB(_at)(const Self* self, const isize i) - { c_assert(c_uless(i, _i_MEMB(_size)(self))); return _i_MEMB(_test)(self, i); } - -STC_INLINE void _i_MEMB(_set)(Self *self, const isize i) - { self->buffer[i/_cbits_WB] |= _cbits_bit(i); } - -STC_INLINE void _i_MEMB(_reset)(Self *self, const isize i) - { self->buffer[i/_cbits_WB] &= ~_cbits_bit(i); } - -STC_INLINE void _i_MEMB(_set_value)(Self *self, const isize i, const bool b) { - self->buffer[i/_cbits_WB] ^= ((uintptr_t)-(int)b ^ self->buffer[i/_cbits_WB]) & _cbits_bit(i); -} - -STC_INLINE void _i_MEMB(_flip)(Self *self, const isize i) - { self->buffer[i/_cbits_WB] ^= _cbits_bit(i); } - -STC_INLINE void _i_MEMB(_flip_all)(Self *self) { - isize n = _cbits_words(_i_MEMB(_size)(self)); - while (n--) self->buffer[n] ^= ~(uintptr_t)0; -} - -STC_INLINE Self _i_MEMB(_from)(const char* str) { - isize n = c_strlen(str); - Self set = _i_MEMB(_with_size)(n, false); - while (n--) if (str[n] == '1') _i_MEMB(_set)(&set, n); - return set; -} - -/* Intersection */ -STC_INLINE void _i_MEMB(_intersect)(Self *self, const Self* other) { - _i_assert(self->_size == other->_size); - isize n = _cbits_words(_i_MEMB(_size)(self)); - while (n--) self->buffer[n] &= other->buffer[n]; -} -/* Union */ -STC_INLINE void _i_MEMB(_union)(Self *self, const Self* other) { - _i_assert(self->_size == other->_size); - isize n = _cbits_words(_i_MEMB(_size)(self)); - while (n--) self->buffer[n] |= other->buffer[n]; -} -/* Exclusive disjunction */ -STC_INLINE void _i_MEMB(_xor)(Self *self, const Self* other) { - _i_assert(self->_size == other->_size); - isize n = _cbits_words(_i_MEMB(_size)(self)); - while (n--) self->buffer[n] ^= other->buffer[n]; -} - -STC_INLINE isize _i_MEMB(_count)(const Self* self) - { return _cbits_count(self->buffer, _i_MEMB(_size)(self)); } - -STC_INLINE char* _i_MEMB(_to_str)(const Self* self, char* out, isize start, isize stop) - { return _cbits_to_str(self->buffer, _i_MEMB(_size)(self), out, start, stop); } - -STC_INLINE bool _i_MEMB(_subset_of)(const Self* self, const Self* other) { - _i_assert(self->_size == other->_size); - return _cbits_subset_of(self->buffer, other->buffer, _i_MEMB(_size)(self)); -} - -STC_INLINE bool _i_MEMB(_disjoint)(const Self* self, const Self* other) { - _i_assert(self->_size == other->_size); - return _cbits_disjoint(self->buffer, other->buffer, _i_MEMB(_size)(self)); -} - -#include "priv/linkage2.h" -#undef i_type -#undef _i_length -#undef _i_MEMB -#undef _i_assert -#undef Self diff --git a/src/finchlite/codegen/stc/stc/common.h b/src/finchlite/codegen/stc/stc/common.h deleted file mode 100644 index fce1829d..00000000 --- a/src/finchlite/codegen/stc/stc/common.h +++ /dev/null @@ -1,359 +0,0 @@ -/* MIT License - * - * Copyright (c) 2025 Tyge Løvset - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef STC_COMMON_H_INCLUDED -#define STC_COMMON_H_INCLUDED - -#ifdef _MSC_VER - #pragma warning(disable: 4116 4996) // unnamed type definition in parentheses -#endif -#include -#include -#include -#include -#include - -typedef ptrdiff_t isize; -#ifndef STC_NO_INT_DEFS - typedef int8_t int8; - typedef uint8_t uint8; - typedef int16_t int16; - typedef uint16_t uint16; - typedef int32_t int32; - typedef uint32_t uint32; - typedef int64_t int64; - typedef uint64_t uint64; -#endif -#if !defined STC_HAS_TYPEOF && (_MSC_FULL_VER >= 193933428 || \ - defined __GNUC__ || defined __clang__ || defined __TINYC__) - #define STC_HAS_TYPEOF 1 -#endif -#if defined __GNUC__ - #define c_GNUATTR(...) __attribute__((__VA_ARGS__)) -#else - #define c_GNUATTR(...) -#endif -#define STC_INLINE static inline c_GNUATTR(unused) -#define c_ZI PRIiPTR -#define c_ZU PRIuPTR -#define c_NPOS INTPTR_MAX - -// Macro overloading feature support -#define c_MACRO_OVERLOAD(name, ...) \ - c_JOIN(name ## _,c_NUMARGS(__VA_ARGS__))(__VA_ARGS__) -#define c_JOIN0(a, b) a ## b -#define c_JOIN(a, b) c_JOIN0(a, b) -#define c_NUMARGS(...) _c_APPLY_ARG_N((__VA_ARGS__, _c_RSEQ_N)) -#define _c_APPLY_ARG_N(args) _c_ARG_N args -#define _c_RSEQ_N 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, -#define _c_ARG_N(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,N,...) N - -// Saturated overloading -// #define foo(...) foo_I(__VA_ARGS__, c_COMMA_N(foo_3), c_COMMA_N(foo_2), c_COMMA_N(foo_1),)(__VA_ARGS__) -// #define foo_I(a,b,c, n, ...) c_TUPLE_AT_1(n, foo_n,) -#define c_TUPLE_AT_1(x,y,...) y -#define c_COMMA_N(x) ,x -#define c_EXPAND(...) __VA_ARGS__ - -// Select arg, e.g. for #define i_type A,B then c_GETARG(2, i_type) is B -#define c_GETARG(N, ...) c_ARG_##N(__VA_ARGS__,) -#define c_ARG_1(a, ...) a -#define c_ARG_2(a, b, ...) b -#define c_ARG_3(a, b, c, ...) c -#define c_ARG_4(a, b, c, d, ...) d - -#define _i_new_n(T, n) ((T*)i_malloc((n)*c_sizeof(T))) -#define _i_new_zeros(T, n) ((T*)i_calloc(n, c_sizeof(T))) -#define _i_realloc_n(ptr, old_n, n) i_realloc(ptr, (old_n)*c_sizeof *(ptr), (n)*c_sizeof *(ptr)) -#define _i_free_n(ptr, n) i_free(ptr, (n)*c_sizeof *(ptr)) - -#ifndef __cplusplus - #define c_new(T, ...) ((T*)c_safe_memcpy(c_malloc(c_sizeof(T)), ((T[]){__VA_ARGS__}), c_sizeof(T))) - #define c_literal(T) (T) - #define c_make_array(T, ...) ((T[])__VA_ARGS__) - #define c_make_array2d(T, N, ...) ((T[][N])__VA_ARGS__) -#else - #include - #define c_new(T, ...) new (c_malloc(c_sizeof(T))) T(__VA_ARGS__) - #define c_literal(T) T - template struct _c_Array { T data[M][N]; }; - #define c_make_array(T, ...) (_c_Array{{__VA_ARGS__}}.data[0]) - #define c_make_array2d(T, N, ...) (_c_Array{__VA_ARGS__}.data) -#endif - -#ifdef STC_ALLOCATOR - #define c_malloc c_JOIN(STC_ALLOCATOR, _malloc) - #define c_calloc c_JOIN(STC_ALLOCATOR, _calloc) - #define c_realloc c_JOIN(STC_ALLOCATOR, _realloc) - #define c_free c_JOIN(STC_ALLOCATOR, _free) -#else - #define c_malloc(sz) malloc(c_i2u_size(sz)) - #define c_calloc(n, sz) calloc(c_i2u_size(n), c_i2u_size(sz)) - #define c_realloc(ptr, old_sz, sz) realloc(ptr, c_i2u_size(1 ? (sz) : (old_sz))) - #define c_free(ptr, sz) ((void)(sz), free(ptr)) -#endif - -#define c_new_n(T, n) ((T*)c_malloc((n)*c_sizeof(T))) -#define c_free_n(ptr, n) c_free(ptr, (n)*c_sizeof *(ptr)) -#define c_realloc_n(ptr, old_n, n) c_realloc(ptr, (old_n)*c_sizeof *(ptr), (n)*c_sizeof *(ptr)) -#define c_delete_n(T, ptr, n) do { \ - T* _tp = ptr; isize _n = n, _i = _n; \ - while (_i--) T##_drop((_tp + _i)); \ - c_free(_tp, _n*c_sizeof(T)); \ -} while (0) - -#define c_static_assert(expr) (void)sizeof(int[(expr) ? 1 : -1]) -#if defined STC_NDEBUG || defined NDEBUG - #define c_assert(expr) (void)sizeof(expr) -#else - #define c_assert(expr) assert(expr) -#endif -#define c_container_of(p, C, m) ((C*)((char*)(1 ? (p) : &((C*)0)->m) - offsetof(C, m))) -#define c_const_cast(Tp, p) ((Tp)(1 ? (p) : (Tp)0)) -#define c_litstrlen(literal) (c_sizeof("" literal) - 1) -#define c_countof(a) (isize)(sizeof(a)/sizeof 0[a]) -#define c_arraylen(a) c_countof(a) // [deprecated]? - -// expect signed ints to/from these (use with gcc -Wconversion) -#define c_sizeof (isize)sizeof -#define c_strlen(s) (isize)strlen(s) -#define c_strncmp(a, b, ilen) strncmp(a, b, c_i2u_size(ilen)) -#define c_memcpy(d, s, ilen) memcpy(d, s, c_i2u_size(ilen)) -#define c_memmove(d, s, ilen) memmove(d, s, c_i2u_size(ilen)) -#define c_memset(d, val, ilen) memset(d, val, c_i2u_size(ilen)) -#define c_memcmp(a, b, ilen) memcmp(a, b, c_i2u_size(ilen)) -// library internal, but may be useful in user code: -#define c_u2i_size(u) (isize)(1 ? (u) : (size_t)1) // warns if u is signed -#define c_i2u_size(i) (size_t)(1 ? (i) : -1) // warns if i is unsigned -#define c_uless(a, b) ((size_t)(a) < (size_t)(b)) -#define c_safe_cast(T, From, x) ((T)(1 ? (x) : (From){0})) - -// x, y are i_keyraw* type, which defaults to i_key*. vp is i_key* type. -#define c_memcmp_eq(x, y) (memcmp(x, y, sizeof *(x)) == 0) -#define c_default_eq(x, y) (*(x) == *(y)) -#define c_default_less(x, y) (*(x) < *(y)) -#define c_default_cmp(x, y) (c_default_less(y, x) - c_default_less(x, y)) -#define c_default_hash(vp) c_hash_n(vp, sizeof *(vp)) -#define c_default_clone(v) (v) -#define c_default_toraw(vp) (*(vp)) -#define c_default_drop(vp) ((void) (vp)) - -// non-owning char pointer -typedef const char* cstr_raw; -#define cstr_raw_cmp(x, y) strcmp(*(x), *(y)) -#define cstr_raw_eq(x, y) (cstr_raw_cmp(x, y) == 0) -#define cstr_raw_hash(vp) c_hash_str(*(vp)) -#define cstr_raw_clone(v) (v) -#define cstr_raw_drop(vp) ((void)vp) - -// Control block macros - -// [deprecated]: -#define c_init(...) c_make(__VA_ARGS__) -#define c_forlist(...) for (c_items(_VA_ARGS__)) -#define c_foritems(...) for (c_items(__VA_ARGS__)) -#define c_foreach(...) for (c_each(__VA_ARGS__)) -#define c_foreach_n(...) for (c_each_n(__VA_ARGS__)) -#define c_foreach_kv(...) for (c_each_kv(__VA_ARGS__)) -#define c_foreach_reverse(...) for (c_each_reverse(__VA_ARGS__)) -#define c_forrange(...) for (c_range(__VA_ARGS__)) -#define c_forrange32(...) for (c_range32(__VA_ARGS__)) - -// New: -#define c_each(...) c_MACRO_OVERLOAD(c_each, __VA_ARGS__) -#define c_each_3(it, C, cnt) \ - C##_iter it = C##_begin(&cnt); it.ref; C##_next(&it) -#define c_each_4(it, C, start, end) \ - _c_each(it, C, start, (end).ref, _) - -#define c_each_item(v, C, cnt) \ - C##_value* v = (C##_value*)&v; v; ) \ - for (C##_iter v##_it = C##_begin(&cnt); (v = v##_it.ref); C##_next(&v##_it) - -#define c_each_n(...) c_MACRO_OVERLOAD(c_each_n, __VA_ARGS__) -#define c_each_n_3(it, C, cnt) c_each_n_4(it, C, cnt, INTPTR_MAX) -#define c_each_n_4(it, C, cnt, n) \ - struct {C##_iter iter; C##_value* ref; isize size, index;} \ - it = {.iter=C##_begin(&cnt), .size=n}; (it.ref = it.iter.ref) && it.index < it.size; C##_next(&it.iter), ++it.index - -#define c_each_reverse(...) c_MACRO_OVERLOAD(c_each_reverse, __VA_ARGS__) -#define c_each_reverse_3(it, C, cnt) /* works for stack, vec, queue, deque */ \ - C##_iter it = C##_rbegin(&cnt); it.ref; C##_rnext(&it) -#define c_each_reverse_4(it, C, start, end) \ - _c_each(it, C, start, (end).ref, _r) - -#define _c_each(it, C, start, endref, rev) /* private */ \ - C##_iter it = (start), *_endref_##it = c_safe_cast(C##_iter*, C##_value*, endref) \ - ; it.ref != (C##_value*)_endref_##it; C##rev##next(&it) - -#define c_each_kv(...) c_MACRO_OVERLOAD(c_each_kv, __VA_ARGS__) -#define c_each_kv_4(key, val, C, cnt) /* structured binding for maps */ \ - _c_each_kv(key, val, C, C##_begin(&cnt), NULL) -#define c_each_kv_5(key, val, C, start, end) \ - _c_each_kv(key, val, C, start, (end).ref) - -#define _c_each_kv(key, val, C, start, endref) /* private */ \ - const C##_key *key = (const C##_key*)&key; key; ) \ - for (C##_mapped *val; key; key = NULL) \ - for (C##_iter _it_##key = start, *_endref_##key = c_safe_cast(C##_iter*, C##_value*, endref); \ - _it_##key.ref != (C##_value*)_endref_##key && (key = &_it_##key.ref->first, val = &_it_##key.ref->second); \ - C##_next(&_it_##key) - -#define c_items(it, T, ...) \ - struct {T* ref; int size, index;} \ - it = {.ref=c_make_array(T, __VA_ARGS__), .size=(int)(sizeof((T[])__VA_ARGS__)/sizeof(T))} \ - ; it.index < it.size ; ++it.ref, ++it.index - -// c_range, c_range32: python-like int range iteration -#define c_range_t(...) c_MACRO_OVERLOAD(c_range_t, __VA_ARGS__) -#define c_range_t_3(T, i, stop) c_range_t_4(T, i, 0, stop) -#define c_range_t_4(T, i, start, stop) \ - T i=start, _c_end_##i=stop; i < _c_end_##i; ++i -#define c_range_t_5(T, i, start, stop, step) \ - T i=start, _c_inc_##i=step, _c_end_##i=(stop) - (_c_inc_##i > 0) \ - ; (_c_inc_##i > 0) == (i <= _c_end_##i) ; i += _c_inc_##i - -#define c_range(...) c_MACRO_OVERLOAD(c_range, __VA_ARGS__) -#define c_range_1(stop) c_range_t_4(isize, _c_i1, 0, stop) -#define c_range_2(i, stop) c_range_t_4(isize, i, 0, stop) -#define c_range_3(i, start, stop) c_range_t_4(isize, i, start, stop) -#define c_range_4(i, start, stop, step) c_range_t_5(isize, i, start, stop, step) - -#define c_range32(...) c_MACRO_OVERLOAD(c_range32, __VA_ARGS__) -#define c_range32_2(i, stop) c_range_t_4(int32_t, i, 0, stop) -#define c_range32_3(i, start, stop) c_range_t_4(int32_t, i, start, stop) -#define c_range32_4(i, start, stop, step) c_range_t_5(int32_t, i, start, stop, step) - -// make container from a literal list -#define c_make(C, ...) \ - C##_from_n(c_make_array(C##_raw, __VA_ARGS__), c_sizeof((C##_raw[])__VA_ARGS__)/c_sizeof(C##_raw)) - -// put multiple raw-type elements from a literal list into a container -#define c_put_items(C, cnt, ...) \ - C##_put_n(cnt, c_make_array(C##_raw, __VA_ARGS__), c_sizeof((C##_raw[])__VA_ARGS__)/c_sizeof(C##_raw)) - -// drop multiple containers of same type -#define c_drop(C, ...) \ - do { for (c_items(_c_i2, C*, {__VA_ARGS__})) C##_drop(*_c_i2.ref); } while(0) - -// RAII scopes -#define c_defer(...) \ - for (int _c_i3 = 0; _c_i3++ == 0; __VA_ARGS__) - -#define c_with(...) c_MACRO_OVERLOAD(c_with, __VA_ARGS__) -#define c_with_2(init, deinit) \ - for (int _c_i4 = 0; _c_i4 == 0; ) for (init; _c_i4++ == 0; deinit) -#define c_with_3(init, condition, deinit) \ - for (int _c_i5 = 0; _c_i5 == 0; ) for (init; _c_i5++ == 0 && (condition); deinit) - -// General functions - -STC_INLINE void* c_safe_memcpy(void* dst, const void* src, isize size) - { return dst ? memcpy(dst, src, (size_t)size) : NULL; } - -#if INTPTR_MAX == INT64_MAX - #define FNV_BASIS 0xcbf29ce484222325 - #define FNV_PRIME 0x00000100000001b3 -#else - #define FNV_BASIS 0x811c9dc5 - #define FNV_PRIME 0x01000193 -#endif - -STC_INLINE size_t c_basehash_n(const void* key, isize len) { - const uint8_t* msg = (const uint8_t*)key; - size_t h = FNV_BASIS, block = 0; - - while (len >= c_sizeof h) { - memcpy(&block, msg, sizeof h); - h ^= block; - h *= FNV_PRIME; - msg += c_sizeof h; - len -= c_sizeof h; - } - while (len--) { - h ^= *(msg++); - h *= FNV_PRIME; - } - return h; -} - -STC_INLINE size_t c_hash_n(const void* key, isize len) { - uint64_t b8; uint32_t b4; - switch (len) { - case 8: memcpy(&b8, key, 8); return (size_t)(b8 * 0xc6a4a7935bd1e99d); - case 4: memcpy(&b4, key, 4); return b4 * FNV_BASIS; - default: return c_basehash_n(key, len); - } -} - -STC_INLINE size_t c_hash_str(const char *str) { - const uint8_t* msg = (const uint8_t*)str; - size_t h = FNV_BASIS; - while (*msg) { - h ^= *(msg++); - h *= FNV_PRIME; - } - return h; -} - -#define c_hash_mix(...) /* non-commutative hash combine */ \ - c_hash_mix_n(c_make_array(size_t, {__VA_ARGS__}), c_sizeof((size_t[]){__VA_ARGS__})/c_sizeof(size_t)) - -STC_INLINE size_t c_hash_mix_n(size_t h[], isize n) { - for (isize i = 1; i < n; ++i) h[0] += h[0] ^ h[i]; - return h[0]; -} - -// generic typesafe swap -#define c_swap(xp, yp) do { \ - (void)sizeof((xp) == (yp)); \ - char _tv[sizeof *(xp)]; \ - void *_xp = xp, *_yp = yp; \ - memcpy(_tv, _xp, sizeof _tv); \ - memcpy(_xp, _yp, sizeof _tv); \ - memcpy(_yp, _tv, sizeof _tv); \ -} while (0) - -// get next power of two -STC_INLINE isize c_next_pow2(isize n) { - n--; - n |= n >> 1, n |= n >> 2; - n |= n >> 4, n |= n >> 8; - n |= n >> 16; - #if INTPTR_MAX == INT64_MAX - n |= n >> 32; - #endif - return n + 1; -} - -STC_INLINE char* c_strnstrn(const char *str, isize slen, const char *needle, isize nlen) { - if (nlen == 0) return (char *)str; - if (nlen > slen) return NULL; - slen -= nlen; - do { - if (*str == *needle && !c_memcmp(str, needle, nlen)) - return (char *)str; - ++str; - } while (slen--); - return NULL; -} -#endif // STC_COMMON_H_INCLUDED diff --git a/src/finchlite/codegen/stc/stc/coption.h b/src/finchlite/codegen/stc/stc/coption.h deleted file mode 100644 index 67650645..00000000 --- a/src/finchlite/codegen/stc/stc/coption.h +++ /dev/null @@ -1,180 +0,0 @@ -/* MIT License - * - * Copyright (c) 2025 Tyge Løvset - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -/* -Inspired by https://attractivechaos.wordpress.com/2018/08/31/a-survey-of-argument-parsing-libraries-in-c-c -Fixed major bugs with optional arguments (both long and short). -Added arg->optstr output field, more consistent API. - -coption_get() is similar to GNU's getopt_long(). Each call parses one option and -returns the option name. opt->arg points to the option argument if present. -The function returns -1 when all command-line arguments are parsed. In this case, -opt->ind is the index of the first non-option argument. - -#include -#include - -int main(int argc, char *argv[]) -{ - coption_long longopts[] = { - {"foo", coption_no_argument, 'f'}, - {"bar", coption_required_argument, 'b'}, - {"opt", coption_optional_argument, 'o'}, - {0} - }; - const char* optstr = "xy:z::123"; - printf("program -x -y ARG -z [ARG] -1 -2 -3 --foo --bar ARG --opt [ARG] [ARGUMENTS]\n"); - int c; - coption opt = coption_init(); - while ((c = coption_get(&opt, argc, argv, optstr, longopts)) != -1) { - switch (c) { - case '?': printf("error: unknown option: %s\n", opt.optstr); return 1; - case ':': printf("error: missing argument for %s (%c)\n", opt.optstr, opt.opt); return 2; - default: printf("option: %c [%s]\n", opt.opt, opt.arg ? opt.arg : ""); break; - } - } - printf("\nNon-option arguments:"); - for (int i = opt.ind; i < argc; ++i) - printf(" %s", argv[i]); - putchar('\n'); - return 0; -} -*/ -#ifndef STC_COPTION_H_INCLUDED -#define STC_COPTION_H_INCLUDED - -#include -#include - -typedef enum { - coption_no_argument, - coption_required_argument, - coption_optional_argument -} coption_type; - -typedef struct { - const char *name; - coption_type type; - int val; -} coption_long; - -typedef struct { - int ind; /* equivalent to optind */ - int opt; /* equivalent to optopt */ - const char *optstr; /* points to the option string */ - const char *arg; /* equivalent to optarg */ - int _i, _pos, _nargs; - char _optstr[4]; -} coption; - -static inline coption coption_init(void) { - coption opt = {1, 0, NULL, NULL, 1, 0, 0, {'-', '?', '\0'}}; - return opt; -} - -/* move argv[j] over n elements to the left */ -static void coption_permute_(char *argv[], int j, int n) { - int k; - char *p = argv[j]; - for (k = 0; k < n; ++k) - argv[j - k] = argv[j - k - 1]; - argv[j - k] = p; -} - -/* @param opt output; must be initialized to coption_init() on first call - * @return ASCII val for a short option; longopt.val for a long option; - * -1 if argv[] is fully processed; '?' for an unknown option or - * an ambiguous long option; ':' if an option argument is missing - */ -static int coption_get(coption *opt, int argc, char *argv[], - const char *shortopts, const coption_long *longopts) { - int optc = -1, i0, j, posixly_correct = (shortopts && shortopts[0] == '+'); - if (!posixly_correct) { - while (opt->_i < argc && (argv[opt->_i][0] != '-' || argv[opt->_i][1] == '\0')) - ++opt->_i, ++opt->_nargs; - } - opt->opt = 0, opt->optstr = NULL, opt->arg = NULL, i0 = opt->_i; - if (opt->_i >= argc || argv[opt->_i][0] != '-' || argv[opt->_i][1] == '\0') { - opt->ind = opt->_i - opt->_nargs; - return -1; - } - if (argv[opt->_i][0] == '-' && argv[opt->_i][1] == '-') { /* "--" or a long option */ - if (argv[opt->_i][2] == '\0') { /* a bare "--" */ - coption_permute_(argv, opt->_i, opt->_nargs); - ++opt->_i, opt->ind = opt->_i - opt->_nargs; - return -1; - } - optc = '?', opt->_pos = -1; - if (longopts) { /* parse long options */ - int k, n_exact = 0, n_partial = 0; - const coption_long *o = 0, *o_exact = 0, *o_partial = 0; - for (j = 2; argv[opt->_i][j] != '\0' && argv[opt->_i][j] != '='; ++j) {} /* find the end of the option name */ - for (k = 0; longopts[k].name != 0; ++k) - if (strncmp(&argv[opt->_i][2], longopts[k].name, (size_t)(j - 2)) == 0) { - if (longopts[k].name[j - 2] == 0) ++n_exact, o_exact = &longopts[k]; - else ++n_partial, o_partial = &longopts[k]; - } - opt->optstr = argv[opt->_i]; - if (n_exact > 1 || (n_exact == 0 && n_partial > 1)) return '?'; - o = n_exact == 1? o_exact : n_partial == 1? o_partial : 0; - if (o) { - opt->opt = optc = o->val; - if (o->type != coption_no_argument) { - if (argv[opt->_i][j] == '=') - opt->arg = &argv[opt->_i][j + 1]; - else if (argv[opt->_i][j] == '\0' && opt->_i < argc - 1 && (o->type == coption_required_argument || - argv[opt->_i + 1][0] != '-')) - opt->arg = argv[++opt->_i]; - else if (o->type == coption_required_argument) - optc = ':'; /* missing option argument */ - } - } - } - } else if (shortopts) { /* a short option */ - const char *p; - if (opt->_pos == 0) opt->_pos = 1; - optc = opt->opt = argv[opt->_i][opt->_pos++]; - opt->_optstr[1] = optc, opt->optstr = opt->_optstr; - p = strchr(shortopts, optc); - if (p == 0) { - optc = '?'; /* unknown option */ - } else if (p[1] == ':') { - if (argv[opt->_i][opt->_pos] != '\0') - opt->arg = &argv[opt->_i][opt->_pos]; - else if (opt->_i < argc - 1 && (p[2] != ':' || argv[opt->_i + 1][0] != '-')) - opt->arg = argv[++opt->_i]; - else if (p[2] != ':') - optc = ':'; - opt->_pos = -1; - } - } - if (opt->_pos < 0 || argv[opt->_i][opt->_pos] == 0) { - ++opt->_i, opt->_pos = 0; - if (opt->_nargs > 0) /* permute */ - for (j = i0; j < opt->_i; ++j) - coption_permute_(argv, j, opt->_nargs); - } - opt->ind = opt->_i - opt->_nargs; - return optc; -} - -#endif // STC_COPTION_H_INCLUDED diff --git a/src/finchlite/codegen/stc/stc/coroutine.h b/src/finchlite/codegen/stc/stc/coroutine.h deleted file mode 100644 index b7a7163b..00000000 --- a/src/finchlite/codegen/stc/stc/coroutine.h +++ /dev/null @@ -1,584 +0,0 @@ -/* MIT License - * - * Copyright (c) 2025 Tyge Løvset - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef STC_COROUTINE_H_INCLUDED -#define STC_COROUTINE_H_INCLUDED -/* -#include -#include - -struct iterpair { - cco_base base; // required member - int max_x, max_y; - int x, y; -}; - -int iterpair(struct iterpair* I) { - cco_async (I) { - for (I->x = 0; I->x < I->max_x; I->x++) - for (I->y = 0; I->y < I->max_y; I->y++) - cco_yield; // suspend - } - - puts("done"); - return 0; // CCO_DONE -} - -int main(void) { - struct iterpair it = {.max_x=3, .max_y=3}; - int n = 0; - while (iterpair(&it)) - { - printf("%d %d\n", it.x, it.y); - // example of early stop: - if (++n == 7) cco_stop(&it); // signal to stop/finalize in next - } - return 0; -} -*/ -#include -#include "common.h" - -enum { - CCO_STATE_INIT = 0, - CCO_STATE_DONE = -1, - CCO_STATE_DROP = -2, -}; -enum cco_status { - CCO_DONE = 0, - CCO_YIELD = 1<<12, - CCO_SUSPEND = 1<<13, - CCO_AWAIT = 1<<14, -}; -#define CCO_CANCEL (1U<<30) - -typedef struct { - int spawn_count; - int await_count; -} cco_group; // waitgroup - -#define cco_state_struct(Prefix) \ - struct Prefix##_state { \ - int32_t pos:24; \ - bool drop; \ - struct Prefix##_fiber* fb; \ - cco_group* wg; \ - } - -#define cco_is_initial(co) ((co)->base.state.pos == CCO_STATE_INIT) -#define cco_is_done(co) ((co)->base.state.pos == CCO_STATE_DONE) -#define cco_is_active(co) ((co)->base.state.pos != CCO_STATE_DONE) - -#if defined STC_HAS_TYPEOF && STC_HAS_TYPEOF - #define _cco_state(co) __typeof__((co)->base.state) - #define _cco_validate_task_struct(co) \ - c_static_assert(/* error: co->base not first member in task struct */ \ - sizeof((co)->base) == sizeof(cco_base) || \ - offsetof(__typeof__(*(co)), base) == 0) -#else - #define _cco_state(co) cco_state - #define _cco_validate_task_struct(co) (void)0 -#endif - -#define cco_async(co) \ - if (0) goto _resume_lbl; \ - else for (_cco_state(co)* _state = (_cco_validate_task_struct(co), (_cco_state(co)*) &(co)->base.state) \ - ; _state->pos != CCO_STATE_DONE \ - ; _state->pos = CCO_STATE_DONE, \ - (void)(sizeof((co)->base) > sizeof(cco_base) && _state->wg ? --_state->wg->spawn_count : 0)) \ - _resume_lbl: switch (_state->pos) case CCO_STATE_INIT: // thanks, @liigo! - -#define cco_finalize /* label */ \ - _state->drop = true; /* FALLTHRU */ \ - case CCO_STATE_DROP - -#define cco_drop cco_finalize // [deprecated] -#define cco_cleanup [fix: use cco_finalize:] -#define cco_routine [fix: use cco_async] - -#define cco_stop(co) \ - do { \ - cco_state* _s = (cco_state*)&(co)->base.state; \ - if (!_s->drop) { _s->pos = CCO_STATE_DROP; _s->drop = true; } \ - } while (0) - -#define cco_reset_state(co) \ - do { \ - cco_state* _s = (cco_state*)&(co)->base.state; \ - _s->pos = CCO_STATE_INIT, _s->drop = false; \ - } while (0) - -#define cco_return \ - do { \ - _state->pos = (_state->drop ? CCO_STATE_DONE : CCO_STATE_DROP); \ - _state->drop = true; \ - goto _resume_lbl; \ - } while (0) - -#define cco_exit() \ - do { \ - _state->pos = CCO_STATE_DONE; \ - goto _resume_lbl; \ - } while (0) - -#define cco_yield_v(status) \ - do { \ - _state->pos = __LINE__; return status; \ - case __LINE__:; \ - } while (0) - -#define cco_yield \ - cco_yield_v(CCO_YIELD) - -#define cco_suspend \ - cco_yield_v(CCO_SUSPEND) - -#define cco_await(until) \ - do { \ - _state->pos = __LINE__; /* FALLTHRU */ \ - case __LINE__: if (!(until)) return CCO_AWAIT; \ - } while (0) - -/* cco_await_coroutine(): assumes coroutine returns a status value (int) */ -#define cco_await_coroutine(...) c_MACRO_OVERLOAD(cco_await_coroutine, __VA_ARGS__) -#define cco_await_coroutine_1(corocall) cco_await_coroutine_2(corocall, CCO_DONE) -#define cco_await_coroutine_2(corocall, awaitbits) \ - do { \ - _state->pos = __LINE__; /* FALLTHRU */ \ - case __LINE__: { \ - int _res = corocall; \ - if (_res & ~(awaitbits)) return _res; \ - } \ - } while (0) - -/* cco_run_coroutine(): assumes coroutine returns a status value (int) */ -#define cco_run_coroutine(corocall) \ - while ((1 ? (corocall) : -1) != CCO_DONE) - - -/* - * Tasks and Fibers - */ -struct cco_error { - int32_t code, line; - const char* file; -}; - -#define cco_fiber_struct(Prefix, Env) \ - typedef Env Prefix##_env; \ - struct Prefix##_fiber { \ - struct cco_task* task; \ - Prefix##_env* env; \ - cco_group* wgcurr; \ - struct cco_task* parent_task; \ - struct cco_task_fiber* next; \ - struct cco_task_state recover_state; \ - struct cco_error err; \ - int awaitbits, status; \ - cco_base base; /* is a coroutine object itself */ \ - } - -/* Define a Task struct */ -#define cco_task_struct(...) c_MACRO_OVERLOAD(cco_task_struct, __VA_ARGS__) -#define cco_task_struct_1(Task) \ - cco_task_struct_2(Task, void) - -#define cco_task_struct_2(Task, Env) \ - cco_fiber_struct(Task, Env); \ - cco_state_struct(Task); \ - _cco_task_struct(Task) - -#define _cco_task_struct(Task) \ - struct Task; \ - typedef struct { \ - int (*func)(struct Task*); \ - int awaitbits; \ - struct Task##_state state; \ - struct cco_task* parent_task; \ - } Task##_base; \ - struct Task - -/* Base cco_task type */ -typedef cco_state_struct(cco_task) cco_state; -typedef struct { cco_state state; } cco_base; -cco_fiber_struct(cco_task, void); -_cco_task_struct(cco_task) { cco_task_base base; }; -typedef struct cco_task_fiber cco_fiber; -typedef struct cco_task cco_task; - -#define cco_err() (&_state->fb->err) -#define cco_status() (_state->fb->status + 0) -#define cco_fb(task) ((cco_fiber*)(task)->base.state.fb + 0) -#define cco_env(task) (task)->base.state.fb->env -#define cco_set_env(task, the_env) ((task)->base.state.fb->env = the_env) - -#define cco_cast_task(...) \ - ((void)sizeof((__VA_ARGS__)->base.func(__VA_ARGS__)), (cco_task *)(__VA_ARGS__)) - -/* Return with error and unwind await stack; must be recovered in cco_finalize section */ -#define cco_throw(error_code) \ - do { \ - cco_fiber* _fb = (cco_fiber*)_state->fb; \ - _fb->err.code = error_code; \ - _fb->err.line = __LINE__; \ - _fb->err.file = __FILE__; \ - cco_return; \ - } while (0) - -#define cco_cancel_fiber(a_fiber) \ - do { \ - cco_fiber* _fb1 = a_fiber; \ - _fb1->err.code = CCO_CANCEL; \ - _fb1->err.line = __LINE__; \ - _fb1->err.file = __FILE__; \ - cco_stop(_fb1->task); \ - } while (0) - -/* Cancel job/task and unwind await stack; MAY be stopped (recovered) in cco_finalize section */ -/* Equals cco_throw(CCO_CANCEL) if a_task is in current fiber. */ -#define cco_cancel_task(a_task) \ - do { \ - cco_task* _tsk1 = cco_cast_task(a_task); \ - cco_cancel_fiber(_tsk1->base.state.fb); \ - cco_stop(_tsk1); \ - if (_tsk1 == _state->fb->task) goto _resume_lbl; \ - } while (0) - -#define cco_await_cancel_task(a_task) do { \ - cco_cancel_task(a_task); \ - cco_await_task(a_task); \ -} while (0) - - -#define cco_cancel_group(waitgroup) \ - _cco_cancel_group((cco_fiber*)_state->fb, waitgroup) - -#define cco_cancel_all() \ - for (cco_fiber *_fbi = _state->fb->next; _fbi != (cco_fiber*)_state->fb; _fbi = _fbi->next) \ - cco_cancel_fiber(_fbi) \ - -/* Recover the thrown error; to be used in cco_finalize section upon handling cco_err()->code */ -#define cco_recover \ - do { \ - cco_fiber* _fb = (cco_fiber*)_state->fb; \ - c_assert(_fb->err.code); \ - _fb->task->base.state = _fb->recover_state; \ - _fb->err.code = 0; \ - goto _resume_lbl; \ - } while (0) - -/* Asymmetric coroutine await/call */ -#define cco_await_task(...) c_MACRO_OVERLOAD(cco_await_task, __VA_ARGS__) -#define cco_await_task_1(a_task) cco_await_task_2(a_task, CCO_DONE) -#define cco_await_task_2(a_task, _awaitbits) do { \ - { cco_task* _await_task = cco_cast_task(a_task); \ - (void)sizeof(cco_env(a_task) == _state->fb->env); \ - cco_fiber* _fb = (cco_fiber*)_state->fb; \ - _await_task->base.awaitbits = (_awaitbits); \ - _await_task->base.parent_task = _fb->task; \ - _fb->task = _await_task; \ - _await_task->base.state.fb = _fb; \ - } \ - cco_suspend; \ -} while (0) - -/* Symmetric coroutine flow of control transfer */ -#define cco_yield_to(a_task) do { \ - { cco_task* _to_task = cco_cast_task(a_task); \ - (void)sizeof(cco_env(a_task) == _state->fb->env); \ - cco_fiber* _fb = (cco_fiber*)_state->fb; \ - _to_task->base.awaitbits = _fb->task->base.awaitbits; \ - _to_task->base.parent_task = NULL; \ - _fb->task = _to_task; \ - _to_task->base.state.fb = _fb; \ - } \ - cco_suspend; \ -} while (0) - -#define cco_resume(a_task) \ - _cco_resume_task(cco_cast_task(a_task)) - -static inline int _cco_resume_task(cco_task* task) - { return task->base.func(task); } - -/* - * cco_run_fiber()/cco_run_task(): Run fibers/tasks in parallel - */ -#define cco_new_fiber(...) c_MACRO_OVERLOAD(cco_new_fiber, __VA_ARGS__) -#define cco_new_fiber_1(task) \ - _cco_new_fiber(cco_cast_task(task), NULL, NULL) -#define cco_new_fiber_2(task, env) \ - _cco_new_fiber(cco_cast_task(task), ((void)sizeof((env) == cco_env(task)), env), NULL) - -#define cco_reset_group(wg) ((wg)->spawn_count = 0) -#define cco_spawn(...) c_MACRO_OVERLOAD(cco_spawn, __VA_ARGS__) -#define cco_spawn_1(task) cco_spawn_4(task, NULL, NULL, _state->fb) -#define cco_spawn_2(task, wg) cco_spawn_4(task, wg, NULL, _state->fb) -#define cco_spawn_3(task, wg, env) cco_spawn_4(task, wg, env, _state->fb) -#define cco_spawn_4(task, wg, env, fiber) \ - _cco_spawn(cco_cast_task(task), wg, ((void)sizeof((env) == cco_env(task)), env), \ - (cco_fiber*)((void)sizeof((fiber)->parent_task), fiber)) - -#define cco_await_group(waitgroup) do { \ - cco_task* top = _state->fb->task; \ - while (top->base.parent_task) \ - top = top->base.parent_task; \ - _state->fb->wgcurr = waitgroup; \ - /* wait until spawn_count = 1 to not wait for itself to finish, else until it is 0 */ \ - _state->fb->wgcurr->await_count = (top->base.state.wg == _state->fb->wgcurr); \ - cco_await(_state->fb->wgcurr->spawn_count == _state->fb->wgcurr->await_count); \ -} while (0) - -#define cco_await_n(waitgroup, n) do { \ - _state->fb->wgcurr = waitgroup; \ - _state->fb->wgcurr->await_count = _state->fb->wgcurr->spawn_count - (n); \ - cco_await(_state->fb->wgcurr->spawn_count == _state->fb->wgcurr->await_count); \ -} while (0) - -#define cco_await_cancel_group(waitgroup) do { \ - cco_group* wg = waitgroup; \ - cco_cancel_group(wg); \ - cco_await_group(wg); \ -} while (0) - -#define cco_await_any(waitgroup) do { \ - cco_await_n(waitgroup, 1); \ - cco_cancel_group(_state->fb->wgcurr); \ - cco_await_group(_state->fb->wgcurr); \ -} while (0) - -#define cco_run_fiber(...) c_MACRO_OVERLOAD(cco_run_fiber, __VA_ARGS__) -#define cco_run_fiber_1(fiber_ref) \ - for (cco_fiber** _it_ref = (cco_fiber**)((void)sizeof((fiber_ref)[0]->env), fiber_ref) \ - ; (*_it_ref = cco_execute_next(*_it_ref)) != NULL; ) -#define cco_run_fiber_2(it, fiber) \ - for (cco_fiber* it = (cco_fiber*)((void)sizeof((fiber)->env), fiber) \ - ; (it = cco_execute_next(it)) != NULL; ) - -#define cco_run_task(...) c_MACRO_OVERLOAD(cco_run_task, __VA_ARGS__) -#define cco_run_task_1(task) cco_run_fiber_2(_it_fb, cco_new_fiber_1(task)) -#define cco_run_task_2(task, env) cco_run_fiber_2(_it_fb, cco_new_fiber_2(task, env)) -#define cco_run_task_3(it, task, env) cco_run_fiber_2(it, cco_new_fiber_2(task, env)) - -#define cco_joined() \ - ((cco_fiber*)_state->fb == _state->fb->next) - -extern int cco_execute(cco_fiber* fb); // is a coroutine itself -extern cco_fiber* cco_execute_next(cco_fiber* fb); // resume and return the next fiber - -extern cco_fiber* _cco_new_fiber(cco_task* task, void* env, cco_group* wg); -extern cco_fiber* _cco_spawn(cco_task* task, cco_group* wg, void* env, cco_fiber* fb); -extern void _cco_cancel_group(cco_fiber* fb, cco_group* waitgroup); - -/* - * Iterate containers with already defined iterator (prefer to use in coroutines only): - */ -#define cco_each(existing_it, C, cnt) \ - existing_it = C##_begin(&cnt); (existing_it).ref; C##_next(&existing_it) - -#define cco_each_reverse(existing_it, C, cnt) \ - existing_it = C##_rbegin(&cnt); (existing_it).ref; C##_rnext(&existing_it) - -/* - * Using c_filter with coroutine iterators: - */ -#define cco_flt_take(n) \ - (c_flt_take(n), fltbase.done ? (_it.base.state.pos = CCO_STATE_DROP, _it.base.state.drop = 1) : 1) - -#define cco_flt_takewhile(pred) \ - (c_flt_takewhile(pred), fltbase.done ? (_it.base.state.pos = CCO_STATE_DROP, _it.base.state.drop = 1) : 1) - - -/* - * Semaphore - */ - -typedef struct { ptrdiff_t acq_count; } cco_semaphore; - -#define cco_make_semaphore(value) (c_literal(cco_semaphore){value}) -#define cco_set_semaphore(sem, value) ((sem)->acq_count = value) -#define cco_acquire_semaphore(sem) (--(sem)->acq_count) -#define cco_release_semaphore(sem) (++(sem)->acq_count) - -#define cco_await_semaphore(sem) \ - do { \ - cco_await((sem)->acq_count > 0); \ - cco_acquire_semaphore(sem); \ - } while (0) - - -/* - * Timer - */ - -#ifdef _WIN32 - #ifdef __cplusplus - #define _c_LINKC extern "C" __declspec(dllimport) - #else - #define _c_LINKC __declspec(dllimport) - #endif - #ifndef _WINDOWS_ // windows.h - typedef long long LARGE_INTEGER; - _c_LINKC int __stdcall QueryPerformanceCounter(LARGE_INTEGER*); - //_c_LINKC int __stdcall QueryPerformanceFrequency(LARGE_INTEGER*); - #endif - #define cco_timer_freq() 10000000LL /* 1/10th microseconds */ - //static inline long long cco_timer_freq(void) { - // long long quad; - // QueryPerformanceFrequency((LARGE_INTEGER*)&quad); - // return quad; - //} - - static inline long long cco_timer_ticks(void) { - long long quad; - QueryPerformanceCounter((LARGE_INTEGER*)&quad); - return quad; - } -#else - #include - #define cco_timer_freq() 1000000LL - - static inline long long cco_timer_ticks(void) { /* microseconds */ - struct timeval tv; - gettimeofday(&tv, NULL); - return tv.tv_sec*cco_timer_freq() + tv.tv_usec; - } -#endif - -typedef struct { double duration; long long start_time; } cco_timer; - -static inline cco_timer cco_make_timer(double sec) { - cco_timer tm = {.duration=sec, .start_time=cco_timer_ticks()}; - return tm; -} - -static inline void cco_start_timer(cco_timer* tm, double sec) { - tm->duration = sec; - tm->start_time = cco_timer_ticks(); -} - -static inline void cco_restart_timer(cco_timer* tm) { - tm->start_time = cco_timer_ticks(); -} - -static inline double cco_timer_elapsed(cco_timer* tm) { - return (double)(cco_timer_ticks() - tm->start_time)*(1.0/cco_timer_freq()); -} - -static inline bool cco_timer_expired(cco_timer* tm) { - return cco_timer_elapsed(tm) >= tm->duration; -} - -static inline double cco_timer_remaining(cco_timer* tm) { - return tm->duration - cco_timer_elapsed(tm); -} - -#define cco_await_timer(tm, sec) \ - do { \ - cco_start_timer(tm, sec); \ - cco_await(cco_timer_expired(tm)); \ - } while (0) - -#endif // STC_COROUTINE_H_INCLUDED - -/* -------------------------- IMPLEMENTATION ------------------------- */ -#if (defined i_implement || defined STC_IMPLEMENT) && !defined STC_COROUTINE_IMPLEMENT -#define STC_COROUTINE_IMPLEMENT -#include - -cco_fiber* _cco_spawn(cco_task* _task, cco_group* wg, void* env, cco_fiber* fb) { - cco_fiber* new_fb; - new_fb = fb->next = (fb->next ? c_new(cco_fiber, {.next=fb->next}) : fb); - new_fb->task = _task; - new_fb->env = (env ? env : fb->env); - _task->base.state.fb = new_fb; - if (wg) wg->spawn_count += 1; - _task->base.state.wg = wg; - return new_fb; -} - -cco_fiber* _cco_new_fiber(cco_task* _task, void* env, cco_group* wg) { - cco_fiber* new_fb = c_new(cco_fiber, {.task=_task, .env=env}); - _task->base.state.fb = new_fb; - _task->base.state.wg = wg; - return (new_fb->next = new_fb); -} - -void _cco_cancel_group(cco_fiber* fb, cco_group* waitgroup) { - for (cco_fiber *fbi = fb->next; fbi != fb; fbi = fbi->next) { - cco_task* top = fbi->task; - while (top->base.parent_task) - top = top->base.parent_task; - if (top->base.state.wg == waitgroup) - cco_cancel_fiber(fbi); - } -} - -cco_fiber* cco_execute_next(cco_fiber* fb) { - cco_fiber *_next = fb->next, *unlinked; - int ret = cco_execute(_next); - - if (ret == CCO_DONE) { - unlinked = _next; - _next = (_next == fb ? NULL : _next->next); - fb->next = _next; - c_free_n(unlinked, 1); - } - return _next; -} - -int cco_execute(cco_fiber* fb) { - cco_async (fb) { - while (1) { - fb->parent_task = fb->task->base.parent_task; - fb->awaitbits = fb->task->base.awaitbits; - fb->status = cco_resume(fb->task); - if (fb->err.code) { - // Note: if fb->status == CCO_DONE, fb->task may already be destructed. - if (fb->status == CCO_DONE) { - fb->task = fb->parent_task; - if (fb->task == NULL) - break; - fb->recover_state = fb->task->base.state; - } - cco_stop(fb->task); - cco_suspend; - continue; - } - if (!((fb->status & ~fb->awaitbits) || (fb->task = fb->parent_task) != NULL)) - break; - cco_suspend; - } - } - - if ((uint32_t)fb->err.code & ~CCO_CANCEL) { // Allow CCO_CANCEL not to trigger error. - fprintf(stderr, __FILE__ ": error: unhandled coroutine error '%d'\n" - "%s:%d: cco_throw(%d);\n", - fb->err.code, fb->err.file, fb->err.line, fb->err.code); - exit(fb->err.code); - } - return CCO_DONE; -} -#endif // IMPLEMENT -#undef i_implement -#undef i_static -#undef i_header diff --git a/src/finchlite/codegen/stc/stc/cregex.h b/src/finchlite/codegen/stc/stc/cregex.h deleted file mode 100644 index 0a1ab03f..00000000 --- a/src/finchlite/codegen/stc/stc/cregex.h +++ /dev/null @@ -1,168 +0,0 @@ -/* -This is a Unix port of the Plan 9 regular expression library, by Rob Pike. - -Copyright © 2021 Plan 9 Foundation -Copyright © 2022 Tyge Løvset, for additions made in 2022. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ -#ifndef STC_CREGEX_H_INCLUDED -#define STC_CREGEX_H_INCLUDED -/* - * cregex.h - * - * This is a extended version of regexp9, supporting UTF8 input, common - * shorthand character classes, ++. - */ -#include "common.h" -#include "types.h" // csview, cstr types - -enum { - CREG_DEFAULT = 0, - - /* compile-flags */ - CREG_DOTALL = 1<<0, /* dot matches newline too */ - CREG_ICASE = 1<<1, /* ignore case */ - - /* match-flags */ - CREG_FULLMATCH = 1<<2, /* like start-, end-of-line anchors were in pattern: "^ ... $" */ - CREG_NEXT = 1<<3, /* use end of previous match[0] as start of input */ - - /* replace-flags */ - CREG_STRIP = 1<<5, /* only keep the matched strings, strip rest */ - - /* limits */ - CREG_MAX_CLASSES = 16, - CREG_MAX_CAPTURES = 32, -}; - -typedef enum { - CREG_OK = 0, - CREG_NOMATCH = -1, - CREG_MATCHERROR = -2, - CREG_OUTOFMEMORY = -3, - CREG_UNMATCHEDLEFTPARENTHESIS = -4, - CREG_UNMATCHEDRIGHTPARENTHESIS = -5, - CREG_TOOMANYSUBEXPRESSIONS = -6, - CREG_TOOMANYCHARACTERCLASSES = -7, - CREG_MALFORMEDCHARACTERCLASS = -8, - CREG_MISSINGOPERAND = -9, - CREG_UNKNOWNOPERATOR = -10, - CREG_OPERANDSTACKOVERFLOW = -11, - CREG_OPERATORSTACKOVERFLOW = -12, - CREG_OPERATORSTACKUNDERFLOW = -13, -} cregex_result; - -typedef struct { - struct _Reprog* prog; - int error; -} cregex; - -typedef struct { - const cregex* regex; - csview input; - csview match[CREG_MAX_CAPTURES]; -} cregex_iter; - -#define c_match(it, re, str) \ - cregex_iter it = {.regex=re, .input={str}, .match={{0}}}; \ - cregex_match(it.regex, it.input.buf, it.match, CREG_NEXT) == CREG_OK && it.match[0].size; - -#define c_match_sv(it, re, strview) \ - cregex_iter it = {.regex=re, .input=strview, .match={{0}}}; \ - cregex_match_sv(it.regex, it.input, it.match, CREG_NEXT) == CREG_OK && it.match[0].size; - -/* compile a regex from a pattern. return CREG_OK, or negative error code on failure. */ -extern int cregex_compile_pro(cregex *re, const char* pattern, int cflags); - -#define cregex_compile(...) \ - c_ARG_4(__VA_ARGS__, cregex_compile_pro(__VA_ARGS__), cregex_compile_pro(__VA_ARGS__, CREG_DEFAULT), _too_few_args_) - -/* construct and return a regex from a pattern. return CREG_OK, or negative error code on failure. */ -STC_INLINE cregex cregex_make(const char* pattern, int cflags) { - cregex re = {0}; - cregex_compile_pro(&re, pattern, cflags); - return re; -} -STC_INLINE cregex cregex_from(const char* pattern) - { return cregex_make(pattern, CREG_DEFAULT); } - -/* destroy regex */ -extern void cregex_drop(cregex* re); - -/* number of capture groups in a regex pattern, excluding the full match capture (0) */ -extern int cregex_captures(const cregex* re); - -/* ----- Private ----- */ - -struct cregex_match_opt { csview* match; int flags; int _dummy; }; -struct cregex_replace_opt { int count; bool(*xform)(int group, csview match, cstr* out); int flags; int _dummy; }; - -extern int cregex_match_opt(const cregex* re, const char* input, const char* input_end, struct cregex_match_opt opt); -extern int cregex_match_aio_opt(const char* pattern, const char* input, const char* input_end, struct cregex_match_opt opt); -extern cstr cregex_replace_opt(const cregex* re, const char* input, const char* input_end, const char* replace, struct cregex_replace_opt opt); -extern cstr cregex_replace_aio_opt(const char* pattern, const char* input, const char* input_end, const char* replace, struct cregex_replace_opt opt); - -static inline int cregex_match_sv_opt(const cregex* re, csview sv, struct cregex_match_opt opt) - { return cregex_match_opt(re, sv.buf, sv.buf+sv.size, opt); } -static inline int cregex_match_aio_sv_opt(const char* pattern, csview sv, struct cregex_match_opt opt) - { return cregex_match_aio_opt(pattern, sv.buf, sv.buf+sv.size, opt); } -static inline cstr cregex_replace_sv_opt(const cregex* re, csview sv, const char* replace, struct cregex_replace_opt opt) - { return cregex_replace_opt(re, sv.buf, sv.buf+sv.size, replace, opt); } -static inline cstr cregex_replace_aio_sv_opt(const char* pattern, csview sv, const char* replace, struct cregex_replace_opt opt) - { return cregex_replace_aio_opt(pattern, sv.buf, sv.buf+sv.size, replace, opt); } - -/* match: return CREG_OK, CREG_NOMATCH or CREG_MATCHERROR. */ -#define _cregex_match(re, str, ...) cregex_match_opt(re, str, NULL, (struct cregex_match_opt){__VA_ARGS__}) -#define _cregex_match_sv(re, sv, ...) cregex_match_sv_opt(re, sv, (struct cregex_match_opt){__VA_ARGS__}) -/* all-in-one: compile RE pattern + match + free */ -#define _cregex_match_aio(pattern, str, ...) cregex_match_aio_opt(pattern, str, NULL, (struct cregex_match_opt){__VA_ARGS__}) -#define _cregex_match_aio_sv(pattern, sv, ...) cregex_match_aio_sv_opt(pattern, sv, (struct cregex_match_opt){__VA_ARGS__}) - -/* replace input with a string using regular expression */ -#define _cregex_replace(re, str, replace, ...) cregex_replace_opt(re, str, NULL, replace, (struct cregex_replace_opt){__VA_ARGS__}) -#define _cregex_replace_sv(re, sv, replace, ...) cregex_replace_sv_opt(re, sv, replace, (struct cregex_replace_opt){__VA_ARGS__}) -/* all-in-one: compile RE string pattern + match + replace + free */ -#define _cregex_replace_aio(pattern, str, replace, ...) cregex_replace_aio_opt(pattern, str, NULL, replace, (struct cregex_replace_opt){__VA_ARGS__}) -#define _cregex_replace_aio_sv(pattern, sv, replace, ...) cregex_replace_aio_sv_opt(pattern, sv, replace, (struct cregex_replace_opt){__VA_ARGS__}) - -/* ----- API functions ---- */ - -#define cregex_match(...) _cregex_match(__VA_ARGS__, ._dummy=0) -#define cregex_match_sv(...) _cregex_match_sv(__VA_ARGS__, ._dummy=0) -#define cregex_match_aio(...) _cregex_match_aio(__VA_ARGS__, ._dummy=0) -#define cregex_match_aio_sv(...) _cregex_match_aio_sv(__VA_ARGS__, ._dummy=0) -#define cregex_is_match(re, str) (_cregex_match(re, str, 0) == CREG_OK) - -#define cregex_replace(...) _cregex_replace(__VA_ARGS__, ._dummy=0) -#define cregex_replace_sv(...) _cregex_replace_sv(__VA_ARGS__, ._dummy=0) -#define cregex_replace_aio(...) _cregex_replace_aio(__VA_ARGS__, ._dummy=0) -#define cregex_replace_aio_sv(...) _cregex_replace_aio_sv(__VA_ARGS__, ._dummy=0) - -#endif // STC_CREGEX_H_INCLUDED - -#if defined STC_IMPLEMENT || defined i_implement || defined i_import - #include "priv/linkage.h" - #include "priv/cregex_prv.c" - #if defined i_import - #include "priv/utf8_prv.c" - #include "priv/cstr_prv.c" - #endif - #include "priv/linkage2.h" -#endif diff --git a/src/finchlite/codegen/stc/stc/cspan.h b/src/finchlite/codegen/stc/stc/cspan.h deleted file mode 100644 index 6f5f16c0..00000000 --- a/src/finchlite/codegen/stc/stc/cspan.h +++ /dev/null @@ -1,480 +0,0 @@ -/* - MIT License - * - * Copyright (c) 2025 Tyge Løvset - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -/* -#include -#include -#include -use_cspan(Span2f, float, 2); -use_cspan(Intspan, int); - -int demo1() { - float raw[4*5]; - Span2f ms = cspan_md(raw, 4, 5); - - for (int i=0; ishape[0]==0 ? NULL : self->data, ._s=self}; \ - } \ - STC_INLINE Self##_iter Self##_end(const Self* self) { \ - (void)self; \ - return c_literal(Self##_iter){0}; \ - } \ - STC_INLINE void Self##_next(Self##_iter* it) { \ - isize off = it->_s->stride.d[RANK - 1]; \ - bool done = _cspan_next##RANK(it->pos, it->_s->shape, it->_s->stride.d, RANK, &off); \ - if (done) it->ref = NULL; else it->ref += off; \ - } \ - STC_INLINE isize Self##_size(const Self* self) \ - { return cspan_size(self); } \ - STC_INLINE Self Self##_transposed(Self sp) \ - { _cspan_transpose(sp.shape, sp.stride.d, cspan_rank(&sp)); return sp; } \ - STC_INLINE Self Self##_swapped_axes(Self sp, int ax1, int ax2) \ - { _cspan_swap_axes(sp.shape, sp.stride.d, cspan_rank(&sp), ax1, ax2); return sp; } \ - struct stc_nostruct - -#define use_cspan_with_eq_4(Self, T, i_eq, RANK) \ - use_cspan_3(Self, T, RANK); \ - STC_INLINE bool Self##_eq(const Self* x, const Self* y) { \ - if (memcmp(x->shape, y->shape, sizeof x->shape) != 0) \ - return false; \ - for (Self##_iter _i = Self##_begin(x), _j = Self##_begin(y); \ - _i.ref != NULL; Self##_next(&_i), Self##_next(&_j)) \ - { if (!(i_eq(_i.ref, _j.ref))) return false; } \ - return true; \ - } \ - STC_INLINE bool Self##_equals(Self sp1, Self sp2) \ - { return Self##_eq(&sp1, &sp2); } \ - struct stc_nostruct - -#define use_cspan2(Self, T) use_cspan_2(Self, T); use_cspan_3(Self##2, T, 2) -#define use_cspan3(Self, T) use_cspan2(Self, T); use_cspan_3(Self##3, T, 3) -#define use_cspan2_with_eq(Self, T, eq) use_cspan_with_eq_3(Self, T, eq); \ - use_cspan_with_eq_4(Self##2, T, eq, 2) -#define use_cspan3_with_eq(Self, T, eq) use_cspan2_with_eq(Self, T, eq); \ - use_cspan_with_eq_4(Self##3, T, eq, 3) -#define use_cspan_tuple(N) typedef struct { _istride d[N]; } cspan_tuple##N -use_cspan_tuple(1); use_cspan_tuple(2); -use_cspan_tuple(3); use_cspan_tuple(4); -use_cspan_tuple(5); use_cspan_tuple(6); -use_cspan_tuple(7); use_cspan_tuple(8); - - -// Construct a cspan from a pointer+size -#define cspan_from_n(dataptr, n) \ - {.data=dataptr, \ - .shape={(_istride)(n)}, \ - .stride=c_literal(cspan_tuple1){.d={1}}} - -// Create a 1d-span in the local lexical scope. N must be a compile-time constant. -#define cspan_by_copy(dataptr, N) \ - cspan_from_n(memcpy((char[(N)*sizeof *(dataptr)]){0}, dataptr, (N)*sizeof *(dataptr)), N) - -// Create a zeroed out 1d-span in the local lexical scope. N must be a compile-time constant. -#define cspan_zeros(Span, N) \ - ((Span)cspan_from_n((Span##_value[N]){0}, N)) - -// Create a global scope 1d-span from constant initializer list, otherwise like c_make(Span, ...). -#define cspan_make(Span, ...) \ - ((Span)cspan_from_n(c_make_array(Span##_value, __VA_ARGS__), \ - sizeof((Span##_value[])__VA_ARGS__)/sizeof(Span##_value))) - -// Make 1d-span from a c-array. -#define cspan_from_array(array) \ - cspan_from_n(array, c_arraylen(array)) - -// Make 1d-span from a vec or stack container. -#define cspan_from_vec(container) \ - cspan_from_n((container)->data, (container)->size) - -// Make a 1d-sub-span from a 1d-span -#define cspan_subspan(self, offset, count) \ - {.data=cspan_at(self, offset), \ - .shape={(_istride)(count)}, \ - .stride=(self)->stride} - -// Accessors -// -#define cspan_size(self) _cspan_size((self)->shape, cspan_rank(self)) -#define cspan_rank(self) c_arraylen((self)->shape) // constexpr -#define cspan_at(self, ...) ((self)->data + cspan_index(self, __VA_ARGS__)) -#define cspan_front(self) ((self)->data) -#define cspan_back(self) ((self)->data + cspan_size(self) - 1) - -#define cspan_index(...) cspan_index_fn(__VA_ARGS__, c_COMMA_N(cspan_index_3d), c_COMMA_N(cspan_index_2d), \ - c_COMMA_N(cspan_index_1d),)(__VA_ARGS__) -#define cspan_index_fn(self, i,j,k,n, ...) c_TUPLE_AT_1(n, cspan_index_nd,) -#define cspan_index_1d(self, i) (c_static_assert(cspan_rank(self) == 1), \ - c_assert((i) < (self)->shape[0]), \ - (i)*(self)->stride.d[0]) -#define cspan_index_2d(self, i,j) (c_static_assert(cspan_rank(self) == 2), \ - c_assert((i) < (self)->shape[0] && (j) < (self)->shape[1]), \ - (i)*(self)->stride.d[0] + (j)*(self)->stride.d[1]) -#define cspan_index_3d(self, i,j,k) (c_static_assert(cspan_rank(self) == 3), \ - c_assert((i) < (self)->shape[0] && (j) < (self)->shape[1] && (k) < (self)->shape[2]), \ - (i)*(self)->stride.d[0] + (j)*(self)->stride.d[1] + (k)*(self)->stride.d[2]) -#define cspan_index_nd(self, ...) _cspan_index((self)->shape, (self)->stride.d, c_make_array(isize, {__VA_ARGS__}), \ - (c_static_assert(cspan_rank(self) == c_NUMARGS(__VA_ARGS__)), cspan_rank(self))) - - -// Multi-dimensional span constructors -// -typedef enum {c_ROWMAJOR, c_COLMAJOR, c_STRIDED} cspan_layout; - -#define cspan_is_colmajor(self) \ - _cspan_is_layout(c_COLMAJOR, (self)->shape, (self)->stride.d, cspan_rank(self)) -#define cspan_is_rowmajor(self) \ - _cspan_is_layout(c_ROWMAJOR, (self)->shape, (self)->stride.d, cspan_rank(self)) -#define cspan_get_layout(self) \ - (cspan_is_rowmajor(self) ? c_ROWMAJOR : cspan_is_colmajor(self) ? c_COLMAJOR : c_STRIDED) - -#define cspan_md(dataptr, ...) \ - cspan_md_layout(c_ROWMAJOR, dataptr, __VA_ARGS__) - -// Span2 sp1 = cspan_md(data, 30, 50); -// Span2 sp2 = {data, cspan_shape(15, 25), cspan_strides(50*2, 2)}; // every second in each dim -#define cspan_shape(...) {__VA_ARGS__} -#define cspan_strides(...) {.d={__VA_ARGS__}} - -#define cspan_md_layout(layout, dataptr, ...) \ - {.data=dataptr, \ - .shape={__VA_ARGS__}, \ - .stride=*(c_JOIN(cspan_tuple,c_NUMARGS(__VA_ARGS__))*) \ - _cspan_shape2stride(layout, c_make_array(_istride, {__VA_ARGS__}), c_NUMARGS(__VA_ARGS__))} - -// Transpose matrix -#define cspan_transpose(self) \ - _cspan_transpose((self)->shape, (self)->stride.d, cspan_rank(self)) - -// Swap two matrix axes -#define cspan_swap_axes(self, ax1, ax2) \ - _cspan_swap_axes((self)->shape, (self)->stride.d, cspan_rank(self), ax1, ax2) - -// Set all span elements to value. -#define cspan_set_all(Span, self, value) do { \ - Span##_value _v = value; \ - for (c_each_3(_it, Span, *(self))) *_it.ref = _v; \ -} while (0) - -// General slicing function. -// -#define c_END (_istride)(((size_t)1 << (sizeof(_istride)*8 - 1)) - 1) -#define c_ALL 0,c_END - -#define cspan_slice(self, Outspan, ...) \ - Outspan##_slice_((self)->data, (self)->shape, (self)->stride.d, \ - c_make_array2d(const isize, 3, {__VA_ARGS__}), \ - (c_static_assert(cspan_rank(self) == sizeof((isize[][3]){__VA_ARGS__})/sizeof(isize[3])), cspan_rank(self))) - -// submd#(): Reduces rank, fully typesafe + range checked by default -// int ms3[N1][N2][N3]; -// int (*ms2)[N3] = ms3[1]; // traditional, lose range test/info. VLA. -// Span3 ms3 = cspan_md(data, N1,N2,N3); // Uses cspan_md instead. -// *cspan_at(&ms3, 1,1,1) = 42; -// Span2 ms2 = cspan_slice(&ms3, Span2, {1}, {c_ALL}, {c_ALL}); -// Span2 ms2 = cspan_submd3(&ms3, 1); // Same as line above, optimized. -#define cspan_submd2(self, x) \ - {.data=cspan_at(self, x, 0), \ - .shape={(self)->shape[1]}, \ - .stride=c_literal(cspan_tuple1){.d={(self)->stride.d[1]}}} - -#define cspan_submd3(...) c_MACRO_OVERLOAD(cspan_submd3, __VA_ARGS__) -#define cspan_submd3_2(self, x) \ - {.data=cspan_at(self, x, 0, 0), \ - .shape={(self)->shape[1], (self)->shape[2]}, \ - .stride=c_literal(cspan_tuple2){.d={(self)->stride.d[1], (self)->stride.d[2]}}} -#define cspan_submd3_3(self, x, y) \ - {.data=cspan_at(self, x, y, 0), \ - .shape={(self)->shape[2]}, \ - .stride=c_literal(cspan_tuple1){.d={(self)->stride.d[2]}}} - -#define cspan_submd4(...) c_MACRO_OVERLOAD(cspan_submd4, __VA_ARGS__) -#define cspan_submd4_2(self, x) \ - {.data=cspan_at(self, x, 0, 0, 0), \ - .shape={(self)->shape[1], (self)->shape[2], (self)->shape[3]}, \ - .stride=c_literal(cspan_tuple3){.d={(self)->stride.d[1], (self)->stride.d[2], (self)->stride.d[3]}}} -#define cspan_submd4_3(self, x, y) \ - {.data=cspan_at(self, x, y, 0, 0), \ - .shape={(self)->shape[2], (self)->shape[3]}, \ - .stride=c_literal(cspan_tuple2){.d={(self)->stride.d[2], (self)->stride.d[3]}}} -#define cspan_submd4_4(self, x, y, z) \ - {.data=cspan_at(self, x, y, z, 0), \ - .shape={(self)->shape[3]}, \ - .stride=c_literal(cspan_tuple1){.d={(self)->stride.d[3]}}} - -#define cspan_print(...) c_MACRO_OVERLOAD(cspan_print, __VA_ARGS__) -#define cspan_print_3(Span, fmt, span) \ - cspan_print_4(Span, fmt, span, stdout) -#define cspan_print_4(Span, fmt, span, fp) \ - cspan_print_5(Span, fmt, span, fp, "[]") -#define cspan_print_5(Span, fmt, span, fp, brackets) \ - cspan_print_6(Span, fmt, span, fp, brackets, c_EXPAND) -#define cspan_print_complex(Span, prec, span, fp) \ - cspan_print_6(Span, "%." #prec "f%+." #prec "fi", span, fp, "[]", cspan_CMPLX_FLD) -#define cspan_CMPLX_FLD(x) creal(x), cimag(x) - -#define cspan_print_6(Span, fmt, span, fp, brackets, field) do { \ - const Span _s = span; \ - const char *_f = fmt, *_b = brackets; \ - FILE* _fp = fp; \ - int _w, _max = 0; \ - char _res[2][20], _fld[64]; \ - for (c_each_3(_it, Span, _s)) { \ - _w = snprintf(NULL, 0ULL, _f, field(_it.ref[0])); \ - if (_w > _max) _max = _w; \ - } \ - for (c_each_3(_it, Span, _s)) { \ - _cspan_print_assist(_it.pos, _s.shape, cspan_rank(&_s), _b, _res); \ - _w = _max + (_it.pos[cspan_rank(&_s) - 1] > 0); \ - snprintf(_fld, sizeof _fld, _f, field(_it.ref[0])); \ - fprintf(_fp, "%s%*s%s", _res[0], _w, _fld, _res[1]); \ - } \ -} while (0) - -/* ----- PRIVATE ----- */ - -STC_INLINE isize _cspan_size(const _istride shape[], int rank) { - isize size = shape[0]; - while (--rank) size *= shape[rank]; - return size; -} - -STC_INLINE void _cspan_swap_axes(_istride shape[], _istride stride[], - int rank, int ax1, int ax2) { - (void)rank; - c_assert(c_uless(ax1, rank) & c_uless(ax2, rank)); - c_swap(shape + ax1, shape + ax2); - c_swap(stride + ax1, stride + ax2); -} - -STC_INLINE void _cspan_transpose(_istride shape[], _istride stride[], int rank) { - for (int i = 0; i < --rank; ++i) { - c_swap(shape + i, shape + rank); - c_swap(stride + i, stride + rank); - } -} - -STC_INLINE isize _cspan_index(const _istride shape[], const _istride stride[], - const isize args[], int rank) { - isize off = 0; - (void)shape; - while (rank-- != 0) { - c_assert(args[rank] < shape[rank]); - off += args[rank]*stride[rank]; - } - return off; -} - -STC_API void _cspan_print_assist(_istride pos[], const _istride shape[], const int rank, - const char* brackets, char result[2][20]); - -STC_API bool _cspan_nextN(_istride pos[], const _istride shape[], const _istride stride[], - int rank, isize* off); -#define _cspan_next1(pos, shape, stride, rank, off) (++pos[0] == shape[0]) -#define _cspan_next2(pos, shape, stride, rank, off) (++pos[1] == shape[1] && \ - (pos[1] = 0, *off += stride[0] - (isize)shape[1]*stride[1], ++pos[0] == shape[0])) -#define _cspan_next3(pos, shape, stride, rank, off) (++pos[2] == shape[2] && \ - (pos[2] = 0, *off += stride[1] - (isize)shape[2]*stride[2], ++pos[1] == shape[1]) && \ - (pos[1] = 0, *off += stride[0] - (isize)shape[1]*stride[1], ++pos[0] == shape[0])) -#define _cspan_next4 _cspan_nextN -#define _cspan_next5 _cspan_nextN -#define _cspan_next6 _cspan_nextN -#define _cspan_next7 _cspan_nextN -#define _cspan_next8 _cspan_nextN - -STC_API isize _cspan_slice(_istride oshape[], _istride ostride[], int* orank, - const _istride shape[], const _istride stride[], - const isize args[][3], int rank); -STC_API _istride* _cspan_shape2stride(cspan_layout layout, _istride shape[], int rank); -STC_API bool _cspan_is_layout(cspan_layout layout, const _istride shape[], const _istride strides[], int rank); - -#endif // STC_CSPAN_H_INCLUDED - -/* --------------------- IMPLEMENTATION --------------------- */ -#if defined i_implement && !defined STC_CSPAN_IMPLEMENT -#define STC_CSPAN_IMPLEMENT - -STC_DEF bool _cspan_is_layout(cspan_layout layout, const _istride shape[], const _istride strides[], int rank) { - _istride tmpshape[16]; // 16 = "max" rank - size_t sz = (size_t)rank*sizeof(_istride); - memcpy(tmpshape, shape, sz); - return memcmp(strides, _cspan_shape2stride(layout, tmpshape, rank), sz) == 0; -} - -STC_DEF void _cspan_print_assist(_istride pos[], const _istride shape[], const int rank, - const char* brackets, char result[2][20]) { - int n = 0, j = 0, r = rank - 1; - memset(result, 0, 32); - - // left braces: - while (n <= r && pos[r - n] == 0) - ++n; - if (n) for (; j < rank; ++j) - result[0][j] = j < rank - n ? ' ' : brackets[0]; - - // right braces: - for (j = 0; r >= 0 && pos[r] + 1 == shape[r]; --r, ++j) - result[1][j] = brackets[1]; - - // comma and newlines: - n = (j > 0) + ((j > 1) & (j < rank)); - if (brackets[2] && j < rank) - result[1][j++] = brackets[2]; // comma - while (n--) - result[1][j++] = '\n'; -} - -STC_DEF bool _cspan_nextN(_istride pos[], const _istride shape[], const _istride stride[], - int rank, isize* off) { - ++pos[--rank]; - for (; rank && pos[rank] == shape[rank]; --rank) { - pos[rank] = 0; ++pos[rank - 1]; - *off += stride[rank - 1] - (isize)shape[rank]*stride[rank]; - } - return pos[rank] == shape[rank]; -} - -STC_DEF _istride* _cspan_shape2stride(cspan_layout layout, _istride shpstri[], int rank) { - int i, inc; - if (layout == c_COLMAJOR) i = 0, inc = 1; - else i = rank - 1, inc = -1; - _istride k = 1, s1 = shpstri[i], s2; - - shpstri[i] = 1; - while (--rank) { - i += inc; - s2 = shpstri[i]; - shpstri[i] = (k *= s1); - s1 = s2; - } - return shpstri; -} - -STC_DEF isize _cspan_slice(_istride oshape[], _istride ostride[], int* orank, - const _istride shape[], const _istride stride[], - const isize args[][3], int rank) { - isize end, off = 0; - int i = 0, oi = 0; - - for (; i < rank; ++i) { - off += args[i][0]*stride[i]; - switch (args[i][1]) { - case 0: c_assert(c_uless(args[i][0], shape[i])); continue; - case c_END: end = shape[i]; break; - default: end = args[i][1]; - } - oshape[oi] = (_istride)(end - args[i][0]); - ostride[oi] = stride[i]; - c_assert((oshape[oi] > 0) & !c_uless(shape[i], end)); - if (args[i][2] > 0) { - ostride[oi] *= (_istride)args[i][2]; - oshape[oi] = (oshape[oi] - 1)/(_istride)args[i][2] + 1; - } - ++oi; - } - *orank = oi; - return off; -} -#endif // IMPLEMENT -#include "priv/linkage2.h" diff --git a/src/finchlite/codegen/stc/stc/cstr.h b/src/finchlite/codegen/stc/stc/cstr.h deleted file mode 100644 index d7c3556d..00000000 --- a/src/finchlite/codegen/stc/stc/cstr.h +++ /dev/null @@ -1,51 +0,0 @@ -/* MIT License - * - * Copyright (c) 2025 Tyge Løvset - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/* A string type with short string optimization in C99. - * Stores up to a 22 bytes long string inside a 24 bytes string representation (x64). - */ -#define i_header // external linkage by default. override with i_static. -#include "priv/linkage.h" - -#ifndef STC_CSTR_H_INCLUDED -#define STC_CSTR_H_INCLUDED - -#include "common.h" -#include "types.h" -#include "priv/utf8_prv.h" -#include "priv/cstr_prv.h" - -#endif // STC_CSTR_H_INCLUDED - -#if defined i_implement || \ - defined STC_CSTR_CORE || \ - defined STC_CSTR_IO || \ - defined STC_CSTR_UTF8 - #include "priv/cstr_prv.c" -#endif // i_implement - -#if defined i_import || defined STC_CSTR_UTF8 - #include "priv/utf8_prv.c" -#endif - -#include "priv/linkage2.h" diff --git a/src/finchlite/codegen/stc/stc/csview.h b/src/finchlite/codegen/stc/stc/csview.h deleted file mode 100644 index c9e71df7..00000000 --- a/src/finchlite/codegen/stc/stc/csview.h +++ /dev/null @@ -1,243 +0,0 @@ -/* MIT License - * - * Copyright (c) 2025 Tyge Løvset - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -// csview is a non-zero-terminated string view. - -#ifndef STC_CSVIEW_H_INCLUDED -#define STC_CSVIEW_H_INCLUDED - -#include "common.h" -#include "types.h" -#include "priv/utf8_prv.h" - -#define csview_init() c_sv_1("") -#define csview_drop(p) c_default_drop(p) -#define csview_clone(sv) c_default_clone(sv) - -csview_iter csview_advance(csview_iter it, isize u8pos); -csview csview_subview_pro(csview sv, isize pos, isize n); -csview csview_token(csview sv, const char* sep, isize* pos); -csview csview_u8_subview(csview sv, isize u8pos, isize u8len); -csview csview_u8_tail(csview sv, isize u8len); -csview_iter csview_u8_at(csview sv, isize u8pos); - -STC_INLINE csview csview_from(const char* str) - { return c_literal(csview){str, c_strlen(str)}; } -STC_INLINE csview csview_from_n(const char* str, isize n) - { return c_literal(csview){str, n}; } - -STC_INLINE void csview_clear(csview* self) { *self = csview_init(); } -STC_INLINE isize csview_size(csview sv) { return sv.size; } -STC_INLINE bool csview_is_empty(csview sv) { return sv.size == 0; } - -STC_INLINE bool csview_equals_sv(csview sv1, csview sv2) - { return sv1.size == sv2.size && !c_memcmp(sv1.buf, sv2.buf, sv1.size); } - -STC_INLINE bool csview_equals(csview sv, const char* str) - { return csview_equals_sv(sv, c_sv_2(str, c_strlen(str))); } - -STC_INLINE size_t csview_hash(const csview *self) - { return c_basehash_n(self->buf, self->size); } - -STC_INLINE isize csview_find_sv(csview sv, csview search) { - char* res = c_strnstrn(sv.buf, sv.size, search.buf, search.size); - return res ? (res - sv.buf) : c_NPOS; -} - -STC_INLINE isize csview_find(csview sv, const char* str) - { return csview_find_sv(sv, c_sv_2(str, c_strlen(str))); } - -STC_INLINE bool csview_contains(csview sv, const char* str) - { return csview_find(sv, str) != c_NPOS; } - -STC_INLINE bool csview_starts_with(csview sv, const char* str) { - isize n = c_strlen(str); - return n <= sv.size && !c_memcmp(sv.buf, str, n); -} - -STC_INLINE bool csview_ends_with(csview sv, const char* str) { - isize n = c_strlen(str); - return n <= sv.size && !c_memcmp(sv.buf + sv.size - n, str, n); -} - -STC_INLINE csview csview_subview(csview sv, isize pos, isize len) { - c_assert(((size_t)pos <= (size_t)sv.size) & (len >= 0)); - if (pos + len > sv.size) len = sv.size - pos; - sv.buf += pos, sv.size = len; - return sv; -} - -STC_INLINE csview csview_slice(csview sv, isize p1, isize p2) { - c_assert(((size_t)p1 <= (size_t)p2) & ((size_t)p1 <= (size_t)sv.size)); - if (p2 > sv.size) p2 = sv.size; - sv.buf += p1, sv.size = p2 - p1; - return sv; -} - -STC_INLINE csview csview_trim_start(csview sv) - { while (sv.size && *sv.buf <= ' ') ++sv.buf, --sv.size; return sv; } - -STC_INLINE csview csview_trim_end(csview sv) - { while (sv.size && sv.buf[sv.size - 1] <= ' ') --sv.size; return sv; } - -STC_INLINE csview csview_trim(csview sv) - { return csview_trim_end(csview_trim_start(sv)); } - -STC_INLINE csview csview_tail(csview sv, isize len) - { return csview_subview(sv, sv.size - len, len); } - -/* utf8 iterator */ -STC_INLINE csview_iter csview_begin(const csview* self) { - csview_iter it = {.u8 = {{self->buf, utf8_chr_size(self->buf)}, - self->buf + self->size}}; - return it; -} -STC_INLINE csview_iter csview_end(const csview* self) { - (void)self; csview_iter it = {0}; return it; -} -STC_INLINE void csview_next(csview_iter* it) { - it->ref += it->chr.size; - it->chr.size = utf8_chr_size(it->ref); - if (it->ref == it->u8.end) it->ref = NULL; -} - -/* utf8 */ -STC_INLINE csview csview_u8_from(const char* str, isize u8pos, isize u8len) - { return utf8_subview(str, u8pos, u8len); } - -STC_INLINE isize csview_u8_size(csview sv) - { return utf8_count_n(sv.buf, sv.size); } - -STC_INLINE bool csview_u8_valid(csview sv) // requires linking with utf8 symbols - { return utf8_valid_n(sv.buf, sv.size); } - -#define c_fortoken(...) for (c_token(__VA_ARGS__)) // [deprecated] - -#define c_token_sv(it, separator, sv) \ - struct { csview input, token; const char* sep; isize pos; } \ - it = {.input=sv, .sep=separator} ; \ - it.pos <= it.input.size && (it.token = csview_token(it.input, it.sep, &it.pos)).buf ; - -#define c_token(it, separator, str) \ - c_token_sv(it, separator, csview_from(str)) - -/* ---- Container helper functions ---- */ - -STC_INLINE int csview_cmp(const csview* x, const csview* y) { - isize n = x->size < y->size ? x->size : y->size; - int c = c_memcmp(x->buf, y->buf, n); - return c ? c : c_default_cmp(&x->size, &y->size); -} - -STC_INLINE bool csview_eq(const csview* x, const csview* y) - { return x->size == y->size && !c_memcmp(x->buf, y->buf, x->size); } - -/* ---- case insensitive ---- */ - -STC_INLINE bool csview_iequals_sv(csview sv1, csview sv2) - { return sv1.size == sv2.size && !utf8_icompare(sv1, sv2); } - -STC_INLINE bool csview_iequals(csview sv, const char* str) - { return csview_iequals_sv(sv, c_sv(str, c_strlen(str))); } - -STC_INLINE bool csview_ieq(const csview* x, const csview* y) - { return csview_iequals_sv(*x, *y); } - -STC_INLINE int csview_icmp(const csview* x, const csview* y) - { return utf8_icompare(*x, *y); } - -STC_INLINE bool csview_istarts_with(csview sv, const char* str) { - isize n = c_strlen(str); - return n <= sv.size && !utf8_icompare(sv, c_sv(str, n)); -} - -STC_INLINE bool csview_iends_with(csview sv, const char* str) { - isize n = c_strlen(str); - return n <= sv.size && !utf8_icmp(sv.buf + sv.size - n, str); -} -#endif // STC_CSVIEW_H_INCLUDED - -/* -------------------------- IMPLEMENTATION ------------------------- */ -#if (defined STC_IMPLEMENT || defined i_implement) && !defined STC_CSVIEW_IMPLEMENT -#define STC_CSVIEW_IMPLEMENT - -csview_iter csview_advance(csview_iter it, isize u8pos) { - int inc = 1; - if (u8pos < 0) u8pos = -u8pos, inc = -1; - while (u8pos && it.ref != it.u8.end) - u8pos -= (*(it.ref += inc) & 0xC0) != 0x80; - if (it.ref == it.u8.end) it.ref = NULL; - else it.chr.size = utf8_chr_size(it.ref); - return it; -} - -csview csview_subview_pro(csview sv, isize pos, isize len) { - if (pos < 0) { - pos += sv.size; - if (pos < 0) pos = 0; - } - if (pos > sv.size) pos = sv.size; - if (pos + len > sv.size) len = sv.size - pos; - sv.buf += pos, sv.size = len; - return sv; -} - -csview csview_token(csview sv, const char* sep, isize* pos) { - isize sep_size = c_strlen(sep); - csview slice = {sv.buf + *pos, sv.size - *pos}; - const char* res = c_strnstrn(slice.buf, slice.size, sep, sep_size); - csview tok = {slice.buf, res ? (res - slice.buf) : slice.size}; - *pos += tok.size + sep_size; - return tok; -} - -csview csview_u8_subview(csview sv, isize u8pos, isize u8len) { - const char* s, *end = &sv.buf[sv.size]; - while ((u8pos > 0) & (sv.buf != end)) - u8pos -= (*++sv.buf & 0xC0) != 0x80; - s = sv.buf; - while ((u8len > 0) & (s != end)) - u8len -= (*++s & 0xC0) != 0x80; - sv.size = s - sv.buf; return sv; -} - -csview csview_u8_tail(csview sv, isize u8len) { - const char* p = &sv.buf[sv.size]; - while (u8len && p != sv.buf) - u8len -= (*--p & 0xC0) != 0x80; - sv.size -= p - sv.buf, sv.buf = p; - return sv; -} - -csview_iter csview_u8_at(csview sv, isize u8pos) { - const char *end = &sv.buf[sv.size]; - while ((u8pos > 0) & (sv.buf != end)) - u8pos -= (*++sv.buf & 0xC0) != 0x80; - sv.size = utf8_chr_size(sv.buf); - c_assert(sv.buf != end); - return c_literal(csview_iter){.u8 = {sv, end}}; -} -#endif // IMPLEMENT - -#if defined i_import -#include "priv/utf8_prv.c" -#endif diff --git a/src/finchlite/codegen/stc/stc/ctest.h b/src/finchlite/codegen/stc/stc/ctest.h deleted file mode 100644 index 8ffbdf7a..00000000 --- a/src/finchlite/codegen/stc/stc/ctest.h +++ /dev/null @@ -1,639 +0,0 @@ -/* Copyright 2011-2023 Bas van den Berg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef CTEST_H -#define CTEST_H - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef __GNUC__ -#define CTEST_IMPL_FORMAT_PRINTF(a, b) __attribute__ ((format(printf, a, b))) -#else -#define CTEST_IMPL_FORMAT_PRINTF(a, b) -#endif - -#include /* intmax_t, uintmax_t, PRI* */ -#include /* bool, true, false */ -#include /* size_t */ - -typedef void (*ctest_nullary_run_func)(void); -typedef void (*ctest_unary_run_func)(void*); -typedef void (*ctest_setup_func)(void*); -typedef void (*ctest_teardown_func)(void*); - -union ctest_run_func_union { - ctest_nullary_run_func nullary; - ctest_unary_run_func unary; -}; - -#define CTEST_IMPL_PRAGMA(x) _Pragma (#x) -#define CTEST_CONTAINER_OF(p, T, m) \ - ((T*)((char*)(p) + 0*sizeof((p) == &((T*)0)->m) - offsetof(T, m))) - -#if defined(__GNUC__) -#if defined(__clang__) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) -/* the GCC argument will work for both gcc and clang */ -#define CTEST_IMPL_DIAG_PUSH_IGNORED(w) \ - CTEST_IMPL_PRAGMA(GCC diagnostic push) \ - CTEST_IMPL_PRAGMA(GCC diagnostic ignored "-W" #w) -#define CTEST_IMPL_DIAG_POP() \ - CTEST_IMPL_PRAGMA(GCC diagnostic pop) -#else -/* the push/pop functionality wasn't in gcc until 4.6, fallback to "ignored" */ -#define CTEST_IMPL_DIAG_PUSH_IGNORED(w) \ - CTEST_IMPL_PRAGMA(GCC diagnostic ignored "-W" #w) -#define CTEST_IMPL_DIAG_POP() -#endif -#else -/* leave them out entirely for non-GNUC compilers */ -#define CTEST_IMPL_DIAG_PUSH_IGNORED(w) -#define CTEST_IMPL_DIAG_POP() -#endif - -struct ctest { - uint32_t magic0, padding; - - const char* ssname; // suite name - const char* ttname; // test name - union ctest_run_func_union run; - - void* data; - ctest_setup_func* setup; - ctest_teardown_func* teardown; - - int32_t skip; - uint32_t magic1; -}; - -#define CTEST_IMPL_NAME(name) ctest_##name -#define CTEST_IMPL_FNAME(sname, tname) CTEST_IMPL_NAME(sname##_##tname##_run) -#define CTEST_IMPL_TNAME(sname, tname) CTEST_IMPL_NAME(sname##_##tname) -#define CTEST_IMPL_DATA_SNAME(sname) CTEST_IMPL_NAME(sname##_data) -#define CTEST_IMPL_DATA_TNAME(sname, tname) CTEST_IMPL_NAME(sname##_##tname##_data) -#define CTEST_IMPL_SETUP_FNAME(sname) CTEST_IMPL_NAME(sname##_setup) -#define CTEST_IMPL_SETUP_FPNAME(sname) CTEST_IMPL_NAME(sname##_setup_ptr) -#define CTEST_IMPL_SETUP_TPNAME(sname, tname) CTEST_IMPL_NAME(sname##_##tname##_setup_ptr) -#define CTEST_IMPL_TEARDOWN_FNAME(sname) CTEST_IMPL_NAME(sname##_teardown) -#define CTEST_IMPL_TEARDOWN_FPNAME(sname) CTEST_IMPL_NAME(sname##_teardown_ptr) -#define CTEST_IMPL_TEARDOWN_TPNAME(sname, tname) CTEST_IMPL_NAME(sname##_##tname##_teardown_ptr) - -#ifdef __APPLE__ -#ifdef __arm64__ -#define CTEST_IMPL_SECTION __attribute__ ((used, section ("__DATA, .ctest"), aligned(8))) -#else -#define CTEST_IMPL_SECTION __attribute__ ((used, section ("__DATA, .ctest"), aligned(1))) -#endif -#elif !defined _MSC_VER -#define CTEST_IMPL_SECTION __attribute__ ((used, section (".ctest"), aligned(1))) -#else -#pragma section(".ctest", read) -#define CTEST_IMPL_SECTION __declspec(allocate(".ctest")) __declspec(align(1)) -#endif - -#define CTEST_IMPL_STRUCT(sname, tname, tskip, tdata, tsetup, tteardown) \ - static CTEST_IMPL_SECTION struct ctest CTEST_IMPL_TNAME(sname, tname) = { \ - 0xBADCAFE0, 0, \ - #sname, \ - #tname, \ - { (ctest_nullary_run_func) CTEST_IMPL_FNAME(sname, tname) }, \ - tdata, \ - (ctest_setup_func*) tsetup, \ - (ctest_teardown_func*) tteardown, \ - tskip, \ - 0xBADCAFE1, \ - } - -#ifdef __cplusplus - -#define CTEST_SETUP(sname, fixt) \ - template <> void CTEST_IMPL_SETUP_FNAME(sname)(struct CTEST_IMPL_DATA_SNAME(sname)* fixt) - -#define CTEST_TEARDOWN(sname, fixt) \ - template <> void CTEST_IMPL_TEARDOWN_FNAME(sname)(struct CTEST_IMPL_DATA_SNAME(sname)* fixt) - -#define CTEST_FIXTURE(sname) \ - template void CTEST_IMPL_SETUP_FNAME(sname)(T* self) { } \ - template void CTEST_IMPL_TEARDOWN_FNAME(sname)(T* self) { } \ - struct CTEST_IMPL_DATA_SNAME(sname) - -#define CTEST_IMPL_CTEST(sname, tname, tskip) \ - static void CTEST_IMPL_FNAME(sname, tname)(void); \ - CTEST_IMPL_STRUCT(sname, tname, tskip, NULL, NULL, NULL); \ - static void CTEST_IMPL_FNAME(sname, tname)(void) - -#define CTEST_IMPL_CTEST_F(sname, tname, tskip, fixt) \ - static struct CTEST_IMPL_DATA_SNAME(sname) CTEST_IMPL_DATA_TNAME(sname, tname); \ - static void CTEST_IMPL_FNAME(sname, tname)(struct CTEST_IMPL_DATA_SNAME(sname)* fixt); \ - static void (*CTEST_IMPL_SETUP_TPNAME(sname, tname))(struct CTEST_IMPL_DATA_SNAME(sname)*) = &CTEST_IMPL_SETUP_FNAME(sname); \ - static void (*CTEST_IMPL_TEARDOWN_TPNAME(sname, tname))(struct CTEST_IMPL_DATA_SNAME(sname)*) = &CTEST_IMPL_TEARDOWN_FNAME(sname); \ - CTEST_IMPL_STRUCT(sname, tname, tskip, &CTEST_IMPL_DATA_TNAME(sname, tname), &CTEST_IMPL_SETUP_TPNAME(sname, tname), &CTEST_IMPL_TEARDOWN_TPNAME(sname, tname)); \ - static void CTEST_IMPL_FNAME(sname, tname)(struct CTEST_IMPL_DATA_SNAME(sname)* fixt) - -#else - -#define CTEST_SETUP(sname, fixt) \ - static void CTEST_IMPL_SETUP_FNAME(sname)(struct CTEST_IMPL_DATA_SNAME(sname)* fixt); \ - static void (*CTEST_IMPL_SETUP_FPNAME(sname))(struct CTEST_IMPL_DATA_SNAME(sname)*) = &CTEST_IMPL_SETUP_FNAME(sname); \ - static void CTEST_IMPL_SETUP_FNAME(sname)(struct CTEST_IMPL_DATA_SNAME(sname)* fixt) - -#define CTEST_TEARDOWN(sname, fixt) \ - static void CTEST_IMPL_TEARDOWN_FNAME(sname)(struct CTEST_IMPL_DATA_SNAME(sname)* fixt); \ - static void (*CTEST_IMPL_TEARDOWN_FPNAME(sname))(struct CTEST_IMPL_DATA_SNAME(sname)*) = &CTEST_IMPL_TEARDOWN_FNAME(sname); \ - static void CTEST_IMPL_TEARDOWN_FNAME(sname)(struct CTEST_IMPL_DATA_SNAME(sname)* fixt) - -#define CTEST_FIXTURE(sname) \ - struct CTEST_IMPL_DATA_SNAME(sname); \ - static void (*CTEST_IMPL_SETUP_FPNAME(sname))(struct CTEST_IMPL_DATA_SNAME(sname)*); \ - static void (*CTEST_IMPL_TEARDOWN_FPNAME(sname))(struct CTEST_IMPL_DATA_SNAME(sname)*); \ - struct CTEST_IMPL_DATA_SNAME(sname) - -#define CTEST_IMPL_CTEST(sname, tname, tskip) \ - static void CTEST_IMPL_FNAME(sname, tname)(void); \ - CTEST_IMPL_STRUCT(sname, tname, tskip, NULL, NULL, NULL); \ - static void CTEST_IMPL_FNAME(sname, tname)(void) - -#define CTEST_IMPL_CTEST_F(sname, tname, tskip, fixt) \ - static struct CTEST_IMPL_DATA_SNAME(sname) CTEST_IMPL_DATA_TNAME(sname, tname); \ - static void CTEST_IMPL_FNAME(sname, tname)(struct CTEST_IMPL_DATA_SNAME(sname)* fixt); \ - CTEST_IMPL_STRUCT(sname, tname, tskip, &CTEST_IMPL_DATA_TNAME(sname, tname), &CTEST_IMPL_SETUP_FPNAME(sname), &CTEST_IMPL_TEARDOWN_FPNAME(sname)); \ - static void CTEST_IMPL_FNAME(sname, tname)(struct CTEST_IMPL_DATA_SNAME(sname)* fixt) - -#endif - -void assert_str(int diag, const char* cmp, const char* exp, const char* real, const char* caller, int line); -void assert_wstr(int diag, const char* cmp, const wchar_t *exp, const wchar_t *real, const char* caller, int line); -void assert_compare(int diag, const char* cmp, intmax_t exp, intmax_t real, const char* caller, int line); -void assert_interval(int diag, intmax_t low, intmax_t high, intmax_t real, const char* caller, int line); -void assert_pointers(int diag, const char* cmp, const void* exp, const void* real, const char* caller, int line); -void assert_bool(int diag, bool exp, bool real, const char* caller, int line); -void assert_dbl_compare(int diag, const char* cmp, double exp, double real, double tol, const char* caller, int line); -void assert_fail(const char* caller, int line); - -#define CTEST_FLT_EPSILON 1e-5 -#define CTEST_DBL_EPSILON 1e-12 - -#define CTEST(sname, tname) CTEST_IMPL_CTEST(sname, tname, 0) -#define CTEST_F(sname, tname, fixt) CTEST_IMPL_CTEST_F(sname, tname, 0, fixt) -#define CTEST_SKIP(sname, tname) CTEST_IMPL_CTEST(sname, tname, 1) -#define CTEST_F_SKIP(sname, tname, fixt) CTEST_IMPL_CTEST_F(sname, tname, 1, fixt) - -// Aliases, more like gtest: -#define TEST(sname, tname) CTEST(sname, tname) -#define TEST_SKIP(sname, tname) CTEST_SKIP(sname, tname) - -#define TEST_FIXTURE(sname) CTEST_FIXTURE(sname) -#define TEST_SETUP(sname, fixt) CTEST_SETUP(sname, fixt) -#define TEST_TEARDOWN(sname, fixt) CTEST_TEARDOWN(sname, fixt) -#define TEST_F(sname, tname, fixt) CTEST_F(sname, tname, fixt) -#define TEST_F_SKIP(sname, tname, fixt) CTEST_F_SKIP(sname, tname, fixt) - -// private -#define _CHECK_STREQ(diag, exp, real) assert_str(diag, "==", exp, real, __FILE__, __LINE__) -#define _CHECK_STRNE(diag, exp, real) assert_str(diag, "!=", exp, real, __FILE__, __LINE__) -#define _CHECK_SUBSTR(diag, substr, real) assert_str(diag, "=~", substr, real, __FILE__, __LINE__) -#define _CHECK_NOT_SUBSTR(diag, substr, real) assert_str(diag, "!~", substr, real, __FILE__, __LINE__) -#define _CHECK_WSTREQ(diag, exp, real) assert_wstr(diag, "==", exp, real, __FILE__, __LINE__) -#define _CHECK_WSTRNE(diag, exp, real) assert_wstr(diag, "!=", exp, real, __FILE__, __LINE__) -#define _CHECK_EQ(diag, v1, v2) assert_compare(diag, "==", v1, v2, __FILE__, __LINE__) -#define _CHECK_NE(diag, v1, v2) assert_compare(diag, "!=", v1, v2, __FILE__, __LINE__) -#define _CHECK_LT(diag, v1, v2) assert_compare(diag, "<", v1, v2, __FILE__, __LINE__) -#define _CHECK_LE(diag, v1, v2) assert_compare(diag, "<=", v1, v2, __FILE__, __LINE__) -#define _CHECK_GT(diag, v1, v2) assert_compare(diag, ">", v1, v2, __FILE__, __LINE__) -#define _CHECK_GE(diag, v1, v2) assert_compare(diag, ">=", v1, v2, __FILE__, __LINE__) -#define _CHECK_INTERVAL(diag, low, high, real) assert_interval(diag, low, high, real, __FILE__, __LINE__) -#define _CHECK_PTR_EQ(diag, exp, real) ((void)sizeof((exp) == (real)), assert_pointers(diag, "==", exp, real, __FILE__, __LINE__)) -#define _CHECK_PTR_NE(diag, exp, real) ((void)sizeof((exp) != (real)), assert_pointers(diag, "!=", exp, real, __FILE__, __LINE__)) -#define _CHECK_BOOL(diag, exp, real) assert_bool(diag, exp, real, __FILE__, __LINE__) -#define _CHECK_NEAR(diag, exp, real, tol) assert_dbl_compare(diag, "==", exp, real, tol, __FILE__, __LINE__) -#define _CHECK_NOT_NEAR(diag, exp, real, tol) assert_dbl_compare(diag, "!=", exp, real, tol, __FILE__, __LINE__) -#define _CHECK_DOUBLE_EQ(diag, exp, real) assert_dbl_compare(diag, "==", exp, real, -CTEST_DBL_EPSILON, __FILE__, __LINE__) -#define _CHECK_DOUBLE_NE(diag, exp, real) assert_dbl_compare(diag, "!=", exp, real, -CTEST_DBL_EPSILON, __FILE__, __LINE__) -#define _CHECK_DOUBLE_LT(diag, v1, v2) assert_dbl_compare(diag, "<", v1, v2, 0.0, __FILE__, __LINE__) -#define _CHECK_DOUBLE_GT(diag, v1, v2) assert_dbl_compare(diag, ">", v1, v2, 0.0, __FILE__, __LINE__) -#define _CHECK_FLOAT_EQ(diag, v1, v2) assert_dbl_compare(diag, "==", v1, v2, -CTEST_FLT_EPSILON, __FILE__, __LINE__) -#define _CHECK_FLOAT_NE(diag, v1, v2) assert_dbl_compare(diag, "!=", v1, v2, -CTEST_FLT_EPSILON, __FILE__, __LINE__) -#define _CHECK_FLOAT_LT(diag, v1, v2) assert_dbl_compare(diag, "<", (float)(v1), (float)(v2), 0.0, __FILE__, __LINE__) -#define _CHECK_FLOAT_GT(diag, v1, v2) assert_dbl_compare(diag, ">", (float)(v1), (float)(v2), 0.0, __FILE__, __LINE__) - -// EXPECT ===================================== - -#define EXPECT_STREQ(exp, real) _CHECK_STREQ(1, exp, real) -#define EXPECT_STRNE(exp, real) _CHECK_STRNE(1, exp, real) -#define EXPECT_SUBSTR(substr, real) _CHECK_SUBSTR(1, substr, real) -#define EXPECT_NOT_SUBSTR(substr, real) _CHECK_NOT_SUBSTR(1, substr, real) -#define EXPECT_WSTREQ(exp, real) _CHECK_WSTREQ(1, exp, real) -#define EXPECT_WSTRNE(exp, real) _CHECK_WSTRNE(1, exp, real) -#define EXPECT_EQ(v1, v2) _CHECK_EQ(1, v1, v2) -#define EXPECT_NE(v1, v2) _CHECK_NE(1, v1, v2) -#define EXPECT_LT(v1, v2) _CHECK_LT(1, v1, v2) -#define EXPECT_LE(v1, v2) _CHECK_LE(1, v1, v2) -#define EXPECT_GT(v1, v2) _CHECK_GT(1, v1, v2) -#define EXPECT_GE(v1, v2) _CHECK_GE(1, v1, v2) -#define EXPECT_INTERVAL(low, high, real) _CHECK_INTERVAL(1, low, high, real) -#define EXPECT_PTR_EQ(exp, real) _CHECK_PTR_EQ(1, exp, real) -#define EXPECT_PTR_NE(exp, real) _CHECK_PTR_NE(1, exp, real) -#define EXPECT_NULL(real) _CHECK_PTR_EQ(1, NULL, real) -#define EXPECT_NOT_NULL(real) _CHECK_PTR_NE(1, NULL, real) -#define EXPECT_TRUE(real) _CHECK_BOOL(1, true, real) -#define EXPECT_FALSE(real) _CHECK_BOOL(1, false, real) -#define EXPECT_NEAR(exp, real, tol) _CHECK_NEAR(1, exp, real, tol) -#define EXPECT_NOT_NEAR(exp, real, tol) _CHECK_NOT_NEAR(1, exp, real, tol) -#define EXPECT_DOUBLE_EQ(exp, real) _CHECK_DOUBLE_EQ(1, exp, real) -#define EXPECT_DOUBLE_NE(exp, real) _CHECK_DOUBLE_NE(1, exp, real) -#define EXPECT_DOUBLE_LT(v1, v2) _CHECK_DOUBLE_LT(1, v1, v2) -#define EXPECT_DOUBLE_GT(v1, v2) _CHECK_DOUBLE_GT(1, v1, v2) -#define EXPECT_FLOAT_EQ(v1, v2) _CHECK_FLOAT_EQ(1, v1, v2) -#define EXPECT_FLOAT_NE(v1, v2) _CHECK_FLOAT_NE(1, v1, v2) -#define EXPECT_FLOAT_LT(v1, v2) _CHECK_FLOAT_LT(1, v1, v2) -#define EXPECT_FLOAT_GT(v1, v2) _CHECK_FLOAT_GT(1, v1, v2) - -// ASSERT ===================================== - -#define ASSERT_STREQ(exp, real) _CHECK_STREQ(2, exp, real) -#define ASSERT_STRNE(exp, real) _CHECK_STRNE(2, exp, real) -#define ASSERT_SUBSTR(substr, real) _CHECK_SUBSTR(2, substr, real) -#define ASSERT_NOT_SUBSTR(substr, real) _CHECK_NOT_SUBSTR(2, substr, real) -#define ASSERT_WSTREQ(exp, real) _CHECK_WSTREQ(2, exp, real) -#define ASSERT_WSTRNE(exp, real) _CHECK_WSTRNE(2, exp, real) -#define ASSERT_EQ(v1, v2) _CHECK_EQ(2, v1, v2) -#define ASSERT_NE(v1, v2) _CHECK_NE(2, v1, v2) -#define ASSERT_LT(v1, v2) _CHECK_LT(2, v1, v2) -#define ASSERT_LE(v1, v2) _CHECK_LE(2, v1, v2) -#define ASSERT_GT(v1, v2) _CHECK_GT(2, v1, v2) -#define ASSERT_GE(v1, v2) _CHECK_GE(2, v1, v2) -#define ASSERT_INTERVAL(low, high, real) _CHECK_INTERVAL(2, low, high, real) -#define ASSERT_PTR_EQ(exp, real) _CHECK_PTR_EQ(2, exp, real) -#define ASSERT_PTR_NE(exp, real) _CHECK_PTR_NE(2, exp, real) -#define ASSERT_NULL(real) _CHECK_PTR_EQ(2, NULL, real) -#define ASSERT_NOT_NULL(real) _CHECK_PTR_NE(2, NULL, real) -#define ASSERT_TRUE(real) _CHECK_BOOL(2, true, real) -#define ASSERT_FALSE(real) _CHECK_BOOL(2, false, real) -#define ASSERT_NEAR(exp, real, tol) _CHECK_NEAR(2, exp, real, tol) -#define ASSERT_NOT_NEAR(exp, real, tol) _CHECK_NOT_NEAR(2, exp, real, tol) -#define ASSERT_DOUBLE_EQ(exp, real) _CHECK_DOUBLE_EQ(2, exp, real) -#define ASSERT_DOUBLE_NE(exp, real) _CHECK_DOUBLE_NE(2, exp, real) -#define ASSERT_DOUBLE_LT(v1, v2) _CHECK_DOUBLE_LT(2, v1, v2) -#define ASSERT_DOUBLE_GT(v1, v2) _CHECK_DOUBLE_GT(2, v1, v2) -#define ASSERT_FLOAT_EQ(v1, v2) _CHECK_FLOAT_EQ(2, v1, v2) -#define ASSERT_FLOAT_NE(v1, v2) _CHECK_FLOAT_NE(2, v1, v2) -#define ASSERT_FLOAT_LT(v1, v2) _CHECK_FLOAT_LT(2, v1, v2) -#define ASSERT_FLOAT_GT(v1, v2) _CHECK_FLOAT_GT(2, v1, v2) -#define ASSERT_FAIL() assert_fail(__FILE__, __LINE__) - -#ifdef CTEST_MAIN - -#include -#include -#include -#include -#include -#if !defined(_WIN32) || defined(__GNUC__) -#include -#elif defined(_WIN32) -#include -#endif -#include -#include -#include - -static int ctest_failed; -static size_t ctest_errorsize; -static char* ctest_errormsg; -#define MSG_SIZE 4096 -static char ctest_errorbuffer[MSG_SIZE]; -static jmp_buf ctest_err; -static int color_output = 1; -static const char* suite_name; - -typedef int (*ctest_filter_func)(struct ctest*); - -#define ANSI_BLACK "\033[0;30m" -#define ANSI_RED "\033[0;31m" -#define ANSI_GREEN "\033[0;32m" -#define ANSI_YELLOW "\033[0;33m" -#define ANSI_BLUE "\033[0;34m" -#define ANSI_MAGENTA "\033[0;35m" -#define ANSI_CYAN "\033[0;36m" -#define ANSI_GREY "\033[0;37m" -#define ANSI_DARKGREY "\033[01;30m" -#define ANSI_BRED "\033[01;31m" -#define ANSI_BGREEN "\033[01;32m" -#define ANSI_BYELLOW "\033[01;33m" -#define ANSI_BBLUE "\033[01;34m" -#define ANSI_BMAGENTA "\033[01;35m" -#define ANSI_BCYAN "\033[01;36m" -#define ANSI_WHITE "\033[01;37m" -#define ANSI_NORMAL "\033[0m" - -CTEST(suite, test) { } - -static void vprint_errormsg(const char* const fmt, va_list ap) CTEST_IMPL_FORMAT_PRINTF(1, 0); -static void print_errormsg(const char* const fmt, ...) CTEST_IMPL_FORMAT_PRINTF(1, 2); - -static void vprint_errormsg(const char* const fmt, va_list ap) { - // (v)snprintf returns the number that would have been written - const int ret = vsnprintf(ctest_errormsg, ctest_errorsize, fmt, ap); - if (ret < 0) { - ctest_errormsg[0] = 0x00; - } else { - const size_t size = (size_t) ret; - const size_t s = (ctest_errorsize <= size ? size -ctest_errorsize : size); - // ctest_errorsize may overflow at this point - ctest_errorsize -= s; - ctest_errormsg += s; - } -} - -static void print_errormsg(const char* const fmt, ...) { - va_list argp; - va_start(argp, fmt); - vprint_errormsg(fmt, argp); - va_end(argp); -} - -static void msg_start(const char* color, const char* title) { - if (color_output) { - print_errormsg("%s", color); - } - print_errormsg(" %s: ", title); -} - -static void msg_end(void) { - if (color_output) { - print_errormsg(ANSI_NORMAL); - } - print_errormsg("\n"); -} - -CTEST_IMPL_DIAG_PUSH_IGNORED(missing-noreturn) - -static void ctest_print(int diag, const char* fmt, ...) CTEST_IMPL_FORMAT_PRINTF(2, 3); // may not return -static void ctest_print(int diag, const char* fmt, ...) -{ - va_list argp; - switch (diag) { - case 0: msg_start(ANSI_BLUE, "LOG"); break; - case 1: msg_start(ANSI_YELLOW, "ERR"); break; - case 2: msg_start(ANSI_BYELLOW, "ERR"); break; - } - va_start(argp, fmt); - vprint_errormsg(fmt, argp); - va_end(argp); - - msg_end(); - - if (diag == 2) - longjmp(ctest_err, 1); - ctest_failed = 1; -} - -CTEST_IMPL_DIAG_POP() - -void assert_str(int diag, const char* cmp, const char* exp, const char* real, const char* caller, int line) { - if ((!exp ^ !real) || (exp && ( - (cmp[1] == '=' && ((cmp[0] == '=') ^ (strcmp(real, exp) == 0))) || - (cmp[1] == '~' && ((cmp[0] == '=') ^ (strstr(real, exp) != NULL))) - ))) { - ctest_print(diag, "%s:%d assertion failed, '%s' %s '%s'", caller, line, exp, cmp, real); - } -} - -void assert_wstr(int diag, const char* cmp, const wchar_t *exp, const wchar_t *real, const char* caller, int line) { - if ((!exp ^ !real) || (exp && ( - (cmp[1] == '=' && ((cmp[0] == '=') ^ (wcscmp(real, exp) == 0))) || - (cmp[1] == '~' && ((cmp[0] == '=') ^ (wcsstr(real, exp) != NULL))) - ))) { - ctest_print(diag, "%s:%d assertion failed, '%ls' %s '%ls'", caller, line, exp, cmp, real); - } -} - -static bool get_compare_result(const char* cmp, int c3, bool eq) { - if (cmp[0] == '<') - return c3 < 0 || ((cmp[1] == '=') & eq); - if (cmp[0] == '>') - return c3 > 0 || ((cmp[1] == '=') & eq); - return (cmp[0] == '=') == eq; -} - -void assert_compare(int diag, const char* cmp, intmax_t exp, intmax_t real, const char* caller, int line) { - int c3 = (real < exp) - (exp < real); - - if (!get_compare_result(cmp, c3, c3 == 0)) { - ctest_print(diag, "%s:%d assertion failed, %" PRIdMAX " %s %" PRIdMAX "", caller, line, exp, cmp, real); - } -} - -void assert_interval(int diag, intmax_t low, intmax_t high, intmax_t real, const char* caller, int line) { - if (real < low || real > high) { - ctest_print(diag, "%s:%d expected %" PRIdMAX "-%" PRIdMAX ", got %" PRIdMAX, caller, line, low, high, real); - } -} - -static bool approximately_equal(double a, double b, double epsilon) { - double d = a - b; - if (d < 0) d = -d; - if (a < 0) a = -a; - if (b < 0) b = -b; - return d <= (a > b ? a : b)*epsilon; /* D.Knuth */ -} - -/* tol < 0 means it is an epsilon, else absolute error */ -void assert_dbl_compare(int diag, const char* cmp, double exp, double real, double tol, const char* caller, int line) { - double diff = exp - real; - double absdiff = diff < 0 ? -diff : diff; - int c3 = (real < exp) - (exp < real); - bool eq = tol < 0 ? approximately_equal(exp, real, -tol) : absdiff <= tol; - - if (!get_compare_result(cmp, c3, eq)) { - const char* tolstr = "tol"; - if (tol < 0) { - tolstr = "eps"; - tol = -tol; - } - ctest_print(diag, "%s:%d assertion failed, %.8g %s %.8g (diff %.4g, %s %.4g)", caller, line, exp, cmp, real, diff, tolstr, tol); - } -} - -void assert_pointers(int diag, const char* cmp, const void* exp, const void* real, const char* caller, int line) { - if ((exp == real) != (cmp[0] == '=')) { - ctest_print(diag, "%s:%d assertion failed (0x%02llx) %s (0x%02llx)", caller, line, - (unsigned long long)(uintptr_t)exp , cmp, (unsigned long long)(uintptr_t)real); - } -} - -void assert_bool(int diag, bool exp, bool real, const char* caller, int line) { - if (exp != real) { - ctest_print(diag, "%s:%d should be %s", caller, line, exp ? "true" : "false"); - } -} - -void assert_fail(const char* caller, int line) { - ctest_print(2, "%s:%d shouldn't come here", caller, line); -} - - -static int suite_all(struct ctest* t) { - (void) t; // avoid unused parameter warning - return 1; -} - -static int suite_filter(struct ctest* t) { - return strncmp(suite_name, t->ssname, strlen(suite_name)) == 0; -} - -static void color_print(const char* color, const char* text) { - if (color_output) - printf("%s%s" ANSI_NORMAL "\n", color, text); - else - printf("%s\n", text); -} - -#ifndef CTEST_NO_SEGFAULT -#include -static void sighandler(int signum) -{ - const char msg_color[] = ANSI_BRED "[SIGSEGV: Segmentation fault]" ANSI_NORMAL "\n"; - const char msg_nocolor[] = "[SIGSEGV: Segmentation fault]\n"; - - const char* msg = color_output ? msg_color : msg_nocolor; - intptr_t n = write(1, msg, (unsigned int)strlen(msg)); - (void)n; - /* "Unregister" the signal handler and send the signal back to the process - * so it can terminate as expected */ - signal(signum, SIG_DFL); -#if !defined(_WIN32) || defined(__CYGWIN__) - //kill(getpid(), signum); -#endif -} -#endif - -int ctest_main(int argc, const char *argv[]); -#ifdef __GNUC__ -__attribute__((no_sanitize_address)) -#endif -int ctest_main(int argc, const char *argv[]) -{ - static int total = 0; - static int num_ok = 0; - static int num_fail = 0; - static int num_skip = 0; - static int idx = 1; - static ctest_filter_func filter = suite_all; - -#ifndef CTEST_NO_SEGFAULT - signal(SIGSEGV, sighandler); -#endif - - if (argc == 2) { - suite_name = argv[1]; - filter = suite_filter; - } -#ifdef CTEST_NO_COLORS - color_output = 0; -#else - color_output = isatty(1); -#endif - clock_t t1 = clock(); - - uint32_t* magic_begin = &CTEST_IMPL_TNAME(suite, test).magic1; - uint32_t* magic_end = &CTEST_IMPL_TNAME(suite, test).magic0, *m; - ptrdiff_t num_ints = sizeof(struct ctest)/sizeof *m; - -#if (defined __TINYC__ && defined __unix__) - #define CTEST_IMPL_MAGIC_SEEK 10 /* search 4*(1+10) bytes outside ctest entry bounds */ -#else - #define CTEST_IMPL_MAGIC_SEEK 0 /* access only 4 bytes outside outer ctest entry bounds */ -#endif - for (m = magic_begin; magic_begin - m <= num_ints + CTEST_IMPL_MAGIC_SEEK; --m) { - if (*m == 0xBADCAFE1) { - magic_begin = m; - m -= num_ints - 1; - } - } - for (m = magic_end; m - magic_end <= num_ints; ++m) { - if (*m == 0xBADCAFE0) { - magic_end = m; - m += num_ints - 1; - } - } - magic_begin = &CTEST_CONTAINER_OF(magic_begin, struct ctest, magic1)->magic0; - - static struct ctest* test; - for (m = magic_begin; m <= magic_end; m += num_ints) { - while (*m != 0xBADCAFE0) ++m; - test = CTEST_CONTAINER_OF(m, struct ctest, magic0); - if (test == &CTEST_IMPL_TNAME(suite, test)) continue; - if (filter(test)) total++; - } - - for (m = magic_begin; m <= magic_end; m += num_ints) { - while (*m != 0xBADCAFE0) ++m; - test = CTEST_CONTAINER_OF(m, struct ctest, magic0); - if (test == &CTEST_IMPL_TNAME(suite, test)) continue; - if (filter(test)) { - ctest_failed = 0; - ctest_errorbuffer[0] = 0; - ctest_errorsize = MSG_SIZE-1; - ctest_errormsg = ctest_errorbuffer; - printf("TEST %d/%d %s:%s ", idx, total, test->ssname, test->ttname); - fflush(stdout); - if (test->skip) { - color_print(ANSI_BYELLOW, "[SKIPPED]"); - num_skip++; - } else { - int result = setjmp(ctest_err); - if (result == 0) { - if (test->setup && *test->setup) (*test->setup)(test->data); - if (test->data) - test->run.unary(test->data); - else - test->run.nullary(); - if (test->teardown && *test->teardown) (*test->teardown)(test->data); - // if we got here it's ok - if (ctest_failed) longjmp(ctest_err, 1); -#ifdef CTEST_NO_COLOR_OK - printf("[OK]\n"); -#else - color_print(ANSI_BGREEN, "[OK]"); -#endif - num_ok++; - } else { - color_print(ANSI_BRED, "[FAIL]"); - num_fail++; - } - if (ctest_errorsize != MSG_SIZE-1) printf("%s", ctest_errorbuffer); - } - idx++; - } - } - clock_t t2 = clock(); - - const char* color = (num_fail) ? ANSI_BRED : ANSI_GREEN; - char results[80]; - snprintf(results, sizeof(results), "RESULTS: %d tests (%d ok, %d failed, %d skipped) ran in %.1f ms", - total, num_ok, num_fail, num_skip, (double)(t2 - t1)*1000.0/CLOCKS_PER_SEC); - color_print(color, results); - return num_fail; -} - -#endif - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/src/finchlite/codegen/stc/stc/deque.h b/src/finchlite/codegen/stc/stc/deque.h deleted file mode 100644 index dd00206f..00000000 --- a/src/finchlite/codegen/stc/stc/deque.h +++ /dev/null @@ -1,205 +0,0 @@ -/* MIT License - * - * Copyright (c) 2025 Tyge Løvset - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -// Deque - double ended queue. Implemented as a ring buffer, extension of queue. - -#include "priv/linkage.h" -#include "types.h" - -#ifndef STC_DEQUE_H_INCLUDED -#define STC_DEQUE_H_INCLUDED -#include "common.h" -#include -#endif // STC_DEQUE_H_INCLUDED - -#ifndef _i_prefix - #define _i_prefix deque_ -#endif -#define _pop _pop_front -#define _pull _pull_front -#include "priv/template.h" -#include "priv/queue_prv.h" -#undef _pop -#undef _pull - -STC_API _m_value* _c_MEMB(_push_front)(Self* self, _m_value value); -STC_API _m_iter _c_MEMB(_insert_n)(Self* self, isize idx, const _m_value* arr, isize n); -STC_API _m_iter _c_MEMB(_insert_uninit)(Self* self, isize idx, isize n); -STC_API void _c_MEMB(_erase_n)(Self* self, isize idx, isize n); - -STC_INLINE const _m_value* -_c_MEMB(_at)(const Self* self, isize idx) { - c_assert(c_uless(idx, _c_MEMB(_size)(self))); - return self->cbuf + _cbuf_topos(self, idx); -} - -STC_INLINE _m_value* -_c_MEMB(_at_mut)(Self* self, isize idx) - { return (_m_value*)_c_MEMB(_at)(self, idx); } - -STC_INLINE _m_value* -_c_MEMB(_push_back)(Self* self, _m_value val) - { return _c_MEMB(_push)(self, val); } - -STC_INLINE void -_c_MEMB(_pop_back)(Self* self) { - c_assert(!_c_MEMB(_is_empty)(self)); - self->end = (self->end - 1) & self->capmask; - i_keydrop((self->cbuf + self->end)); -} - -STC_INLINE _m_value _c_MEMB(_pull_back)(Self* self) { // move back out of deque - c_assert(!_c_MEMB(_is_empty)(self)); - self->end = (self->end - 1) & self->capmask; - return self->cbuf[self->end]; -} - -STC_INLINE _m_iter -_c_MEMB(_insert_at)(Self* self, _m_iter it, const _m_value val) { - isize idx = _cbuf_toidx(self, it.pos); - return _c_MEMB(_insert_n)(self, idx, &val, 1); -} - -STC_INLINE _m_iter -_c_MEMB(_erase_at)(Self* self, _m_iter it) { - _c_MEMB(_erase_n)(self, _cbuf_toidx(self, it.pos), 1); - if (it.pos == self->end) it.ref = NULL; - return it; -} - -STC_INLINE _m_iter -_c_MEMB(_erase_range)(Self* self, _m_iter it1, _m_iter it2) { - isize idx1 = _cbuf_toidx(self, it1.pos); - isize idx2 = _cbuf_toidx(self, it2.pos); - _c_MEMB(_erase_n)(self, idx1, idx2 - idx1); - if (it1.pos == self->end) it1.ref = NULL; - return it1; -} - -#ifndef i_no_emplace -STC_API _m_iter -_c_MEMB(_emplace_n)(Self* self, isize idx, const _m_raw* raw, isize n); - -STC_INLINE _m_value* -_c_MEMB(_emplace_front)(Self* self, const _m_raw raw) - { return _c_MEMB(_push_front)(self, i_keyfrom(raw)); } - -STC_INLINE _m_value* -_c_MEMB(_emplace_back)(Self* self, const _m_raw raw) - { return _c_MEMB(_push)(self, i_keyfrom(raw)); } - -STC_INLINE _m_iter -_c_MEMB(_emplace_at)(Self* self, _m_iter it, const _m_raw raw) - { return _c_MEMB(_insert_at)(self, it, i_keyfrom(raw)); } -#endif - -#if defined _i_has_eq -STC_API _m_iter _c_MEMB(_find_in)(const Self* self, _m_iter p1, _m_iter p2, _m_raw raw); - -STC_INLINE _m_iter -_c_MEMB(_find)(const Self* self, _m_raw raw) { - return _c_MEMB(_find_in)(self, _c_MEMB(_begin)(self), _c_MEMB(_end)(self), raw); -} -#endif // _i_has_eq - -#if defined _i_has_cmp -#include "priv/sort_prv.h" -#endif // _i_has_cmp - -/* -------------------------- IMPLEMENTATION ------------------------- */ -#if defined i_implement - -STC_DEF _m_value* -_c_MEMB(_push_front)(Self* self, _m_value value) { - isize start = (self->start - 1) & self->capmask; - if (start == self->end) { // full - if (!_c_MEMB(_reserve)(self, self->capmask + 3)) // => 2x expand - return NULL; - start = (self->start - 1) & self->capmask; - } - _m_value *v = self->cbuf + start; - self->start = start; - *v = value; - return v; -} - -STC_DEF void -_c_MEMB(_erase_n)(Self* self, const isize idx, const isize n) { - const isize len = _c_MEMB(_size)(self); - c_assert(idx + n <= len); - for (isize i = idx + n - 1; i >= idx; --i) - i_keydrop(_c_MEMB(_at_mut)(self, i)); - for (isize i = idx, j = i + n; j < len; ++i, ++j) - *_c_MEMB(_at_mut)(self, i) = *_c_MEMB(_at)(self, j); - self->end = (self->end - n) & self->capmask; -} - -STC_DEF _m_iter -_c_MEMB(_insert_uninit)(Self* self, const isize idx, const isize n) { - const isize len = _c_MEMB(_size)(self); - _m_iter it = {._s=self}; - if (len + n >= self->capmask) - if (!_c_MEMB(_reserve)(self, len + n)) // minimum 2x expand - return it; - it.pos = _cbuf_topos(self, idx); - it.ref = self->cbuf + it.pos; - self->end = (self->end + n) & self->capmask; - - if (it.pos < self->end) // common case because of reserve policy - c_memmove(it.ref + n, it.ref, (len - idx)*c_sizeof *it.ref); - else for (isize i = len - 1, j = i + n; i >= idx; --i, --j) - *_c_MEMB(_at_mut)(self, j) = *_c_MEMB(_at)(self, i); - return it; -} - -STC_DEF _m_iter -_c_MEMB(_insert_n)(Self* self, const isize idx, const _m_value* arr, const isize n) { - _m_iter it = _c_MEMB(_insert_uninit)(self, idx, n); - for (isize i = idx, j = 0; j < n; ++i, ++j) - *_c_MEMB(_at_mut)(self, i) = arr[j]; - return it; -} - -#ifndef i_no_emplace -STC_DEF _m_iter -_c_MEMB(_emplace_n)(Self* self, const isize idx, const _m_raw* raw, const isize n) { - _m_iter it = _c_MEMB(_insert_uninit)(self, idx, n); - for (isize i = idx, j = 0; j < n; ++i, ++j) - *_c_MEMB(_at_mut)(self, i) = i_keyfrom(raw[j]); - return it; -} -#endif - -#if defined _i_has_eq -STC_DEF _m_iter -_c_MEMB(_find_in)(const Self* self, _m_iter i1, _m_iter i2, _m_raw raw) { - (void)self; - for (; i1.ref != i2.ref; _c_MEMB(_next)(&i1)) { - const _m_raw r = i_keytoraw(i1.ref); - if (i_eq((&raw), (&r))) - break; - } - return i1; -} -#endif -#endif // IMPLEMENTATION -#include "sys/finalize.h" diff --git a/src/finchlite/codegen/stc/stc/hashmap.h b/src/finchlite/codegen/stc/stc/hashmap.h deleted file mode 100644 index 5eaaf9df..00000000 --- a/src/finchlite/codegen/stc/stc/hashmap.h +++ /dev/null @@ -1,43 +0,0 @@ -/* MIT License - * - * Copyright (c) 2025 Tyge Løvmap - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -// Unordered map - implemented with the robin-hood hashing scheme. -/* -#define T IMap, int, int -#include -#include - -int main(void) { - IMap map = c_make(IMap, {{12, 32}, {42, 54}}); - IMap_insert(&map, 5, 15); - IMap_insert(&map, 8, 18); - - for (c_each_kv(k, v, IMap, map)) - printf(" %d -> %d\n", *k, *v); - - IMap_drop(&map); -} -*/ - -#define _i_prefix hmap_ -#include "hmap.h" diff --git a/src/finchlite/codegen/stc/stc/hashset.h b/src/finchlite/codegen/stc/stc/hashset.h deleted file mode 100644 index 76a858d7..00000000 --- a/src/finchlite/codegen/stc/stc/hashset.h +++ /dev/null @@ -1,44 +0,0 @@ -/* MIT License - * - * Copyright (c) 2025 Tyge Løvset - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -// Unordered set - implemented with the robin-hood hashing scheme. -/* -#define T ISet, int -#include -#include - -int main(void) { - ISet set = {0}; - ISet_insert(&set, 5); - ISet_insert(&set, 8); - - for (c_each(i, ISet, set)) - printf(" %d\n", *i.ref); - - ISet_drop(&set); -} -*/ - -#define _i_prefix hset_ -#define _i_is_set -#include "hmap.h" diff --git a/src/finchlite/codegen/stc/stc/hmap.h b/src/finchlite/codegen/stc/stc/hmap.h deleted file mode 100644 index b6988c32..00000000 --- a/src/finchlite/codegen/stc/stc/hmap.h +++ /dev/null @@ -1,517 +0,0 @@ -/* MIT License - * - * Copyright (c) 2025 Tyge Løvset - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -// Unordered set/map - implemented with the robin-hood hashing scheme. -/* -#include - -#define T icmap, int, char -#include - -int main(void) { - icmap m = {0}; - icmap_emplace(&m, 5, 'a'); - icmap_emplace(&m, 8, 'b'); - icmap_emplace(&m, 12, 'c'); - - icmap_value* v = icmap_get(&m, 10); // NULL - char val = *icmap_at(&m, 5); // 'a' - icmap_emplace_or_assign(&m, 5, 'd'); // update - icmap_erase(&m, 8); - - for (c_each(i, icmap, m)) - printf("map %d: %c\n", i.ref->first, i.ref->second); - - icmap_drop(&m); -} -*/ -#include "priv/linkage.h" -#include "types.h" - -#ifndef STC_HMAP_H_INCLUDED -#define STC_HMAP_H_INCLUDED -#include "common.h" -#include -#define _hashmask 0x3fU -#define _distmask 0x3ffU -struct hmap_meta { uint16_t hashx:6, dist:10; }; // dist: 0=empty, 1=PSL 0, 2=PSL 1, ... -#endif // STC_HMAP_H_INCLUDED - -#ifndef _i_prefix - #define _i_prefix hmap_ -#endif -#ifndef _i_is_set - #define _i_is_map - #define _i_MAP_ONLY c_true - #define _i_SET_ONLY c_false - #define _i_keyref(vp) (&(vp)->first) -#else - #define _i_MAP_ONLY c_false - #define _i_SET_ONLY c_true - #define _i_keyref(vp) (vp) -#endif -#define _i_is_hash -#include "priv/template.h" -#ifndef i_declared - _c_DEFTYPES(_declare_htable, Self, i_key, i_val, _i_MAP_ONLY, _i_SET_ONLY, _i_aux_def); -#endif - -_i_MAP_ONLY( struct _m_value { - _m_key first; - _m_mapped second; -}; ) - -typedef i_keyraw _m_keyraw; -typedef i_valraw _m_rmapped; -typedef _i_SET_ONLY( i_keyraw ) - _i_MAP_ONLY( struct { _m_keyraw first; - _m_rmapped second; } ) -_m_raw; - -#ifndef i_no_clone -STC_API Self _c_MEMB(_clone)(Self map); -#endif -STC_API void _c_MEMB(_drop)(const Self* cself); -STC_API void _c_MEMB(_clear)(Self* self); -STC_API bool _c_MEMB(_reserve)(Self* self, isize capacity); -STC_API void _c_MEMB(_erase_entry)(Self* self, _m_value* val); -STC_API float _c_MEMB(_max_load_factor)(const Self* self); -STC_API isize _c_MEMB(_capacity)(const Self* map); -STC_API _m_result _c_MEMB(_bucket_lookup_)(const Self* self, const _m_keyraw* rkeyptr); -STC_API _m_result _c_MEMB(_bucket_insert_)(const Self* self, const _m_keyraw* rkeyptr); - -STC_INLINE bool _c_MEMB(_is_empty)(const Self* map) { return !map->size; } -STC_INLINE isize _c_MEMB(_size)(const Self* map) { return (isize)map->size; } -STC_INLINE isize _c_MEMB(_bucket_count)(Self* map) { return map->bucket_count; } -STC_INLINE bool _c_MEMB(_contains)(const Self* self, _m_keyraw rkey) - { return self->size && _c_MEMB(_bucket_lookup_)(self, &rkey).ref; } -STC_INLINE void _c_MEMB(_shrink_to_fit)(Self* self) - { _c_MEMB(_reserve)(self, (isize)self->size); } - -#ifndef i_max_load_factor - #define i_max_load_factor 0.80f -#endif - -STC_INLINE _m_result -_c_MEMB(_insert_entry_)(Self* self, _m_keyraw rkey) { - if (self->size >= (isize)((float)self->bucket_count * (i_max_load_factor))) - if (!_c_MEMB(_reserve)(self, (isize)(self->size*3/2 + 2))) - return c_literal(_m_result){0}; - - _m_result res = _c_MEMB(_bucket_insert_)(self, &rkey); - self->size += res.inserted; - return res; -} - -#ifdef _i_is_map - STC_API _m_result _c_MEMB(_insert_or_assign)(Self* self, _m_key key, _m_mapped mapped); - #ifndef i_no_emplace - STC_API _m_result _c_MEMB(_emplace_or_assign)(Self* self, _m_keyraw rkey, _m_rmapped rmapped); - #endif - - STC_INLINE const _m_mapped* _c_MEMB(_at)(const Self* self, _m_keyraw rkey) { - _m_result res = _c_MEMB(_bucket_lookup_)(self, &rkey); - c_assert(res.ref); - return &res.ref->second; - } - - STC_INLINE _m_mapped* _c_MEMB(_at_mut)(Self* self, _m_keyraw rkey) - { return (_m_mapped*)_c_MEMB(_at)(self, rkey); } -#endif // _i_is_map - -#ifndef i_no_clone - STC_INLINE void _c_MEMB(_copy)(Self *self, const Self* other) { - if (self == other) - return; - _c_MEMB(_drop)(self); - *self = _c_MEMB(_clone)(*other); - } - - STC_INLINE _m_value _c_MEMB(_value_clone)(const Self* self, _m_value _val) { - (void)self; - *_i_keyref(&_val) = i_keyclone((*_i_keyref(&_val))); - _i_MAP_ONLY( _val.second = i_valclone(_val.second); ) - return _val; - } -#endif // !i_no_clone - -#ifndef i_no_emplace - STC_INLINE _m_result - _c_MEMB(_emplace)(Self* self, _m_keyraw rkey _i_MAP_ONLY(, _m_rmapped rmapped)) { - _m_result _res = _c_MEMB(_insert_entry_)(self, rkey); - if (_res.inserted) { - *_i_keyref(_res.ref) = i_keyfrom(rkey); - _i_MAP_ONLY( _res.ref->second = i_valfrom(rmapped); ) - } - return _res; - } -#endif // !i_no_emplace - -STC_INLINE _m_raw _c_MEMB(_value_toraw)(const _m_value* val) { - return _i_SET_ONLY( i_keytoraw(val) ) - _i_MAP_ONLY( c_literal(_m_raw){i_keytoraw((&val->first)), i_valtoraw((&val->second))} ); -} - -STC_INLINE void _c_MEMB(_value_drop)(const Self* self, _m_value* _val) { - (void)self; - i_keydrop(_i_keyref(_val)); - _i_MAP_ONLY( i_valdrop((&_val->second)); ) -} - -STC_INLINE Self _c_MEMB(_move)(Self *self) { - Self m = *self; - self->bucket_count = self->size = 0; - self->meta = NULL; self->table = NULL; - return m; -} - -STC_INLINE void _c_MEMB(_take)(Self *self, Self unowned) { - _c_MEMB(_drop)(self); - *self = unowned; -} - -STC_INLINE _m_result -_c_MEMB(_insert)(Self* self, _m_key _key _i_MAP_ONLY(, _m_mapped _mapped)) { - _m_result _res = _c_MEMB(_insert_entry_)(self, i_keytoraw((&_key))); - if (_res.inserted) - { *_i_keyref(_res.ref) = _key; _i_MAP_ONLY( _res.ref->second = _mapped; )} - else - { i_keydrop((&_key)); _i_MAP_ONLY( i_valdrop((&_mapped)); )} - return _res; -} - -STC_INLINE _m_value* _c_MEMB(_push)(Self* self, _m_value _val) { - _m_result _res = _c_MEMB(_insert_entry_)(self, i_keytoraw(_i_keyref(&_val))); - if (_res.inserted) - *_res.ref = _val; - else - _c_MEMB(_value_drop)(self, &_val); - return _res.ref; -} - -#if defined _i_is_map && !defined _i_no_put -STC_INLINE _m_result _c_MEMB(_put)(Self* self, _m_keyraw rkey, _m_rmapped rmapped) { - #ifdef i_no_emplace - return _c_MEMB(_insert_or_assign)(self, rkey, rmapped); - #else - return _c_MEMB(_emplace_or_assign)(self, rkey, rmapped); - #endif -} -#endif - -#ifndef _i_no_put -STC_INLINE void _c_MEMB(_put_n)(Self* self, const _m_raw* raw, isize n) { - while (n--) - #if defined _i_is_set && defined i_no_emplace - _c_MEMB(_insert)(self, *raw++); - #elif defined _i_is_set - _c_MEMB(_emplace)(self, *raw++); - #else - _c_MEMB(_put)(self, raw->first, raw->second), ++raw; - #endif -} -#endif - -#ifndef _i_aux_alloc -STC_INLINE Self _c_MEMB(_init)(void) - { Self cx = {0}; return cx; } - -#ifndef _i_no_put -STC_INLINE Self _c_MEMB(_from_n)(const _m_raw* raw, isize n) - { Self cx = {0}; _c_MEMB(_put_n)(&cx, raw, n); return cx; } -#endif - -STC_INLINE Self _c_MEMB(_with_capacity)(const isize cap) - { Self cx = {0}; _c_MEMB(_reserve)(&cx, cap); return cx; } -#endif - -STC_API _m_iter _c_MEMB(_begin)(const Self* self); - -STC_INLINE _m_iter _c_MEMB(_end)(const Self* self) - { (void)self; return c_literal(_m_iter){0}; } - -STC_INLINE void _c_MEMB(_next)(_m_iter* it) { - while ((++it->ref, (++it->_mref)->dist == 0)) ; - if (it->ref == it->_end) it->ref = NULL; -} - -STC_INLINE _m_iter _c_MEMB(_advance)(_m_iter it, size_t n) { - while (n-- && it.ref) _c_MEMB(_next)(&it); - return it; -} - -STC_INLINE _m_iter -_c_MEMB(_find)(const Self* self, _m_keyraw rkey) { - _m_value* ref; - if (self->size != 0 && (ref = _c_MEMB(_bucket_lookup_)(self, &rkey).ref) != NULL) - return c_literal(_m_iter){ref, - &self->table[self->bucket_count], - &self->meta[ref - self->table]}; - return _c_MEMB(_end)(self); -} - -STC_INLINE const _m_value* -_c_MEMB(_get)(const Self* self, _m_keyraw rkey) { - return self->size ? _c_MEMB(_bucket_lookup_)(self, &rkey).ref : NULL; -} - -STC_INLINE _m_value* -_c_MEMB(_get_mut)(Self* self, _m_keyraw rkey) - { return (_m_value*)_c_MEMB(_get)(self, rkey); } - -STC_INLINE int -_c_MEMB(_erase)(Self* self, _m_keyraw rkey) { - _m_value* ref; - if (self->size != 0 && (ref = _c_MEMB(_bucket_lookup_)(self, &rkey).ref) != NULL) - { _c_MEMB(_erase_entry)(self, ref); return 1; } - return 0; -} - -STC_INLINE _m_iter -_c_MEMB(_erase_at)(Self* self, _m_iter it) { - _c_MEMB(_erase_entry)(self, it.ref); - if (it._mref->dist == 0) - _c_MEMB(_next)(&it); - return it; -} - -STC_INLINE bool -_c_MEMB(_eq)(const Self* self, const Self* other) { - if (_c_MEMB(_size)(self) != _c_MEMB(_size)(other)) return false; - for (_m_iter i = _c_MEMB(_begin)(self); i.ref; _c_MEMB(_next)(&i)) { - const _m_keyraw _raw = i_keytoraw(_i_keyref(i.ref)); - if (!_c_MEMB(_contains)(other, _raw)) return false; - } - return true; -} - -/* -------------------------- IMPLEMENTATION ------------------------- */ -#if defined i_implement - -STC_DEF _m_iter _c_MEMB(_begin)(const Self* self) { - _m_iter it = {self->table, self->table, self->meta}; - if (it.ref == NULL) return it; - it._end += self->bucket_count; - while (it._mref->dist == 0) - ++it.ref, ++it._mref; - if (it.ref == it._end) it.ref = NULL; - return it; -} - -STC_DEF float _c_MEMB(_max_load_factor)(const Self* self) { - (void)self; return (float)(i_max_load_factor); -} - -STC_DEF isize _c_MEMB(_capacity)(const Self* map) { - return (isize)((float)map->bucket_count * (i_max_load_factor)); -} - -static void _c_MEMB(_wipe_)(Self* self) { - if (self->size == 0) - return; - _m_value* d = self->table, *_end = &d[self->bucket_count]; - struct hmap_meta* m = self->meta; - for (; d != _end; ++d) - if ((m++)->dist) - _c_MEMB(_value_drop)(self, d); -} - -STC_DEF void _c_MEMB(_drop)(const Self* cself) { - Self* self = (Self*)cself; - if (self->bucket_count > 0) { - _c_MEMB(_wipe_)(self); - _i_free_n(self->meta, self->bucket_count + 1); - _i_free_n(self->table, self->bucket_count); - } -} - -STC_DEF void _c_MEMB(_clear)(Self* self) { - _c_MEMB(_wipe_)(self); - self->size = 0; - c_memset(self->meta, 0, c_sizeof(struct hmap_meta)*self->bucket_count); -} - -#ifdef _i_is_map - STC_DEF _m_result - _c_MEMB(_insert_or_assign)(Self* self, _m_key _key, _m_mapped _mapped) { - _m_result _res = _c_MEMB(_insert_entry_)(self, i_keytoraw((&_key))); - _m_mapped* _mp = _res.ref ? &_res.ref->second : &_mapped; - if (_res.inserted) - _res.ref->first = _key; - else - { i_keydrop((&_key)); i_valdrop(_mp); } - *_mp = _mapped; - return _res; - } - - #ifndef i_no_emplace - STC_DEF _m_result - _c_MEMB(_emplace_or_assign)(Self* self, _m_keyraw rkey, _m_rmapped rmapped) { - _m_result _res = _c_MEMB(_insert_entry_)(self, rkey); - if (_res.inserted) - _res.ref->first = i_keyfrom(rkey); - else { - if (_res.ref == NULL) return _res; - i_valdrop((&_res.ref->second)); - } - _res.ref->second = i_valfrom(rmapped); - return _res; - } - #endif // !i_no_emplace -#endif // _i_is_map - -STC_DEF _m_result -_c_MEMB(_bucket_lookup_)(const Self* self, const _m_keyraw* rkeyptr) { - const size_t _hash = i_hash(rkeyptr); - const size_t _idxmask = (size_t)self->bucket_count - 1; - _m_result _res = {.idx=_hash & _idxmask, .hashx=(uint8_t)((_hash >> 24) & _hashmask), .dist=1}; - - while (_res.dist <= self->meta[_res.idx].dist) { - if (self->meta[_res.idx].hashx == _res.hashx) { - const _m_keyraw _raw = i_keytoraw(_i_keyref(&self->table[_res.idx])); - if (i_eq((&_raw), rkeyptr)) { - _res.ref = &self->table[_res.idx]; - break; - } - } - _res.idx = (_res.idx + 1) & _idxmask; - ++_res.dist; - } - return _res; -} - -STC_DEF _m_result -_c_MEMB(_bucket_insert_)(const Self* self, const _m_keyraw* rkeyptr) { - _m_result res = _c_MEMB(_bucket_lookup_)(self, rkeyptr); - if (res.ref) // bucket exists - return res; - res.ref = &self->table[res.idx]; - res.inserted = true; - struct hmap_meta mnew = {.hashx=(uint16_t)(res.hashx & _hashmask), - .dist=(uint16_t)(res.dist & _distmask)}; - struct hmap_meta mcur = self->meta[res.idx]; - self->meta[res.idx] = mnew; - - if (mcur.dist != 0) { // collision, reorder buckets - size_t mask = (size_t)self->bucket_count - 1; - _m_value dcur = *res.ref; - for (;;) { - res.idx = (res.idx + 1) & mask; - ++mcur.dist; - if (self->meta[res.idx].dist == 0) - break; - if (self->meta[res.idx].dist < mcur.dist) { - c_swap(&mcur, &self->meta[res.idx]); - c_swap(&dcur, &self->table[res.idx]); - } - } - self->meta[res.idx] = mcur; - self->table[res.idx] = dcur; - } - return res; -} - - -#ifndef i_no_clone - STC_DEF Self - _c_MEMB(_clone)(Self map) { - if (map.bucket_count == 0) - return c_literal(Self){0}; - Self out = map, *self = &out; // _i_new_n may refer self via i_aux - const isize _mbytes = (map.bucket_count + 1)*c_sizeof *map.meta; - out.table = (_m_value *)i_malloc(map.bucket_count*c_sizeof *out.table); - out.meta = (struct hmap_meta *)i_malloc(_mbytes); - - if (out.table && out.meta) { - c_memcpy(out.meta, map.meta, _mbytes); - for (isize i = 0; i < map.bucket_count; ++i) - if (map.meta[i].dist) - out.table[i] = _c_MEMB(_value_clone)(self, map.table[i]); - return out; - } else { - if (out.meta) i_free(out.meta, _mbytes); - if (out.table) _i_free_n(out.table, map.bucket_count); - return c_literal(Self){0}; - } - } -#endif - -STC_DEF bool -_c_MEMB(_reserve)(Self* _self, const isize _newcap) { - isize _newbucks = (isize)((float)_newcap / (i_max_load_factor)) + 4; - _newbucks = c_next_pow2(_newbucks); - - if (_newcap < _self->size || _newbucks == _self->bucket_count) - return true; - Self map = *_self, *self = ↦ (void)self; - map.table = _i_new_n(_m_value, _newbucks); - map.meta = _i_new_zeros(struct hmap_meta, _newbucks + 1); - map.bucket_count = _newbucks; - - bool ok = map.table && map.meta; - if (ok) { // Rehash: - map.meta[_newbucks].dist = _distmask; // end-mark for iter - const _m_value* d = _self->table; - const struct hmap_meta* m = _self->meta; - - for (isize i = 0; i < _self->bucket_count; ++i, ++d) if (m[i].dist != 0) { - _m_keyraw r = i_keytoraw(_i_keyref(d)); - *_c_MEMB(_bucket_insert_)(&map, &r).ref = *d; // move element - } - c_swap(_self, &map); - } - _i_free_n(map.meta, map.bucket_count + (int)(map.meta != NULL)); - _i_free_n(map.table, map.bucket_count); - return ok; -} - -STC_DEF void -_c_MEMB(_erase_entry)(Self* self, _m_value* _val) { - _m_value* d = self->table; - struct hmap_meta *m = self->meta; - size_t i = (size_t)(_val - d), j = i; - size_t mask = (size_t)self->bucket_count - 1; - - _c_MEMB(_value_drop)(self, _val); - for (;;) { - j = (j + 1) & mask; - if (m[j].dist < 2) // 0 => empty, 1 => PSL 0 - break; - d[i] = d[j]; - m[i] = m[j]; - --m[i].dist; - i = j; - } - m[i].dist = 0; - --self->size; -} - -#endif // i_implement -#undef i_max_load_factor -#undef _i_is_set -#undef _i_is_map -#undef _i_is_hash -#undef _i_keyref -#undef _i_MAP_ONLY -#undef _i_SET_ONLY -#include "sys/finalize.h" diff --git a/src/finchlite/codegen/stc/stc/hset.h b/src/finchlite/codegen/stc/stc/hset.h deleted file mode 100644 index d8a7d990..00000000 --- a/src/finchlite/codegen/stc/stc/hset.h +++ /dev/null @@ -1,43 +0,0 @@ -/* MIT License - * - * Copyright (c) 2025 Tyge Løvset - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -// Unordered set - implemented with the robin-hood hashing scheme. -/* -#define T iset, int -#include -#include - -int main(void) { - iset set = {0}; - iset_insert(&set, 5); - iset_insert(&set, 8); - - for (c_each(i, iset, set)) - printf("set %d\n", *i.ref); - iset_drop(&set); -} -*/ - -#define _i_prefix hset_ -#define _i_is_set -#include "hmap.h" diff --git a/src/finchlite/codegen/stc/stc/list.h b/src/finchlite/codegen/stc/stc/list.h deleted file mode 100644 index bf85b3c5..00000000 --- a/src/finchlite/codegen/stc/stc/list.h +++ /dev/null @@ -1,431 +0,0 @@ -/* MIT License - * - * Copyright (c) 2025 Tyge Løvset - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/* Circular Singly-linked Lists. - This implements a std::forward_list-like class in C. Because it is circular, - it also support both push_back() and push_front(), unlike std::forward_list: - - #include - #include - - #define T List, long, (c_use_cmp) // enable sorting, uses default *x < *y. - #include - - int main(void) - { - List list = {0}; - - for (int i = 0; i < 5000000; ++i) // five million - List_push_back(&list, crand64_uint() & (1<<24) - 1; - - int n = 0; - for (c_each(i, List, list)) - if (++n % 100000 == 0) printf("%8d: %10zu\n", n, *i.ref); - - // Sort them... - List_sort(&list); // sort.h quicksort - - n = 0; - puts("sorted"); - for (c_each(i, List, list)) - if (++n % 100000 == 0) printf("%8d: %10zu\n", n, *i.ref); - - List_drop(&list); - } -*/ -#include "priv/linkage.h" -#include "types.h" - -#ifndef STC_LIST_H_INCLUDED -#define STC_LIST_H_INCLUDED -#include "common.h" -#include - -#define _c_list_complete_types(SELF, dummy) \ - struct SELF##_node { \ - SELF##_value value; /* must be first! */ \ - struct SELF##_node *next; \ - } - -#define _clist_tonode(vp) c_safe_cast(_m_node*, _m_value*, vp) - -#define _c_list_insert_entry_after(ref, val) \ - _m_node *entry = _i_new_n(_m_node, 1); entry->value = val; \ - _c_list_insert_after_node(ref, entry) - -#define _c_list_insert_after_node(ref, entry) \ - if (ref) entry->next = ref->next, ref->next = entry; \ - else entry->next = entry - // +: set self->last based on node - -#endif // STC_LIST_H_INCLUDED - -#ifndef _i_prefix - #define _i_prefix list_ -#endif -#include "priv/template.h" - -#define _i_is_list -#ifndef i_declared - _c_DEFTYPES(_declare_list, Self, i_key, _i_aux_def); -#endif -_c_DEFTYPES(_c_list_complete_types, Self, dummy); -typedef i_keyraw _m_raw; - -STC_API void _c_MEMB(_drop)(const Self* cself); -STC_API _m_value* _c_MEMB(_push_back)(Self* self, _m_value value); -STC_API _m_value* _c_MEMB(_push_front)(Self* self, _m_value value); -STC_API _m_iter _c_MEMB(_insert_at)(Self* self, _m_iter it, _m_value value); -STC_API _m_iter _c_MEMB(_erase_at)(Self* self, _m_iter it); -STC_API _m_iter _c_MEMB(_erase_range)(Self* self, _m_iter it1, _m_iter it2); -#if defined _i_has_eq -STC_API _m_iter _c_MEMB(_find_in)(const Self* self, _m_iter it1, _m_iter it2, _m_raw val); -STC_API isize _c_MEMB(_remove)(Self* self, _m_raw val); -#endif -#if defined _i_has_cmp -STC_API bool _c_MEMB(_sort)(Self* self); -#endif -STC_API void _c_MEMB(_reverse)(Self* self); -STC_API _m_iter _c_MEMB(_splice)(Self* self, _m_iter it, Self* other); -STC_API Self _c_MEMB(_split_off)(Self* self, _m_iter it1, _m_iter it2); -STC_API _m_value* _c_MEMB(_push_back_node)(Self* self, _m_node* node); -STC_API _m_value* _c_MEMB(_insert_after_node)(Self* self, _m_node* ref, _m_node* node); -STC_API _m_node* _c_MEMB(_unlink_after_node)(Self* self, _m_node* ref); -STC_API void _c_MEMB(_erase_after_node)(Self* self, _m_node* ref); -STC_INLINE _m_node* _c_MEMB(_get_node)(_m_value* pval) { return _clist_tonode(pval); } -STC_INLINE _m_node* _c_MEMB(_unlink_front_node)(Self* self) - { return _c_MEMB(_unlink_after_node)(self, self->last); } -#ifndef i_no_clone -STC_API Self _c_MEMB(_clone)(Self cx); -STC_INLINE _m_value _c_MEMB(_value_clone)(const Self* self, _m_value val) - { (void)self; return i_keyclone(val); } - -STC_INLINE void -_c_MEMB(_copy)(Self *self, const Self* other) { - if (self->last == other->last) return; - _c_MEMB(_drop)(self); - *self = _c_MEMB(_clone)(*other); -} -#endif // !i_no_clone - -#ifndef i_no_emplace -STC_INLINE _m_value* _c_MEMB(_emplace_back)(Self* self, _m_raw raw) - { return _c_MEMB(_push_back)(self, i_keyfrom(raw)); } -STC_INLINE _m_value* _c_MEMB(_emplace_front)(Self* self, _m_raw raw) - { return _c_MEMB(_push_front)(self, i_keyfrom(raw)); } -STC_INLINE _m_iter _c_MEMB(_emplace_at)(Self* self, _m_iter it, _m_raw raw) - { return _c_MEMB(_insert_at)(self, it, i_keyfrom(raw)); } -STC_INLINE _m_value* _c_MEMB(_emplace)(Self* self, _m_raw raw) - { return _c_MEMB(_push_back)(self, i_keyfrom(raw)); } -#endif // !i_no_emplace - -#ifndef _i_no_put -STC_INLINE void _c_MEMB(_put_n)(Self* self, const _m_raw* raw, isize n) - { while (n--) _c_MEMB(_push_back)(self, i_keyfrom((*raw))), ++raw; } -#endif - -#ifndef _i_aux_alloc - STC_INLINE Self _c_MEMB(_init)(void) { return c_literal(Self){0}; } - #ifndef _i_no_put - STC_INLINE Self _c_MEMB(_from_n)(const _m_raw* raw, isize n) - { Self cx = {0}; _c_MEMB(_put_n)(&cx, raw, n); return cx; } - #endif -#endif - -STC_INLINE bool _c_MEMB(_reserve)(Self* self, isize n) { (void)(self + n); return true; } -STC_INLINE bool _c_MEMB(_is_empty)(const Self* self) { return self->last == NULL; } -STC_INLINE void _c_MEMB(_clear)(Self* self) { _c_MEMB(_drop)(self); } -STC_INLINE _m_value* _c_MEMB(_push)(Self* self, _m_value value) - { return _c_MEMB(_push_back)(self, value); } -STC_INLINE void _c_MEMB(_pop_front)(Self* self) - { c_assert(!_c_MEMB(_is_empty)(self)); _c_MEMB(_erase_after_node)(self, self->last); } -STC_INLINE const _m_value* _c_MEMB(_front)(const Self* self) { return &self->last->next->value; } -STC_INLINE _m_value* _c_MEMB(_front_mut)(Self* self) { return &self->last->next->value; } -STC_INLINE const _m_value* _c_MEMB(_back)(const Self* self) { return &self->last->value; } -STC_INLINE _m_value* _c_MEMB(_back_mut)(Self* self) { return &self->last->value; } -STC_INLINE _m_raw _c_MEMB(_value_toraw)(const _m_value* pval) { return i_keytoraw(pval); } -STC_INLINE void _c_MEMB(_value_drop)(const Self* self, _m_value* pval) { (void)self; i_keydrop(pval); } - -STC_INLINE Self _c_MEMB(_move)(Self *self) { - Self m = *self; - self->last = NULL; - return m; -} - -STC_INLINE void _c_MEMB(_take)(Self *self, Self unowned) { - _c_MEMB(_drop)(self); - *self = unowned; -} - -STC_INLINE isize -_c_MEMB(_count)(const Self* self) { - isize n = 1; const _m_node *node = self->last; - if (node == NULL) return 0; - while ((node = node->next) != self->last) ++n; - return n; -} - -STC_INLINE _m_iter -_c_MEMB(_begin)(const Self* self) { - _m_value* head = self->last ? &self->last->next->value : NULL; - return c_literal(_m_iter){head, &self->last, self->last}; -} - -STC_INLINE _m_iter -_c_MEMB(_end)(const Self* self) - { (void)self; return c_literal(_m_iter){0}; } - -STC_INLINE void -_c_MEMB(_next)(_m_iter* it) { - _m_node* node = it->prev = _clist_tonode(it->ref); - it->ref = (node == *it->_last ? NULL : &node->next->value); -} - -STC_INLINE _m_iter -_c_MEMB(_advance)(_m_iter it, size_t n) { - while (n-- && it.ref) _c_MEMB(_next)(&it); - return it; -} - -STC_INLINE _m_iter -_c_MEMB(_splice_range)(Self* self, _m_iter it, - Self* other, _m_iter it1, _m_iter it2) { - Self tmp = _c_MEMB(_split_off)(other, it1, it2); - return _c_MEMB(_splice)(self, it, &tmp); -} - -#if defined _i_has_eq -STC_INLINE _m_iter -_c_MEMB(_find)(const Self* self, _m_raw val) { - return _c_MEMB(_find_in)(self, _c_MEMB(_begin)(self), _c_MEMB(_end)(self), val); -} - -STC_INLINE bool _c_MEMB(_eq)(const Self* self, const Self* other) { - _m_iter i = _c_MEMB(_begin)(self), j = _c_MEMB(_begin)(other); - for (; i.ref && j.ref; _c_MEMB(_next)(&i), _c_MEMB(_next)(&j)) { - const _m_raw _rx = i_keytoraw(i.ref), _ry = i_keytoraw(j.ref); - if (!(i_eq((&_rx), (&_ry)))) return false; - } - return !(i.ref || j.ref); -} -#endif - -// -------------------------- IMPLEMENTATION ------------------------- -#if defined i_implement - -#ifndef i_no_clone -STC_DEF Self -_c_MEMB(_clone)(Self lst) { - Self out = lst, *self = &out; (void)self; // may be used by i_keyclone via i_aux - out.last = NULL; - for (c_each(it, Self, lst)) - _c_MEMB(_push_back)(&out, i_keyclone((*it.ref))); - return out; -} -#endif - -STC_DEF void -_c_MEMB(_drop)(const Self* cself) { - Self* self = (Self*)cself; - while (self->last) - _c_MEMB(_erase_after_node)(self, self->last); -} - -STC_DEF _m_value* -_c_MEMB(_push_back)(Self* self, _m_value value) { - _c_list_insert_entry_after(self->last, value); - self->last = entry; - return &entry->value; -} - -STC_DEF _m_value* -_c_MEMB(_push_front)(Self* self, _m_value value) { - _c_list_insert_entry_after(self->last, value); - if (self->last == NULL) - self->last = entry; - return &entry->value; -} - -STC_DEF _m_value* -_c_MEMB(_push_back_node)(Self* self, _m_node* node) { - _c_list_insert_after_node(self->last, node); - self->last = node; - return &node->value; -} - -STC_DEF _m_value* -_c_MEMB(_insert_after_node)(Self* self, _m_node* ref, _m_node* node) { - _c_list_insert_after_node(ref, node); - if (self->last == NULL) - self->last = node; - return &node->value; -} - -STC_DEF _m_iter -_c_MEMB(_insert_at)(Self* self, _m_iter it, _m_value value) { - _m_node* node = it.ref ? it.prev : self->last; - _c_list_insert_entry_after(node, value); - if (self->last == NULL || it.ref == NULL) { - it.prev = self->last ? self->last : entry; - self->last = entry; - } - it.ref = &entry->value; - return it; -} - -STC_DEF _m_iter -_c_MEMB(_erase_at)(Self* self, _m_iter it) { - _m_node *node = _clist_tonode(it.ref); - it.ref = (node == self->last) ? NULL : &node->next->value; - _c_MEMB(_erase_after_node)(self, it.prev); - return it; -} - -STC_DEF _m_iter -_c_MEMB(_erase_range)(Self* self, _m_iter it1, _m_iter it2) { - _m_node *end = it2.ref ? _clist_tonode(it2.ref) : self->last->next; - if (it1.ref != it2.ref) do { - _c_MEMB(_erase_after_node)(self, it1.prev); - if (self->last == NULL) break; - } while (it1.prev->next != end); - return it2; -} - -STC_DEF void -_c_MEMB(_erase_after_node)(Self* self, _m_node* ref) { - _m_node* node = _c_MEMB(_unlink_after_node)(self, ref); - i_keydrop((&node->value)); - _i_free_n(node, 1); -} - -STC_DEF _m_node* -_c_MEMB(_unlink_after_node)(Self* self, _m_node* ref) { - _m_node* node = ref->next, *next = node->next; - ref->next = next; - if (node == next) - self->last = NULL; - else if (node == self->last) - self->last = ref; - return node; -} - -STC_DEF void -_c_MEMB(_reverse)(Self* self) { - Self rev = *self; - rev.last = NULL; - while (self->last) { - _m_node* node = _c_MEMB(_unlink_after_node)(self, self->last); - _c_MEMB(_insert_after_node)(&rev, rev.last, node); - } - *self = rev; -} - -STC_DEF _m_iter -_c_MEMB(_splice)(Self* self, _m_iter it, Self* other) { - if (self->last == NULL) - self->last = other->last; - else if (other->last) { - _m_node *p = it.ref ? it.prev : self->last, *next = p->next; - it.prev = other->last; - p->next = it.prev->next; - it.prev->next = next; - if (it.ref == NULL) self->last = it.prev; - } - other->last = NULL; - return it; -} - -STC_DEF Self -_c_MEMB(_split_off)(Self* self, _m_iter it1, _m_iter it2) { - Self lst = *self; - lst.last = NULL; - if (it1.ref == it2.ref) - return lst; - _m_node *p1 = it1.prev, - *p2 = it2.ref ? it2.prev : self->last; - p1->next = p2->next; - p2->next = _clist_tonode(it1.ref); - if (self->last == p2) - self->last = (p1 == p2) ? NULL : p1; - lst.last = p2; - return lst; -} - -#if defined _i_has_eq -STC_DEF _m_iter -_c_MEMB(_find_in)(const Self* self, _m_iter it1, _m_iter it2, _m_raw val) { - (void)self; - for (c_each(it, Self, it1, it2)) { - _m_raw r = i_keytoraw(it.ref); - if (i_eq((&r), (&val))) - return it; - } - it2.ref = NULL; return it2; -} - -STC_DEF isize -_c_MEMB(_remove)(Self* self, _m_raw val) { - isize n = 0; - _m_node *prev = self->last, *node; - if (prev) do { - node = prev->next; - _m_raw r = i_keytoraw((&node->value)); - if (i_eq((&r), (&val))) { - _c_MEMB(_erase_after_node)(self, prev), ++n; - if (self->last == NULL) break; - } else - prev = node; - } while (node != self->last); - return n; -} -#endif - -#if defined _i_has_cmp -#include "priv/sort_prv.h" - -STC_DEF bool _c_MEMB(_sort)(Self* self) { - isize len = 0, cap = 0; - _m_value *arr = NULL, *p = NULL; - _m_node* keep; - for (c_each(i, Self, *self)) { - if (len == cap) { - isize cap_n = cap + cap/2 + 8; - if ((p = (_m_value *)_i_realloc_n(arr, cap, cap_n)) == NULL) - goto done; - arr = p, cap = cap_n; - } - arr[len++] = *i.ref; - } - keep = self->last; - self->last = (_m_node *)arr; - _c_MEMB(_sort_lowhigh)(self, 0, len - 1); - self->last = keep; - for (c_each(i, Self, *self)) - *i.ref = *p++; - done: _i_free_n(arr, cap); - return p != NULL; -} -#endif // _i_has_cmp -#endif // i_implement -#undef _i_is_list -#include "sys/finalize.h" diff --git a/src/finchlite/codegen/stc/stc/pqueue.h b/src/finchlite/codegen/stc/stc/pqueue.h deleted file mode 100644 index dc952416..00000000 --- a/src/finchlite/codegen/stc/stc/pqueue.h +++ /dev/null @@ -1,185 +0,0 @@ -/* MIT License - * - * Copyright (c) 2025 Tyge Løvset - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include "priv/linkage.h" -#include "types.h" - -#ifndef STC_PQUEUE_H_INCLUDED -#define STC_PQUEUE_H_INCLUDED -#include "common.h" -#include -#endif // STC_PQUEUIE_H_INCLUDED - -#ifndef _i_prefix - #define _i_prefix pqueue_ -#endif -#define _i_sorted -#include "priv/template.h" -#ifndef i_declared - _c_DEFTYPES(_declare_stack, Self, i_key, _i_aux_def); -#endif -typedef i_keyraw _m_raw; - -STC_API void _c_MEMB(_make_heap)(Self* self); -STC_API void _c_MEMB(_erase_at)(Self* self, isize idx); -STC_API _m_value* _c_MEMB(_push)(Self* self, _m_value value); - -#ifndef _i_no_put -STC_INLINE void _c_MEMB(_put_n)(Self* self, const _m_raw* raw, isize n) - { while (n--) _c_MEMB(_push)(self, i_keyfrom((*raw))), ++raw; } -#endif - -STC_INLINE bool _c_MEMB(_reserve)(Self* self, const isize cap) { - if (cap != self->size && cap <= self->capacity) return true; - _m_value *d = (_m_value *)_i_realloc_n(self->data, self->capacity, cap); - return d ? (self->data = d, self->capacity = cap, true) : false; -} - -STC_INLINE void _c_MEMB(_shrink_to_fit)(Self* self) - { _c_MEMB(_reserve)(self, self->size); } - -#ifndef _i_aux_alloc -STC_INLINE Self _c_MEMB(_init)(void) - { return c_literal(Self){0}; } - -#ifndef _i_no_put -STC_INLINE Self _c_MEMB(_from_n)(const _m_raw* raw, isize n) - { Self cx = {0}; _c_MEMB(_put_n)(&cx, raw, n); return cx; } -#endif - -STC_INLINE Self _c_MEMB(_with_capacity)(const isize cap) - { Self cx = {0}; _c_MEMB(_reserve)(&cx, cap); return cx; } -#endif - -STC_INLINE void _c_MEMB(_clear)(Self* self) { - isize i = self->size; self->size = 0; - while (i--) { i_keydrop((self->data + i)); } -} - -STC_INLINE void _c_MEMB(_drop)(const Self* cself) { - Self* self = (Self*)cself; - _c_MEMB(_clear)(self); - _i_free_n(self->data, self->capacity); -} - -STC_INLINE Self _c_MEMB(_move)(Self *self) { - Self m = *self; - self->size = self->capacity = 0; - self->data = NULL; - return m; -} - -STC_INLINE void _c_MEMB(_take)(Self *self, Self unowned) { - _c_MEMB(_drop)(self); - *self = unowned; -} - -STC_INLINE isize _c_MEMB(_size)(const Self* q) - { return q->size; } - -STC_INLINE bool _c_MEMB(_is_empty)(const Self* q) - { return !q->size; } - -STC_INLINE isize _c_MEMB(_capacity)(const Self* q) - { return q->capacity; } - -STC_INLINE const _m_value* _c_MEMB(_top)(const Self* self) - { return &self->data[0]; } - -STC_INLINE void _c_MEMB(_pop)(Self* self) - { c_assert(!_c_MEMB(_is_empty)(self)); _c_MEMB(_erase_at)(self, 0); } - -STC_INLINE _m_value _c_MEMB(_pull)(Self* self) - { _m_value v = self->data[0]; _c_MEMB(_erase_at)(self, 0); return v; } - -#ifndef i_no_clone -STC_API Self _c_MEMB(_clone)(Self q); - -STC_INLINE void _c_MEMB(_copy)(Self *self, const Self* other) { - if (self == other) return; - _c_MEMB(_drop)(self); - *self = _c_MEMB(_clone)(*other); -} -STC_INLINE _m_value _c_MEMB(_value_clone)(const Self* self, _m_value val) - { (void)self; return i_keyclone(val); } -#endif // !i_no_clone - -#ifndef i_no_emplace -STC_INLINE void _c_MEMB(_emplace)(Self* self, _m_raw raw) - { _c_MEMB(_push)(self, i_keyfrom(raw)); } -#endif // !i_no_emplace - -/* -------------------------- IMPLEMENTATION ------------------------- */ -#if defined i_implement - -STC_DEF void -_c_MEMB(_sift_down_)(Self* self, const isize idx, const isize n) { - _m_value t, *arr = self->data - 1; - for (isize r = idx, c = idx*2; c <= n; c *= 2) { - c += i_less((&arr[c]), (&arr[c + (c < n)])); - if (!(i_less((&arr[r]), (&arr[c])))) return; - t = arr[r], arr[r] = arr[c], arr[r = c] = t; - } -} - -STC_DEF void -_c_MEMB(_make_heap)(Self* self) { - isize n = self->size; - for (isize k = n/2; k != 0; --k) - _c_MEMB(_sift_down_)(self, k, n); -} - -#ifndef i_no_clone -STC_DEF Self _c_MEMB(_clone)(Self q) { - Self out = q, *self = &out; (void)self; - out.capacity = out.size = 0; out.data = NULL; - _c_MEMB(_reserve)(&out, q.size); - out.size = q.size; - for (c_range(i, q.size)) - out.data[i] = i_keyclone(q.data[i]); - return out; -} -#endif - -STC_DEF void -_c_MEMB(_erase_at)(Self* self, const isize idx) { - i_keydrop((self->data + idx)); - const isize n = --self->size; - self->data[idx] = self->data[n]; - _c_MEMB(_sift_down_)(self, idx + 1, n); -} - -STC_DEF _m_value* -_c_MEMB(_push)(Self* self, _m_value value) { - if (self->size == self->capacity) - _c_MEMB(_reserve)(self, self->size*3/2 + 4); - _m_value *arr = self->data - 1; /* base 1 */ - isize c = ++self->size; - for (; c > 1 && (i_less((&arr[c/2]), (&value))); c /= 2) - arr[c] = arr[c/2]; - arr[c] = value; - return arr + c; -} -#endif - -#undef _i_sorted -#include "sys/finalize.h" diff --git a/src/finchlite/codegen/stc/stc/priv/cstr_prv.h b/src/finchlite/codegen/stc/stc/priv/cstr_prv.h deleted file mode 100644 index 40f58c94..00000000 --- a/src/finchlite/codegen/stc/stc/priv/cstr_prv.h +++ /dev/null @@ -1,420 +0,0 @@ -/* MIT License - * - * Copyright (c) 2025 Tyge Løvset - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -// IWYU pragma: private, include "stc/cstr.h" -#ifndef STC_CSTR_PRV_H_INCLUDED -#define STC_CSTR_PRV_H_INCLUDED - -#include /* FILE*, vsnprintf */ -#include /* malloc */ -#include /* size_t */ -#include /* cstr_vfmt() */ -/**************************** PRIVATE API **********************************/ - -#if defined __GNUC__ && !defined __clang__ - // linkage.h already does diagnostic push - // Warns wrongfully on -O3 on cstr_assign(&str, "literal longer than 23 ..."); - #pragma GCC diagnostic ignored "-Warray-bounds" -#endif - -enum { cstr_s_cap = sizeof(cstr_buf) - 2 }; -#define cstr_s_size(s) ((isize)(s)->sml.size) -#define cstr_s_set_size(s, len) ((s)->sml.data[(s)->sml.size = (uint8_t)(len)] = 0) -#define cstr_s_data(s) (s)->sml.data - -#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ - #define byte_rotl_(x, b) ((x) << (b)*8 | (x) >> (sizeof(x) - (b))*8) - #define cstr_l_cap(s) (isize)(~byte_rotl_((s)->lon.ncap, sizeof((s)->lon.ncap) - 1)) - #define cstr_l_set_cap(s, cap) ((s)->lon.ncap = ~byte_rotl_((uintptr_t)(cap), 1)) -#else - #define cstr_l_cap(s) (isize)(~(s)->lon.ncap) - #define cstr_l_set_cap(s, cap) ((s)->lon.ncap = ~(uintptr_t)(cap)) -#endif -#define cstr_l_size(s) (isize)((s)->lon.size) -#define cstr_l_set_size(s, len) ((s)->lon.data[(s)->lon.size = (uintptr_t)(len)] = 0) -#define cstr_l_data(s) (s)->lon.data -#define cstr_l_drop(s) c_free((s)->lon.data, cstr_l_cap(s) + 1) - -#define cstr_is_long(s) ((s)->sml.size >= 128) -extern char* _cstr_init(cstr* self, isize len, isize cap); -extern char* _cstr_internal_move(cstr* self, isize pos1, isize pos2); - -/**************************** PUBLIC API **********************************/ - -#define cstr_init() (c_literal(cstr){0}) -#define cstr_lit(literal) cstr_from_n(literal, c_litstrlen(literal)) - -extern cstr cstr_from_replace(csview sv, csview search, csview repl, int32_t count); -extern cstr cstr_from_fmt(const char* fmt, ...) c_GNUATTR(format(printf, 1, 2)); - -extern void cstr_drop(const cstr* self); -extern cstr* cstr_take(cstr* self, const cstr s); -extern char* cstr_reserve(cstr* self, isize cap); -extern void cstr_shrink_to_fit(cstr* self); -extern char* cstr_resize(cstr* self, isize size, char value); -extern isize cstr_find_at(const cstr* self, isize pos, const char* search); -extern isize cstr_find_sv(const cstr* self, csview search); -extern char* cstr_assign_n(cstr* self, const char* str, isize len); -extern char* cstr_append_n(cstr* self, const char* str, isize len); -extern isize cstr_append_fmt(cstr* self, const char* fmt, ...) c_GNUATTR(format(printf, 2, 3)); -extern char* cstr_append_uninit(cstr *self, isize len); - -extern bool cstr_getdelim(cstr *self, int delim, FILE *fp); -extern void cstr_erase(cstr* self, isize pos, isize len); -extern isize cstr_printf(cstr* self, const char* fmt, ...) c_GNUATTR(format(printf, 2, 3)); -extern isize cstr_vfmt(cstr* self, isize start, const char* fmt, va_list args); -extern size_t cstr_hash(const cstr *self); -extern bool cstr_u8_valid(const cstr* self); -extern void cstr_u8_erase(cstr* self, isize u8pos, isize u8len); - -STC_INLINE cstr_buf cstr_getbuf(cstr* s) { - return cstr_is_long(s) ? c_literal(cstr_buf){s->lon.data, cstr_l_size(s), cstr_l_cap(s)} - : c_literal(cstr_buf){s->sml.data, cstr_s_size(s), cstr_s_cap}; -} -STC_INLINE zsview cstr_zv(const cstr* s) { - return cstr_is_long(s) ? c_literal(zsview){s->lon.data, cstr_l_size(s)} - : c_literal(zsview){s->sml.data, cstr_s_size(s)}; -} -STC_INLINE csview cstr_sv(const cstr* s) { - return cstr_is_long(s) ? c_literal(csview){s->lon.data, cstr_l_size(s)} - : c_literal(csview){s->sml.data, cstr_s_size(s)}; -} - -STC_INLINE cstr cstr_from_n(const char* str, const isize len) { - cstr s; - c_memcpy(_cstr_init(&s, len, len), str, len); - return s; -} - -STC_INLINE cstr cstr_from(const char* str) - { return cstr_from_n(str, c_strlen(str)); } - -STC_INLINE cstr cstr_from_sv(csview sv) - { return cstr_from_n(sv.buf, sv.size); } - -STC_INLINE cstr cstr_from_zv(zsview zv) - { return cstr_from_n(zv.str, zv.size); } - -STC_INLINE cstr cstr_with_size(const isize size, const char value) { - cstr s; - c_memset(_cstr_init(&s, size, size), value, size); - return s; -} - -STC_INLINE cstr cstr_with_capacity(const isize cap) { - cstr s; - _cstr_init(&s, 0, cap); - return s; -} - -STC_INLINE cstr cstr_move(cstr* self) { - cstr tmp = *self; - *self = cstr_init(); - return tmp; -} - -STC_INLINE cstr cstr_clone(cstr s) { - csview sv = cstr_sv(&s); - return cstr_from_n(sv.buf, sv.size); -} - -#define SSO_CALL(s, call) (cstr_is_long(s) ? cstr_l_##call : cstr_s_##call) - -STC_INLINE void _cstr_set_size(cstr* self, isize len) - { SSO_CALL(self, set_size(self, len)); } - -STC_INLINE void cstr_clear(cstr* self) - { _cstr_set_size(self, 0); } - -STC_INLINE char* cstr_data(cstr* self) - { return SSO_CALL(self, data(self)); } - -STC_INLINE const char* cstr_str(const cstr* self) - { return SSO_CALL(self, data(self)); } - -STC_INLINE const char* cstr_toraw(const cstr* self) - { return SSO_CALL(self, data(self)); } - -STC_INLINE isize cstr_size(const cstr* self) - { return SSO_CALL(self, size(self)); } - -STC_INLINE bool cstr_is_empty(const cstr* self) - { return cstr_size(self) == 0; } - -STC_INLINE isize cstr_capacity(const cstr* self) - { return cstr_is_long(self) ? cstr_l_cap(self) : cstr_s_cap; } - -STC_INLINE isize cstr_to_index(const cstr* self, cstr_iter it) - { return it.ref - cstr_str(self); } - -STC_INLINE cstr cstr_from_s(cstr s, isize pos, isize len) - { return cstr_from_n(cstr_str(&s) + pos, len); } - -STC_INLINE csview cstr_subview(const cstr* self, isize pos, isize len) { - csview sv = cstr_sv(self); - c_assert(((size_t)pos <= (size_t)sv.size) & (len >= 0)); - if (pos + len > sv.size) len = sv.size - pos; - return c_literal(csview){sv.buf + pos, len}; -} - -STC_INLINE zsview cstr_tail(const cstr* self, isize len) { - c_assert(len >= 0); - csview sv = cstr_sv(self); - if (len > sv.size) len = sv.size; - return c_literal(zsview){&sv.buf[sv.size - len], len}; -} - -// BEGIN utf8 functions ===== - -STC_INLINE cstr cstr_u8_from(const char* str, isize u8pos, isize u8len) - { str = utf8_at(str, u8pos); return cstr_from_n(str, utf8_to_index(str, u8len)); } - -STC_INLINE isize cstr_u8_size(const cstr* self) - { return utf8_count(cstr_str(self)); } - -STC_INLINE isize cstr_u8_to_index(const cstr* self, isize u8pos) - { return utf8_to_index(cstr_str(self), u8pos); } - -STC_INLINE zsview cstr_u8_tail(const cstr* self, isize u8len) { - csview sv = cstr_sv(self); - const char* p = &sv.buf[sv.size]; - while (u8len && p != sv.buf) - u8len -= (*--p & 0xC0) != 0x80; - return c_literal(zsview){p, sv.size - (p - sv.buf)}; -} - -STC_INLINE csview cstr_u8_subview(const cstr* self, isize u8pos, isize u8len) - { return utf8_subview(cstr_str(self), u8pos, u8len); } - -STC_INLINE cstr_iter cstr_u8_at(const cstr* self, isize u8pos) { - csview sv; - sv.buf = utf8_at(cstr_str(self), u8pos); - sv.size = utf8_chr_size(sv.buf); - c_assert(sv.size); - return c_literal(cstr_iter){.chr = sv}; -} - -// utf8 iterator - -STC_INLINE cstr_iter cstr_begin(const cstr* self) { - csview sv = cstr_sv(self); - cstr_iter it = {.chr = {sv.buf, utf8_chr_size(sv.buf)}}; - return it; -} -STC_INLINE cstr_iter cstr_end(const cstr* self) { - (void)self; cstr_iter it = {0}; return it; -} -STC_INLINE void cstr_next(cstr_iter* it) { - it->ref += it->chr.size; - it->chr.size = utf8_chr_size(it->ref); - if (*it->ref == '\0') it->ref = NULL; -} - -STC_INLINE cstr_iter cstr_advance(cstr_iter it, isize u8pos) { - it.ref = utf8_offset(it.ref, u8pos); - it.chr.size = utf8_chr_size(it.ref); - if (*it.ref == '\0') it.ref = NULL; - return it; -} - -// utf8 case conversion: requires `#define i_import` before including cstr.h in one TU. -extern cstr cstr_tocase_sv(csview sv, int k); - -STC_INLINE cstr cstr_casefold_sv(csview sv) - { return cstr_tocase_sv(sv, 0); } - -STC_INLINE cstr cstr_tolower_sv(csview sv) - { return cstr_tocase_sv(sv, 1); } - -STC_INLINE cstr cstr_toupper_sv(csview sv) - { return cstr_tocase_sv(sv, 2); } - -STC_INLINE cstr cstr_tolower(const char* str) - { return cstr_tolower_sv(c_sv(str, c_strlen(str))); } - -STC_INLINE cstr cstr_toupper(const char* str) - { return cstr_toupper_sv(c_sv(str, c_strlen(str))); } - -STC_INLINE void cstr_lowercase(cstr* self) - { cstr_take(self, cstr_tolower_sv(cstr_sv(self))); } - -STC_INLINE void cstr_uppercase(cstr* self) - { cstr_take(self, cstr_toupper_sv(cstr_sv(self))); } - -STC_INLINE bool cstr_istarts_with(const cstr* self, const char* sub) { - csview sv = cstr_sv(self); - isize len = c_strlen(sub); - return len <= sv.size && !utf8_icompare((sv.size = len, sv), c_sv(sub, len)); -} - -STC_INLINE bool cstr_iends_with(const cstr* self, const char* sub) { - csview sv = cstr_sv(self); - isize len = c_strlen(sub); - return len <= sv.size && !utf8_icmp(sv.buf + sv.size - len, sub); -} - -STC_INLINE int cstr_icmp(const cstr* s1, const cstr* s2) - { return utf8_icmp(cstr_str(s1), cstr_str(s2)); } - -STC_INLINE bool cstr_ieq(const cstr* s1, const cstr* s2) { - csview x = cstr_sv(s1), y = cstr_sv(s2); - return x.size == y.size && !utf8_icompare(x, y); -} - -STC_INLINE bool cstr_iequals(const cstr* self, const char* str) - { return !utf8_icmp(cstr_str(self), str); } - -// END utf8 ===== - -STC_INLINE int cstr_cmp(const cstr* s1, const cstr* s2) - { return strcmp(cstr_str(s1), cstr_str(s2)); } - -STC_INLINE bool cstr_eq(const cstr* s1, const cstr* s2) { - csview x = cstr_sv(s1), y = cstr_sv(s2); - return x.size == y.size && !c_memcmp(x.buf, y.buf, x.size); -} - -STC_INLINE bool cstr_equals(const cstr* self, const char* str) - { return !strcmp(cstr_str(self), str); } - -STC_INLINE bool cstr_equals_sv(const cstr* self, csview sv) - { return sv.size == cstr_size(self) && !c_memcmp(cstr_str(self), sv.buf, sv.size); } - -STC_INLINE isize cstr_find(const cstr* self, const char* search) { - const char *str = cstr_str(self), *res = strstr((char*)str, search); - return res ? (res - str) : c_NPOS; -} - -STC_INLINE bool cstr_contains(const cstr* self, const char* search) - { return strstr((char*)cstr_str(self), search) != NULL; } - -STC_INLINE bool cstr_contains_sv(const cstr* self, csview search) - { return cstr_find_sv(self, search) != c_NPOS; } - - -STC_INLINE bool cstr_starts_with_sv(const cstr* self, csview sub) { - if (sub.size > cstr_size(self)) return false; - return !c_memcmp(cstr_str(self), sub.buf, sub.size); -} - -STC_INLINE bool cstr_starts_with(const cstr* self, const char* sub) { - const char* str = cstr_str(self); - while (*sub && *str == *sub) ++str, ++sub; - return !*sub; -} - -STC_INLINE bool cstr_ends_with_sv(const cstr* self, csview sub) { - csview sv = cstr_sv(self); - if (sub.size > sv.size) return false; - return !c_memcmp(sv.buf + sv.size - sub.size, sub.buf, sub.size); -} - -STC_INLINE bool cstr_ends_with(const cstr* self, const char* sub) - { return cstr_ends_with_sv(self, c_sv(sub, c_strlen(sub))); } - -STC_INLINE char* cstr_assign(cstr* self, const char* str) - { return cstr_assign_n(self, str, c_strlen(str)); } - -STC_INLINE char* cstr_assign_sv(cstr* self, csview sv) - { return cstr_assign_n(self, sv.buf, sv.size); } - -STC_INLINE char* cstr_copy(cstr* self, cstr s) { - csview sv = cstr_sv(&s); - return cstr_assign_n(self, sv.buf, sv.size); -} - - -STC_INLINE char* cstr_push(cstr* self, const char* chr) - { return cstr_append_n(self, chr, utf8_chr_size(chr)); } - -STC_INLINE void cstr_pop(cstr* self) { - csview sv = cstr_sv(self); - const char* s = sv.buf + sv.size; - while ((*--s & 0xC0) == 0x80) ; - _cstr_set_size(self, (s - sv.buf)); -} - -STC_INLINE char* cstr_append(cstr* self, const char* str) - { return cstr_append_n(self, str, c_strlen(str)); } - -STC_INLINE char* cstr_append_sv(cstr* self, csview sv) - { return cstr_append_n(self, sv.buf, sv.size); } - -STC_INLINE char* cstr_append_s(cstr* self, cstr s) - { return cstr_append_sv(self, cstr_sv(&s)); } - -#define cstr_join(self, sep, vec) do { \ - struct _vec_s { cstr* data; ptrdiff_t size; } \ - *_vec = (struct _vec_s*)&(vec); \ - (void)sizeof((vec).data == _vec->data && &(vec).size == &_vec->size); \ - cstr_join_sn(self, sep, _vec->data, _vec->size); \ -} while (0); - -#define cstr_join_items(self, sep, ...) \ - cstr_join_n(self, sep, c_make_array(const char*, __VA_ARGS__), c_sizeof((const char*[])__VA_ARGS__)/c_sizeof(char*)) - -STC_INLINE void cstr_join_n(cstr* self, const char* sep, const char* arr[], isize n) { - const char* _sep = cstr_is_empty(self) ? "" : sep; - while (n--) { cstr_append(self, _sep); cstr_append(self, *arr++); _sep = sep; } -} -STC_INLINE void cstr_join_sn(cstr* self, const char* sep, const cstr arr[], isize n) { - const char* _sep = cstr_is_empty(self) ? "" : sep; - while (n--) { cstr_append(self, _sep); cstr_append_s(self, *arr++); _sep = sep; } -} - - -STC_INLINE void cstr_replace_sv(cstr* self, csview search, csview repl, int32_t count) - { cstr_take(self, cstr_from_replace(cstr_sv(self), search, repl, count)); } - -STC_INLINE void cstr_replace_nfirst(cstr* self, const char* search, const char* repl, int32_t count) - { cstr_replace_sv(self, c_sv(search, c_strlen(search)), c_sv(repl, c_strlen(repl)), count); } - -STC_INLINE void cstr_replace(cstr* self, const char* search, const char* repl) - { cstr_replace_nfirst(self, search, repl, INT32_MAX); } - - -STC_INLINE void cstr_replace_at_sv(cstr* self, isize pos, isize len, const csview repl) { - char* d = _cstr_internal_move(self, pos + len, pos + repl.size); - c_memcpy(d + pos, repl.buf, repl.size); -} -STC_INLINE void cstr_replace_at(cstr* self, isize pos, isize len, const char* repl) - { cstr_replace_at_sv(self, pos, len, c_sv(repl, c_strlen(repl))); } - -STC_INLINE void cstr_u8_replace(cstr* self, isize u8pos, isize u8len, const char* repl) { - const char* s = cstr_str(self); csview span = utf8_subview(s, u8pos, u8len); - cstr_replace_at(self, span.buf - s, span.size, repl); -} - - -STC_INLINE void cstr_insert_sv(cstr* self, isize pos, csview sv) - { cstr_replace_at_sv(self, pos, 0, sv); } - -STC_INLINE void cstr_insert(cstr* self, isize pos, const char* str) - { cstr_replace_at_sv(self, pos, 0, c_sv(str, c_strlen(str))); } - -STC_INLINE void cstr_u8_insert(cstr* self, isize u8pos, const char* str) - { cstr_insert(self, utf8_to_index(cstr_str(self), u8pos), str); } - -STC_INLINE bool cstr_getline(cstr *self, FILE *fp) - { return cstr_getdelim(self, '\n', fp); } - -#endif // STC_CSTR_PRV_H_INCLUDED diff --git a/src/finchlite/codegen/stc/stc/priv/linkage.h b/src/finchlite/codegen/stc/stc/priv/linkage.h deleted file mode 100644 index ed18c729..00000000 --- a/src/finchlite/codegen/stc/stc/priv/linkage.h +++ /dev/null @@ -1,77 +0,0 @@ -/* MIT License - * - * Copyright (c) 2025 Tyge Løvset - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#undef STC_API -#undef STC_DEF - -#if !defined i_static && !defined STC_STATIC && (defined i_header || defined STC_HEADER || \ - defined i_implement || defined STC_IMPLEMENT) - #define STC_API extern - #define STC_DEF -#else - #define i_implement - #if defined __GNUC__ || defined __clang__ || defined __INTEL_LLVM_COMPILER - #define STC_API static __attribute__((unused)) - #else - #define STC_API static inline - #endif - #define STC_DEF static -#endif -#if defined STC_IMPLEMENT || defined i_import - #define i_implement -#endif - -#if defined i_aux && defined i_allocator - #define _i_aux_alloc -#endif -#ifndef i_allocator - #define i_allocator c -#endif -#ifndef i_free - #define i_malloc c_JOIN(i_allocator, _malloc) - #define i_calloc c_JOIN(i_allocator, _calloc) - #define i_realloc c_JOIN(i_allocator, _realloc) - #define i_free c_JOIN(i_allocator, _free) -#endif - -#if defined __clang__ && !defined __cplusplus - #pragma clang diagnostic push - #pragma clang diagnostic warning "-Wall" - #pragma clang diagnostic warning "-Wextra" - #pragma clang diagnostic warning "-Wpedantic" - #pragma clang diagnostic warning "-Wconversion" - #pragma clang diagnostic warning "-Wwrite-strings" - // ignored - #pragma clang diagnostic ignored "-Wmissing-field-initializers" -#elif defined __GNUC__ && !defined __cplusplus - #pragma GCC diagnostic push - #pragma GCC diagnostic warning "-Wall" - #pragma GCC diagnostic warning "-Wextra" - #pragma GCC diagnostic warning "-Wpedantic" - #pragma GCC diagnostic warning "-Wconversion" - #pragma GCC diagnostic warning "-Wwrite-strings" - // ignored - #pragma GCC diagnostic ignored "-Wclobbered" - #pragma GCC diagnostic ignored "-Wimplicit-fallthrough=3" - #pragma GCC diagnostic ignored "-Wstringop-overflow=" - #pragma GCC diagnostic ignored "-Wmissing-field-initializers" -#endif diff --git a/src/finchlite/codegen/stc/stc/priv/linkage2.h b/src/finchlite/codegen/stc/stc/priv/linkage2.h deleted file mode 100644 index d99dd239..00000000 --- a/src/finchlite/codegen/stc/stc/priv/linkage2.h +++ /dev/null @@ -1,42 +0,0 @@ -/* MIT License - * - * Copyright (c) 2025 Tyge Løvset - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#undef i_aux -#undef _i_aux_alloc - -#undef i_allocator -#undef i_malloc -#undef i_calloc -#undef i_realloc -#undef i_free - -#undef i_static -#undef i_header -#undef i_implement -#undef i_import - -#if defined __clang__ && !defined __cplusplus - #pragma clang diagnostic pop -#elif defined __GNUC__ && !defined __cplusplus - #pragma GCC diagnostic pop -#endif diff --git a/src/finchlite/codegen/stc/stc/priv/queue_prv.h b/src/finchlite/codegen/stc/stc/priv/queue_prv.h deleted file mode 100644 index b1d46c60..00000000 --- a/src/finchlite/codegen/stc/stc/priv/queue_prv.h +++ /dev/null @@ -1,291 +0,0 @@ -/* MIT License - * - * Copyright (c) 2025 Tyge Løvset - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -// IWYU pragma: private -#ifndef i_declared -_c_DEFTYPES(_declare_queue, Self, i_key, _i_aux_def); -#endif -typedef i_keyraw _m_raw; - -STC_API bool _c_MEMB(_reserve)(Self* self, const isize cap); -STC_API void _c_MEMB(_clear)(Self* self); -STC_API void _c_MEMB(_drop)(const Self* cself); -STC_API _m_value* _c_MEMB(_push)(Self* self, _m_value value); // push_back -STC_API void _c_MEMB(_shrink_to_fit)(Self *self); -STC_API _m_iter _c_MEMB(_advance)(_m_iter it, isize n); - -#define _cbuf_toidx(self, pos) (((pos) - (self)->start) & (self)->capmask) -#define _cbuf_topos(self, idx) (((self)->start + (idx)) & (self)->capmask) - -#ifndef _i_no_put -STC_INLINE void _c_MEMB(_put_n)(Self* self, const _m_raw* raw, isize n) - { while (n--) _c_MEMB(_push)(self, i_keyfrom((*raw))), ++raw; } -#endif - -STC_INLINE void _c_MEMB(_value_drop)(const Self* self, _m_value* val) - { (void)self; i_keydrop(val); } - -#ifndef _i_aux_alloc - STC_INLINE Self _c_MEMB(_init)(void) - { Self out = {0}; return out; } - - STC_INLINE Self _c_MEMB(_with_capacity)(isize cap) { - cap = c_next_pow2(cap + 1); - Self out = {_i_new_n(_m_value, cap), 0, 0, cap - 1}; - return out; - } - - STC_INLINE Self _c_MEMB(_with_size_uninit)(isize size) - { Self out = _c_MEMB(_with_capacity)(size); out.end = size; return out; } - - STC_INLINE Self _c_MEMB(_with_size)(isize size, _m_raw default_raw) { - Self out = _c_MEMB(_with_capacity)(size); - while (out.end < size) out.cbuf[out.end++] = i_keyfrom(default_raw); - return out; - } - - #ifndef _i_no_put - STC_INLINE Self _c_MEMB(_from_n)(const _m_raw* raw, isize n) { - Self out = _c_MEMB(_with_capacity)(n); - _c_MEMB(_put_n)(&out, raw, n); return out; - } - #endif -#endif - -#ifndef i_no_emplace -STC_INLINE _m_value* _c_MEMB(_emplace)(Self* self, _m_raw raw) - { return _c_MEMB(_push)(self, i_keyfrom(raw)); } -#endif - -#if defined _i_has_eq -STC_API bool _c_MEMB(_eq)(const Self* self, const Self* other); -#endif - -#ifndef i_no_clone -STC_API Self _c_MEMB(_clone)(Self q); - -STC_INLINE _m_value _c_MEMB(_value_clone)(const Self* self, _m_value val) - { (void)self; return i_keyclone(val); } - -STC_INLINE void _c_MEMB(_copy)(Self* self, const Self* other) { - if (self == other) return; - _c_MEMB(_drop)(self); - *self = _c_MEMB(_clone)(*other); -} -#endif // !i_no_clone - -STC_INLINE isize _c_MEMB(_size)(const Self* self) - { return _cbuf_toidx(self, self->end); } - -STC_INLINE isize _c_MEMB(_capacity)(const Self* self) - { return self->capmask; } - -STC_INLINE bool _c_MEMB(_is_empty)(const Self* self) - { return self->start == self->end; } - -STC_INLINE _m_raw _c_MEMB(_value_toraw)(const _m_value* pval) - { return i_keytoraw(pval); } - -STC_INLINE const _m_value* _c_MEMB(_front)(const Self* self) - { return self->cbuf + self->start; } - -STC_INLINE _m_value* _c_MEMB(_front_mut)(Self* self) - { return self->cbuf + self->start; } - -STC_INLINE const _m_value* _c_MEMB(_back)(const Self* self) - { return self->cbuf + ((self->end - 1) & self->capmask); } - -STC_INLINE _m_value* _c_MEMB(_back_mut)(Self* self) - { return (_m_value*)_c_MEMB(_back)(self); } - -STC_INLINE Self _c_MEMB(_move)(Self *self) { - Self m = *self; - self->capmask = self->start = self->end = 0; - self->cbuf = NULL; - return m; -} - -STC_INLINE void _c_MEMB(_take)(Self *self, Self unowned) - { _c_MEMB(_drop)(self); *self = unowned; } - -STC_INLINE void _c_MEMB(_pop)(Self* self) { // pop_front - c_assert(!_c_MEMB(_is_empty)(self)); - i_keydrop((self->cbuf + self->start)); - self->start = (self->start + 1) & self->capmask; -} - -STC_INLINE _m_value _c_MEMB(_pull)(Self* self) { // move front out of queue - c_assert(!_c_MEMB(_is_empty)(self)); - isize s = self->start; - self->start = (s + 1) & self->capmask; - return self->cbuf[s]; -} - -STC_INLINE _m_iter _c_MEMB(_begin)(const Self* self) { - return c_literal(_m_iter){ - .ref=_c_MEMB(_is_empty)(self) ? NULL : self->cbuf + self->start, - .pos=self->start, ._s=self}; -} - -STC_INLINE _m_iter _c_MEMB(_rbegin)(const Self* self) { - isize pos = (self->end - 1) & self->capmask; - return c_literal(_m_iter){ - .ref=_c_MEMB(_is_empty)(self) ? NULL : self->cbuf + pos, - .pos=pos, ._s=self}; -} - -STC_INLINE _m_iter _c_MEMB(_end)(const Self* self) - { (void)self; return c_literal(_m_iter){0}; } - -STC_INLINE _m_iter _c_MEMB(_rend)(const Self* self) - { (void)self; return c_literal(_m_iter){0}; } - -STC_INLINE void _c_MEMB(_next)(_m_iter* it) { - if (it->pos != it->_s->capmask) { ++it->ref; ++it->pos; } - else { it->ref -= it->pos; it->pos = 0; } - if (it->pos == it->_s->end) it->ref = NULL; -} - -STC_INLINE void _c_MEMB(_rnext)(_m_iter* it) { - if (it->pos == it->_s->start) it->ref = NULL; - else if (it->pos != 0) { --it->ref; --it->pos; } - else it->ref += (it->pos = it->_s->capmask); -} - -STC_INLINE isize _c_MEMB(_index)(const Self* self, _m_iter it) - { return _cbuf_toidx(self, it.pos); } - -STC_INLINE void _c_MEMB(_adjust_end_)(Self* self, isize n) - { self->end = (self->end + n) & self->capmask; } - -/* -------------------------- IMPLEMENTATION ------------------------- */ -#if defined i_implement - -STC_DEF _m_iter _c_MEMB(_advance)(_m_iter it, isize n) { - isize len = _c_MEMB(_size)(it._s); - isize pos = it.pos, idx = _cbuf_toidx(it._s, pos); - it.pos = (pos + n) & it._s->capmask; - it.ref += it.pos - pos; - if (!c_uless(idx + n, len)) it.ref = NULL; - return it; -} - -STC_DEF void -_c_MEMB(_clear)(Self* self) { - for (c_each(i, Self, *self)) - { i_keydrop(i.ref); } - self->start = 0, self->end = 0; -} - -STC_DEF void -_c_MEMB(_drop)(const Self* cself) { - Self* self = (Self*)cself; - _c_MEMB(_clear)(self); - _i_free_n(self->cbuf, self->capmask + 1); -} - -STC_DEF bool -_c_MEMB(_reserve)(Self* self, const isize cap) { - isize oldpow2 = self->capmask + (self->capmask & 1); // handle capmask = 0 - isize newpow2 = c_next_pow2(cap + 1); - if (newpow2 <= oldpow2) - return self->cbuf != NULL; - _m_value* d = (_m_value *)_i_realloc_n(self->cbuf, oldpow2, newpow2); - if (d == NULL) - return false; - isize head = oldpow2 - self->start; - if (self->start <= self->end) // [..S########E....|................] - ; - else if (head < self->end) { // [#######E.....S##|.............s!!] - c_memcpy(d + newpow2 - head, d + self->start, head*c_sizeof *d); - self->start = newpow2 - head; - } else { // [##E.....S#######|!!e.............] - c_memcpy(d + oldpow2, d, self->end*c_sizeof *d); - self->end += oldpow2; - } - self->capmask = newpow2 - 1; - self->cbuf = d; - return true; -} - -STC_DEF _m_value* -_c_MEMB(_push)(Self* self, _m_value value) { // push_back - isize end = (self->end + 1) & self->capmask; - if (end == self->start) { // full - if (!_c_MEMB(_reserve)(self, self->capmask + 3)) // => 2x expand - return NULL; - end = (self->end + 1) & self->capmask; - } - _m_value *v = self->cbuf + self->end; - self->end = end; - *v = value; - return v; -} - -STC_DEF void -_c_MEMB(_shrink_to_fit)(Self *self) { - isize sz = _c_MEMB(_size)(self); - isize newpow2 = c_next_pow2(sz + 1); - if (newpow2 > self->capmask) - return; - if (self->start <= self->end) { - c_memmove(self->cbuf, self->cbuf + self->start, sz*c_sizeof *self->cbuf); - self->start = 0, self->end = sz; - } else { - isize n = self->capmask - self->start + 1; - c_memmove(self->cbuf + (newpow2 - n), self->cbuf + self->start, n*c_sizeof *self->cbuf); - self->start = newpow2 - n; - } - self->cbuf = (_m_value *)_i_realloc_n(self->cbuf, self->capmask + 1, newpow2); - self->capmask = newpow2 - 1; -} - -#ifndef i_no_clone -STC_DEF Self -_c_MEMB(_clone)(Self q) { - Self out = q, *self = &out; (void)self; // may be used by _i_new_n/i_keyclone via i_aux. - out.start = 0; out.end = _c_MEMB(_size)(&q); - out.capmask = c_next_pow2(out.end + 1) - 1; - out.cbuf = _i_new_n(_m_value, out.capmask + 1); - isize i = 0; - if (out.cbuf) - for (c_each(it, Self, q)) - out.cbuf[i++] = i_keyclone((*it.ref)); - return out; -} -#endif // i_no_clone - -#if defined _i_has_eq -STC_DEF bool -_c_MEMB(_eq)(const Self* self, const Self* other) { - if (_c_MEMB(_size)(self) != _c_MEMB(_size)(other)) return false; - for (_m_iter i = _c_MEMB(_begin)(self), j = _c_MEMB(_begin)(other); - i.ref; _c_MEMB(_next)(&i), _c_MEMB(_next)(&j)) - { - const _m_raw _rx = i_keytoraw(i.ref), _ry = i_keytoraw(j.ref); - if (!(i_eq((&_rx), (&_ry)))) return false; - } - return true; -} -#endif // _i_has_eq -#endif // IMPLEMENTATION diff --git a/src/finchlite/codegen/stc/stc/priv/sort_prv.h b/src/finchlite/codegen/stc/stc/priv/sort_prv.h deleted file mode 100644 index 6a9f5098..00000000 --- a/src/finchlite/codegen/stc/stc/priv/sort_prv.h +++ /dev/null @@ -1,136 +0,0 @@ -/* MIT License - * - * Copyright (c) 2025 Tyge Løvset - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -// IWYU pragma: private -#ifdef _i_is_list - #define i_at(self, idx) (&((_m_value *)(self)->last)[idx]) - #define i_at_mut i_at -#elif !defined i_at - #define i_at(self, idx) _c_MEMB(_at)(self, idx) - #define i_at_mut(self, idx) _c_MEMB(_at_mut)(self, idx) -#endif - -STC_API void _c_MEMB(_sort_lowhigh)(Self* self, isize lo, isize hi); - -#ifdef _i_is_array -STC_API isize _c_MEMB(_lower_bound_range)(const Self* self, const _m_raw raw, isize start, isize end); -STC_API isize _c_MEMB(_binary_search_range)(const Self* self, const _m_raw raw, isize start, isize end); - -static inline void _c_MEMB(_sort)(Self* arr, isize n) - { _c_MEMB(_sort_lowhigh)(arr, 0, n - 1); } - -static inline isize // c_NPOS = not found -_c_MEMB(_lower_bound)(const Self* arr, const _m_raw raw, isize n) - { return _c_MEMB(_lower_bound_range)(arr, raw, 0, n); } - -static inline isize // c_NPOS = not found -_c_MEMB(_binary_search)(const Self* arr, const _m_raw raw, isize n) - { return _c_MEMB(_binary_search_range)(arr, raw, 0, n); } - -#elif !defined _i_is_list -STC_API isize _c_MEMB(_lower_bound_range)(const Self* self, const _m_raw raw, isize start, isize end); -STC_API isize _c_MEMB(_binary_search_range)(const Self* self, const _m_raw raw, isize start, isize end); - -static inline void _c_MEMB(_sort)(Self* self) - { _c_MEMB(_sort_lowhigh)(self, 0, _c_MEMB(_size)(self) - 1); } - -static inline isize // c_NPOS = not found -_c_MEMB(_lower_bound)(const Self* self, const _m_raw raw) - { return _c_MEMB(_lower_bound_range)(self, raw, 0, _c_MEMB(_size)(self)); } - -static inline isize // c_NPOS = not found -_c_MEMB(_binary_search)(const Self* self, const _m_raw raw) - { return _c_MEMB(_binary_search_range)(self, raw, 0, _c_MEMB(_size)(self)); } -#endif - -/* -------------------------- IMPLEMENTATION ------------------------- */ -#if defined i_implement - -static void _c_MEMB(_insertsort_lowhigh)(Self* self, isize lo, isize hi) { - for (isize j = lo, i = lo + 1; i <= hi; j = i, ++i) { - _m_value x = *i_at(self, i); - _m_raw rx = i_keytoraw((&x)); - while (j >= 0) { - _m_raw ry = i_keytoraw(i_at(self, j)); - if (!(i_less((&rx), (&ry)))) break; - *i_at_mut(self, j + 1) = *i_at(self, j); - --j; - } - *i_at_mut(self, j + 1) = x; - } -} - -STC_DEF void _c_MEMB(_sort_lowhigh)(Self* self, isize lo, isize hi) { - isize i = lo, j; - while (lo < hi) { - _m_raw pivot = i_keytoraw(i_at(self, (isize)(lo + (hi - lo)*7LL/16))), rx; - j = hi; - do { - do { rx = i_keytoraw(i_at(self, i)); } while ((i_less((&rx), (&pivot))) && ++i); - do { rx = i_keytoraw(i_at(self, j)); } while ((i_less((&pivot), (&rx))) && --j); - if (i > j) break; - c_swap(i_at_mut(self, i), i_at_mut(self, j)); - ++i; --j; - } while (i <= j); - - if (j - lo > hi - i) { - c_swap(&lo, &i); - c_swap(&hi, &j); - } - if (j - lo > 64) _c_MEMB(_sort_lowhigh)(self, lo, j); - else if (j > lo) _c_MEMB(_insertsort_lowhigh)(self, lo, j); - lo = i; - } -} - -#ifndef _i_is_list -STC_DEF isize // c_NPOS = not found -_c_MEMB(_lower_bound_range)(const Self* self, const _m_raw raw, isize start, isize end) { - isize count = end - start, step = count/2; - while (count > 0) { - const _m_raw rx = i_keytoraw(i_at(self, start + step)); - if (i_less((&rx), (&raw))) { - start += step + 1; - count -= step + 1; - step = count*7/8; - } else { - count = step; - step = count/8; - } - } - return start >= end ? c_NPOS : start; -} - -STC_DEF isize // c_NPOS = not found -_c_MEMB(_binary_search_range)(const Self* self, const _m_raw raw, isize start, isize end) { - isize res = _c_MEMB(_lower_bound_range)(self, raw, start, end); - if (res != c_NPOS) { - const _m_raw rx = i_keytoraw(i_at(self, res)); - if (i_less((&raw), (&rx))) res = c_NPOS; - } - return res; -} -#endif // !_i_is_list -#endif // IMPLEMENTATION -#undef i_at -#undef i_at_mut diff --git a/src/finchlite/codegen/stc/stc/priv/template.h b/src/finchlite/codegen/stc/stc/priv/template.h deleted file mode 100644 index 86211456..00000000 --- a/src/finchlite/codegen/stc/stc/priv/template.h +++ /dev/null @@ -1,301 +0,0 @@ -/* MIT License - * - * Copyright (c) 2025 Tyge Løvset - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -// IWYU pragma: private -#ifndef _i_template -#define _i_template - -#ifndef STC_TEMPLATE_H_INCLUDED -#define STC_TEMPLATE_H_INCLUDED - - #define _c_MEMB(name) c_JOIN(Self, name) - #define _c_DEFTYPES(macro, SELF, ...) macro(SELF, __VA_ARGS__) - #define _m_value _c_MEMB(_value) - #define _m_key _c_MEMB(_key) - #define _m_mapped _c_MEMB(_mapped) - #define _m_rmapped _c_MEMB(_rmapped) - #define _m_raw _c_MEMB(_raw) - #define _m_keyraw _c_MEMB(_keyraw) - #define _m_iter _c_MEMB(_iter) - #define _m_result _c_MEMB(_result) - #define _m_node _c_MEMB(_node) - - #define c_OPTION(flag) ((i_opt) & (flag)) - #define c_declared (1<<0) - #define c_no_atomic (1<<1) - #define c_arc2 (1<<2) - #define c_no_clone (1<<3) - #define c_no_hash (1<<4) - #define c_use_cmp (1<<5) - #define c_use_eq (1<<6) - #define c_cmpclass (1<<7) - #define c_keyclass (1<<8) - #define c_valclass (1<<9) - #define c_keypro (1<<10) - #define c_valpro (1<<11) -#endif - -#if defined i_rawclass // [deprecated] - #define i_cmpclass i_rawclass -#endif - -#if defined T && !defined i_type - #define i_type T -#endif -#if defined i_type && c_NUMARGS(i_type) > 1 - #define Self c_GETARG(1, i_type) - #define i_key c_GETARG(2, i_type) - #if c_NUMARGS(i_type) == 3 - #if defined _i_is_map - #define i_val c_GETARG(3, i_type) - #else - #define i_opt c_GETARG(3, i_type) - #endif - #elif c_NUMARGS(i_type) == 4 - #define i_val c_GETARG(3, i_type) - #define i_opt c_GETARG(4, i_type) - #endif -#elif !defined Self && defined i_type - #define Self i_type -#elif !defined Self - #define Self c_JOIN(_i_prefix, i_tag) -#endif - -#if defined i_aux && c_NUMARGS(i_aux) == 2 - // shorthand for defining i_aux AND i_allocator as a one-liner combo. - #define _i_aux_alloc - #define _i_aux_def c_GETARG(1, i_aux) aux; - #undef i_allocator // override: - #define i_allocator c_GETARG(2, i_aux) -#elif defined i_aux - #define _i_aux_def i_aux aux; -#else - #define _i_aux_def -#endif - -#if c_OPTION(c_declared) - #define i_declared -#endif -#if c_OPTION(c_no_hash) - #define i_no_hash -#endif -#if c_OPTION(c_use_cmp) - #define i_use_cmp -#endif -#if c_OPTION(c_use_eq) - #define i_use_eq -#endif -#if c_OPTION(c_no_clone) || defined _i_is_arc - #define i_no_clone -#endif -#if c_OPTION(c_keyclass) - #define i_keyclass i_key -#endif -#if c_OPTION(c_valclass) - #define i_valclass i_val -#endif -#if c_OPTION(c_cmpclass) - #define i_cmpclass i_key - #define i_use_cmp -#endif -#if c_OPTION(c_keypro) - #define i_keypro i_key -#endif -#if c_OPTION(c_valpro) - #define i_valpro i_val -#endif - -#if defined i_keypro - #define i_keyclass i_keypro - #define i_cmpclass c_JOIN(i_keypro, _raw) -#endif - -#if defined i_cmpclass - #define i_keyraw i_cmpclass -#elif defined i_keyclass && !defined i_keyraw - // When only i_keyclass is defined, we also define i_cmpclass to the same. - // We do not define i_keyraw here, otherwise _from() / _toraw() is expected to exist. - #define i_cmpclass i_key -#elif defined i_keyraw && !defined i_keyfrom - // Define _i_no_put when i_keyfrom is not explicitly defined and i_keyraw is. - // In this case, i_keytoraw needs to be defined (may be done later in this file). - #define _i_no_put -#endif - -// Bind to i_key "class members": _clone, _drop, _from and _toraw (when conditions are met). -#if defined i_keyclass - #ifndef i_key - #define i_key i_keyclass - #endif - #if !defined i_keyclone && !defined i_no_clone - #define i_keyclone c_JOIN(i_keyclass, _clone) - #endif - #ifndef i_keydrop - #define i_keydrop c_JOIN(i_keyclass, _drop) - #endif - #if !defined i_keyfrom && defined i_keyraw - #define i_keyfrom c_JOIN(i_keyclass, _from) - #endif - #if !defined i_keytoraw && defined i_keyraw - #define i_keytoraw c_JOIN(i_keyclass, _toraw) - #endif -#endif - -// Define when container has support for sorting (cmp) and linear search (eq) -#if defined i_use_cmp || defined i_cmp || defined i_less - #define _i_has_cmp -#endif -#if defined i_use_cmp || defined i_cmp || defined i_use_eq || defined i_eq - #define _i_has_eq -#endif - -// Bind to i_cmpclass "class members": _cmp, _eq and _hash (when conditions are met). -#if defined i_cmpclass - #if !(defined i_cmp || defined i_less) && (defined i_use_cmp || defined _i_sorted) - #define i_cmp c_JOIN(i_cmpclass, _cmp) - #endif - #if !defined i_eq && (defined i_use_eq || defined i_hash || defined _i_is_hash) - #define i_eq c_JOIN(i_cmpclass, _eq) - #endif - #if !(defined i_hash || defined i_no_hash) - #define i_hash c_JOIN(i_cmpclass, _hash) - #endif -#endif - -#if !defined i_key - #error "No i_key defined" -#elif defined i_keyraw && !(c_OPTION(c_cmpclass) || defined i_keytoraw) - #error "If i_cmpclass / i_keyraw is defined, i_keytoraw must be defined too" -#elif !defined i_no_clone && (defined i_keyclone ^ defined i_keydrop) - #error "Both i_keyclone and i_keydrop must be defined, if any (unless i_no_clone defined)." -#elif defined i_from || defined i_drop - #error "i_from / i_drop not supported. Use i_keyfrom/i_keydrop" -#elif defined i_keyto || defined i_valto - #error i_keyto / i_valto not supported. Use i_keytoraw / i_valtoraw -#elif defined i_keyraw && defined i_use_cmp && !defined _i_has_cmp - #error "For smap / sset / pqueue, i_cmp or i_less must be defined when i_keyraw is defined." -#endif - -// Fill in missing i_eq, i_less, i_cmp functions with defaults. -#if !defined i_eq && defined i_cmp - #define i_eq(x, y) (i_cmp(x, y)) == 0 -#elif !defined i_eq - #define i_eq(x, y) *x == *y // works for integral types -#endif -#if !defined i_less && defined i_cmp - #define i_less(x, y) (i_cmp(x, y)) < 0 -#elif !defined i_less - #define i_less(x, y) *x < *y // works for integral types -#endif -#if !defined i_cmp && defined i_less - #define i_cmp(x, y) (i_less(y, x)) - (i_less(x, y)) -#endif -#if !(defined i_hash || defined i_no_hash) - #define i_hash c_default_hash -#endif - -#define i_no_emplace - -#ifndef i_tag - #define i_tag i_key -#endif -#ifndef i_keyfrom - #define i_keyfrom c_default_clone -#else - #undef i_no_emplace -#endif -#ifndef i_keyraw - #define i_keyraw i_key -#endif -#ifndef i_keytoraw - #define i_keytoraw c_default_toraw -#endif -#ifndef i_keyclone - #define i_keyclone c_default_clone -#endif -#ifndef i_keydrop - #define i_keydrop c_default_drop -#endif - -#if defined _i_is_map // ---- process hashmap/sortedmap value i_val, ... ---- - -#if defined i_valpro - #define i_valclass i_valpro - #define i_valraw c_JOIN(i_valpro, _raw) -#endif - -#ifdef i_valclass - #ifndef i_val - #define i_val i_valclass - #endif - #if !defined i_valclone && !defined i_no_clone - #define i_valclone c_JOIN(i_valclass, _clone) - #endif - #ifndef i_valdrop - #define i_valdrop c_JOIN(i_valclass, _drop) - #endif - #if !defined i_valfrom && defined i_valraw - #define i_valfrom c_JOIN(i_valclass, _from) - #endif - #if !defined i_valtoraw && defined i_valraw - #define i_valtoraw c_JOIN(i_valclass, _toraw) - #endif -#endif - -#ifndef i_val - #error "i_val* must be defined for maps" -#elif defined i_valraw && !defined i_valtoraw - #error "If i_valraw is defined, i_valtoraw must be defined too" -#elif !defined i_no_clone && (defined i_valclone ^ defined i_valdrop) - #error "Both i_valclone and i_valdrop must be defined, if any" -#endif - -#ifndef i_valfrom - #define i_valfrom c_default_clone - #ifdef i_valraw - #define _i_no_put - #endif -#else - #undef i_no_emplace -#endif -#ifndef i_valraw - #define i_valraw i_val -#endif -#ifndef i_valtoraw - #define i_valtoraw c_default_toraw -#endif -#ifndef i_valclone - #define i_valclone c_default_clone -#endif -#ifndef i_valdrop - #define i_valdrop c_default_drop -#endif - -#endif // !_i_is_map - -#ifndef i_val - #define i_val i_key -#endif -#ifndef i_valraw - #define i_valraw i_keyraw -#endif -#endif // STC_TEMPLATE_H_INCLUDED diff --git a/src/finchlite/codegen/stc/stc/priv/template2.h b/src/finchlite/codegen/stc/stc/priv/template2.h deleted file mode 100644 index fc5cc8f2..00000000 --- a/src/finchlite/codegen/stc/stc/priv/template2.h +++ /dev/null @@ -1,72 +0,0 @@ -/* MIT License - * - * Copyright (c) 2025 Tyge Løvset - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -// IWYU pragma: private -#undef T // alias for i_type -#undef i_type -#undef i_class -#undef i_tag -#undef i_opt -#undef i_capacity - -#undef i_key -#undef i_keypro // Replaces next two -#undef i_key_str // [deprecated] -#undef i_key_arcbox // [deprecated] -#undef i_keyclass -#undef i_cmpclass // define i_keyraw, and bind i_cmp, i_eq, i_hash "class members" -#undef i_rawclass // [deprecated] for i_cmpclass -#undef i_keyclone -#undef i_keydrop -#undef i_keyraw -#undef i_keyfrom -#undef i_keytoraw -#undef i_cmp -#undef i_less -#undef i_eq -#undef i_hash - -#undef i_val -#undef i_valpro // Replaces next two -#undef i_val_str // [deprecated] -#undef i_val_arcbox // [deprecated] -#undef i_valclass -#undef i_valclone -#undef i_valdrop -#undef i_valraw -#undef i_valfrom -#undef i_valtoraw - -#undef i_use_cmp -#undef i_use_eq -#undef i_no_hash -#undef i_no_clone -#undef i_no_emplace -#undef i_declared - -#undef _i_no_put -#undef _i_aux_def -#undef _i_has_cmp -#undef _i_has_eq -#undef _i_prefix -#undef _i_template -#undef Self diff --git a/src/finchlite/codegen/stc/stc/priv/utf8_prv.h b/src/finchlite/codegen/stc/stc/priv/utf8_prv.h deleted file mode 100644 index 1a5e6154..00000000 --- a/src/finchlite/codegen/stc/stc/priv/utf8_prv.h +++ /dev/null @@ -1,192 +0,0 @@ -/* MIT License - * - * Copyright (c) 2025 Tyge Løvset - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -// IWYU pragma: private, include "stc/utf8.h" -#ifndef STC_UTF8_PRV_H_INCLUDED -#define STC_UTF8_PRV_H_INCLUDED - -#include - -// The following functions assume valid utf8 strings: - -/* number of bytes in the utf8 codepoint from s */ -STC_INLINE int utf8_chr_size(const char *s) { - unsigned b = (uint8_t)*s; - if (b < 0x80) return 1; - /*if (b < 0xC2) return 0;*/ - if (b < 0xE0) return 2; - if (b < 0xF0) return 3; - /*if (b < 0xF5)*/ return 4; - /*return 0;*/ -} - -/* number of codepoints in the utf8 string s */ -STC_INLINE isize utf8_count(const char *s) { - isize size = 0; - while (*s) - size += (*++s & 0xC0) != 0x80; - return size; -} - -STC_INLINE isize utf8_count_n(const char *s, isize nbytes) { - isize size = 0; - while ((nbytes-- != 0) & (*s != 0)) { - size += (*++s & 0xC0) != 0x80; - } - return size; -} - -STC_INLINE const char* utf8_at(const char *s, isize u8pos) { - while ((u8pos > 0) & (*s != 0)) - u8pos -= (*++s & 0xC0) != 0x80; - return s; -} - -STC_INLINE const char* utf8_offset(const char* s, isize u8pos) { - int inc = 1; - if (u8pos < 0) u8pos = -u8pos, inc = -1; - while (u8pos && *s) - u8pos -= (*(s += inc) & 0xC0) != 0x80; - return s; -} - -STC_INLINE isize utf8_to_index(const char* s, isize u8pos) - { return utf8_at(s, u8pos) - s; } - -STC_INLINE csview utf8_subview(const char *s, isize u8pos, isize u8len) { - csview span; - span.buf = utf8_at(s, u8pos); - span.size = utf8_to_index(span.buf, u8len); - return span; -} - -// ------------------------------------------------------ -// Functions below must be linked with utf8_prv.c content -// To call them, either define i_import before including -// one of cstr, csview, zsview, or link with src/libstc.a - -/* decode next utf8 codepoint. https://bjoern.hoehrmann.de/utf-8/decoder/dfa */ -typedef struct { uint32_t state, codep; } utf8_decode_t; -extern const uint8_t utf8_dtab[]; /* utf8code.c */ -#define utf8_ACCEPT 0 -#define utf8_REJECT 12 - -extern bool utf8_valid(const char* s); -extern bool utf8_valid_n(const char* s, isize nbytes); -extern int utf8_encode(char *out, uint32_t c); -extern int utf8_decode_codepoint(utf8_decode_t* d, const char* s, const char* end); -extern int utf8_icompare(const csview s1, const csview s2); -extern uint32_t utf8_peek_at(const char* s, isize u8offset); -extern uint32_t utf8_casefold(uint32_t c); -extern uint32_t utf8_tolower(uint32_t c); -extern uint32_t utf8_toupper(uint32_t c); - -STC_INLINE bool utf8_isupper(uint32_t c) - { return c < 128 ? (c >= 'A') & (c <= 'Z') : utf8_tolower(c) != c; } - -STC_INLINE bool utf8_islower(uint32_t c) - { return c < 128 ? (c >= 'a') & (c <= 'z') : utf8_toupper(c) != c; } - -STC_INLINE uint32_t utf8_decode(utf8_decode_t* d, const uint32_t byte) { - const uint32_t type = utf8_dtab[byte]; - d->codep = d->state ? (byte & 0x3fu) | (d->codep << 6) - : (0xffU >> type) & byte; - return d->state = utf8_dtab[256 + d->state + type]; -} - -STC_INLINE uint32_t utf8_peek(const char* s) { - utf8_decode_t d = {.state=0}; - do { - utf8_decode(&d, (uint8_t)*s++); - } while (d.state > utf8_REJECT); - return d.state == utf8_ACCEPT ? d.codep : 0xFFFD; -} - -/* case-insensitive utf8 string comparison */ -STC_INLINE int utf8_icmp(const char* s1, const char* s2) { - return utf8_icompare(c_sv(s1, INTPTR_MAX), c_sv(s2, INTPTR_MAX)); -} - -// ------------------------------------------------------ -// Functions below must be linked with ucd_prv.c content - -enum utf8_group { - U8G_Cc, U8G_L, U8G_Lm, U8G_Lt, U8G_Nd, U8G_Nl, U8G_No, - U8G_P, U8G_Pc, U8G_Pd, U8G_Pe, U8G_Pf, U8G_Pi, U8G_Ps, - U8G_Sc, U8G_Sk, U8G_Sm, U8G_Zl, U8G_Zp, U8G_Zs, - U8G_Arabic, U8G_Bengali, U8G_Cyrillic, - U8G_Devanagari, U8G_Georgian, U8G_Greek, - U8G_Han, U8G_Hiragana, U8G_Katakana, - U8G_Latin, U8G_Thai, - U8G_SIZE -}; - -extern bool utf8_isgroup(int group, uint32_t c); - -STC_INLINE bool utf8_isdigit(uint32_t c) - { return c < 128 ? (c >= '0') & (c <= '9') : utf8_isgroup(U8G_Nd, c); } - -STC_INLINE bool utf8_isalpha(uint32_t c) - { return (c < 128 ? isalpha((int)c) != 0 : utf8_isgroup(U8G_L, c)); } - -STC_INLINE bool utf8_iscased(uint32_t c) { - if (c < 128) return isalpha((int)c) != 0; - return utf8_toupper(c) != c || utf8_tolower(c) != c || utf8_isgroup(U8G_Lt, c); -} - -STC_INLINE bool utf8_isalnum(uint32_t c) { - if (c < 128) return isalnum((int)c) != 0; - return utf8_isgroup(U8G_L, c) || utf8_isgroup(U8G_Nd, c); -} - -STC_INLINE bool utf8_isword(uint32_t c) { - if (c < 128) return (isalnum((int)c) != 0) | (c == '_'); - return utf8_isgroup(U8G_L, c) || utf8_isgroup(U8G_Nd, c) || utf8_isgroup(U8G_Pc, c); -} - -STC_INLINE bool utf8_isblank(uint32_t c) { - if (c < 128) return (c == ' ') | (c == '\t'); - return utf8_isgroup(U8G_Zs, c); -} - -STC_INLINE bool utf8_isspace(uint32_t c) { - if (c < 128) return isspace((int)c) != 0; - return ((c == 8232) | (c == 8233)) || utf8_isgroup(U8G_Zs, c); -} - -#define c_lowerbound(T, c, at, less, N, ret) do { \ - int _n = N, _i = 0, _mid = _n/2; \ - T _c = c; \ - while (_n > 0) { \ - if (less(at((_i + _mid)), &_c)) { \ - _i += _mid + 1; \ - _n -= _mid + 1; \ - _mid = _n*7/8; \ - } else { \ - _n = _mid; \ - _mid = _n/8; \ - } \ - } \ - *(ret) = _i; \ -} while (0) - -#endif // STC_UTF8_PRV_H_INCLUDED diff --git a/src/finchlite/codegen/stc/stc/queue.h b/src/finchlite/codegen/stc/stc/queue.h deleted file mode 100644 index 507cf8ae..00000000 --- a/src/finchlite/codegen/stc/stc/queue.h +++ /dev/null @@ -1,39 +0,0 @@ -/* MIT License - * - * Copyright (c) 2025 Tyge Løvset - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -// Queue. Implemented as a ring buffer. -#include "priv/linkage.h" -#include "types.h" - -#ifndef STC_QUEUE_H_INCLUDED -#define STC_QUEUE_H_INCLUDED -#include "common.h" -#include -#endif // STC_QUEUE_H_INCLUDED - -#ifndef _i_prefix - #define _i_prefix queue_ -#endif -#include "priv/template.h" -#include "priv/queue_prv.h" -#include "sys/finalize.h" diff --git a/src/finchlite/codegen/stc/stc/random.h b/src/finchlite/codegen/stc/stc/random.h deleted file mode 100644 index 4d729054..00000000 --- a/src/finchlite/codegen/stc/stc/random.h +++ /dev/null @@ -1,248 +0,0 @@ -/* MIT License - * - * Copyright (c) 2025 Tyge Løvset - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. -*/ -#define i_header // external linkage of normal_dist by default. -#include "priv/linkage.h" - -#ifndef STC_RANDOM_H_INCLUDED -#define STC_RANDOM_H_INCLUDED - -#include "common.h" - -// ===== crand64 =================================== - -typedef struct { - uint64_t data[4]; -} crand64; - -typedef struct { - double mean, stddev; - double _next; - int _has_next; -} crand64_normal_dist; - -STC_API double crand64_normal(crand64_normal_dist* d); -STC_API double crand64_normal_r(crand64* rng, uint64_t stream, crand64_normal_dist* d); - -#if INTPTR_MAX == INT64_MAX - #define crandWS crand64 -#else - #define crandWS crand32 -#endif - -#define c_shuffle_seed(s) \ - c_JOIN(crandWS, _seed)(s) - -#define c_shuffle_array(array, n) do { \ - typedef struct { char d[sizeof 0[array]]; } _etype; \ - _etype* _arr = (_etype *)(array); \ - for (isize _i = (n) - 1; _i > 0; --_i) { \ - isize _j = (isize)(c_JOIN(crandWS, _uint)() % (_i + 1)); \ - c_swap(_arr + _i, _arr + _j); \ - } \ -} while (0) - -// Compiles with vec, stack, and deque container types: -#define c_shuffle(CntType, self) do { \ - CntType* _self = self; \ - for (isize _i = CntType##_size(_self) - 1; _i > 0; --_i) { \ - isize _j = (isize)(c_JOIN(crandWS, _uint)() % (_i + 1)); \ - c_swap(CntType##_at_mut(_self, _i), CntType##_at_mut(_self, _j)); \ - } \ -} while (0) - -STC_INLINE void crand64_seed_r(crand64* rng, uint64_t seed) { - uint64_t* s = rng->data; - s[0] = seed*0x9e3779b97f4a7c15; s[0] ^= s[0] >> 30; - s[1] = s[0]*0xbf58476d1ce4e5b9; s[1] ^= s[1] >> 27; - s[2] = s[1]*0x94d049bb133111eb; s[2] ^= s[2] >> 31; - s[3] = seed; -} - -// Minimum period length 2^64 per stream. 2^63 streams (odd numbers only) -STC_INLINE uint64_t crand64_uint_r(crand64* rng, uint64_t stream) { - uint64_t* s = rng->data; - const uint64_t result = (s[0] ^ (s[3] += stream)) + s[1]; - s[0] = s[1] ^ (s[1] >> 11); - s[1] = s[2] + (s[2] << 3); - s[2] = ((s[2] << 24) | (s[2] >> 40)) + result; - return result; -} - -STC_INLINE double crand64_real_r(crand64* rng, uint64_t stream) - { return (double)(crand64_uint_r(rng, stream) >> 11) * 0x1.0p-53; } - -STC_INLINE crand64* _stc64(void) { - static crand64 rng = {{0x9e3779bb07979af0,0x6f682616bae3641a,0xe220a8397b1dcdaf,0x1}}; - return &rng; -} - -STC_INLINE void crand64_seed(uint64_t seed) - { crand64_seed_r(_stc64(), seed); } - -STC_INLINE crand64 crand64_from(uint64_t seed) - { crand64 rng; crand64_seed_r(&rng, seed); return rng; } - -STC_INLINE uint64_t crand64_uint(void) - { return crand64_uint_r(_stc64(), 1); } - -STC_INLINE double crand64_real(void) - { return crand64_real_r(_stc64(), 1); } - -// --- crand64_uniform --- - -typedef struct { - int64_t low; - uint64_t range, threshold; -} crand64_uniform_dist; - -STC_INLINE crand64_uniform_dist -crand64_make_uniform(int64_t low, int64_t high) { - crand64_uniform_dist d = {low, (uint64_t)(high - low + 1)}; - d.threshold = (uint64_t)(0 - d.range) % d.range; - return d; -} - -// 128-bit multiplication -#if defined(__SIZEOF_INT128__) - #define c_umul128(a, b, lo, hi) \ - do { __uint128_t _z = (__uint128_t)(a)*(b); \ - *(lo) = (uint64_t)_z, *(hi) = (uint64_t)(_z >> 64U); } while(0) -#elif defined(_MSC_VER) && defined(_WIN64) - #include - #define c_umul128(a, b, lo, hi) ((void)(*(lo) = _umul128(a, b, hi))) -#elif defined(__x86_64__) - #define c_umul128(a, b, lo, hi) \ - asm("mulq %3" : "=a"(*(lo)), "=d"(*(hi)) : "a"(a), "rm"(b)) -#endif - -STC_INLINE int64_t -crand64_uniform_r(crand64* rng, uint64_t stream, crand64_uniform_dist* d) { - uint64_t lo, hi; - #ifdef c_umul128 - do { c_umul128(crand64_uint_r(rng, stream), d->range, &lo, &hi); } while (lo < d->threshold); - #else - do { lo = crand64_uint_r(rng, stream); hi = lo % d->range; } while (lo - hi > -d->range); - #endif - return d->low + (int64_t)hi; -} - -STC_INLINE int64_t crand64_uniform(crand64_uniform_dist* d) - { return crand64_uniform_r(_stc64(), 1, d); } - -// ===== crand32 =================================== - -typedef struct { uint32_t data[4]; } crand32; - -STC_INLINE void crand32_seed_r(crand32* rng, uint32_t seed) { - uint32_t* s = rng->data; - s[0] = seed*0x9e3779b9; s[0] ^= s[0] >> 16; - s[1] = s[0]*0x21f0aaad; s[1] ^= s[1] >> 15; - s[2] = s[1]*0x735a2d97; s[2] ^= s[2] >> 15; - s[3] = seed; -} - -// Minimum period length 2^32 per stream. 2^31 streams (odd numbers only) -STC_INLINE uint32_t crand32_uint_r(crand32* rng, uint32_t stream) { - uint32_t* s = rng->data; - const uint32_t result = (s[0] ^ (s[3] += stream)) + s[1]; - s[0] = s[1] ^ (s[1] >> 9); - s[1] = s[2] + (s[2] << 3); - s[2] = ((s[2] << 21) | (s[2] >> 11)) + result; - return result; -} - -STC_INLINE double crand32_real_r(crand32* rng, uint32_t stream) - { return crand32_uint_r(rng, stream) * 0x1.0p-32; } - -STC_INLINE crand32* _stc32(void) { - static crand32 rng = {{0x9e37e78e,0x6eab1ba1,0x64625032,0x1}}; - return &rng; -} - -STC_INLINE void crand32_seed(uint32_t seed) - { crand32_seed_r(_stc32(), seed); } - -STC_INLINE crand32 crand32_from(uint32_t seed) - { crand32 rng; crand32_seed_r(&rng, seed); return rng; } - -STC_INLINE uint32_t crand32_uint(void) - { return crand32_uint_r(_stc32(), 1); } - -STC_INLINE double crand32_real(void) - { return crand32_real_r(_stc32(), 1); } - -// --- crand32_uniform --- - -typedef struct { - int32_t low; - uint32_t range, threshold; -} crand32_uniform_dist; - -STC_INLINE crand32_uniform_dist -crand32_make_uniform(int32_t low, int32_t high) { - crand32_uniform_dist d = {low, (uint32_t)(high - low + 1)}; - d.threshold = (uint32_t)(0 - d.range) % d.range; - return d; -} - -STC_INLINE int32_t -crand32_uniform_r(crand32* rng, uint32_t stream, crand32_uniform_dist* d) { - uint64_t r; - do { - r = crand32_uint_r(rng, stream) * (uint64_t)d->range; - } while ((uint32_t)r < d->threshold); - return d->low + (int32_t)(r >> 32); -} - -STC_INLINE int64_t crand32_uniform(crand32_uniform_dist* d) - { return crand32_uniform_r(_stc32(), 1, d); } - -#endif // STC_RANDOM_H_INCLUDED - -/* -------------------------- IMPLEMENTATION ------------------------- */ -#if defined i_implement && !defined STC_RANDOM_IMPLEMENT -#define STC_RANDOM_IMPLEMENT -#include - -STC_DEF double -crand64_normal_r(crand64* rng, uint64_t stream, crand64_normal_dist* d) { - double v1, v2, sq, rt; - if (d->_has_next++ & 1) - return d->_next*d->stddev + d->mean; - do { - // range (-1.0, 1.0): - v1 = (double)((int64_t)crand64_uint_r(rng, stream) >> 11) * 0x1.0p-52; - v2 = (double)((int64_t)crand64_uint_r(rng, stream) >> 11) * 0x1.0p-52; - - sq = v1*v1 + v2*v2; - } while (sq >= 1.0 || sq == 0.0); - rt = sqrt(-2.0 * log(sq) / sq); - d->_next = v2*rt; - return (v1*rt)*d->stddev + d->mean; -} - -STC_DEF double crand64_normal(crand64_normal_dist* d) - { return crand64_normal_r(_stc64(), 1, d); } - -#endif // IMPLEMENT -#include "priv/linkage2.h" diff --git a/src/finchlite/codegen/stc/stc/rc.h b/src/finchlite/codegen/stc/stc/rc.h deleted file mode 100644 index 98ec6e08..00000000 --- a/src/finchlite/codegen/stc/stc/rc.h +++ /dev/null @@ -1,38 +0,0 @@ -/* MIT License - * - * Copyright (c) 2025 Tyge Løvmap - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -// Unordered map - implemented with the robin-hood hashing scheme. -/* -#define T IRefc, int -#include -#include - -int main(void) { - IRefc rc = IRefc_make(42); - IRefc_drop(&rc); -} -*/ - -#define i_no_atomic -#define _i_prefix rc_ -#include "arc.h" diff --git a/src/finchlite/codegen/stc/stc/smap.h b/src/finchlite/codegen/stc/stc/smap.h deleted file mode 100644 index 1dfdb4b8..00000000 --- a/src/finchlite/codegen/stc/stc/smap.h +++ /dev/null @@ -1,612 +0,0 @@ -/* MIT License - * - * Copyright (c) 2025 Tyge Løvset - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -// Sorted/Ordered set and map - implemented as an AA-tree. -/* -#include -#include - -#define T SMap, cstr, double, (c_keypro) // Sorted map -#include - -int main(void) { - SMap m = {0}; - SMap_emplace(&m, "Testing one", 1.234); - SMap_emplace(&m, "Testing two", 12.34); - SMap_emplace(&m, "Testing three", 123.4); - - SMap_value *v = SMap_get(&m, "Testing five"); // NULL - double num = *SMap_at(&m, "Testing one"); - SMap_emplace_or_assign(&m, "Testing three", 1000.0); // update - SMap_erase(&m, "Testing two"); - - for (c_each(i, SMap, m)) - printf("map %s: %g\n", cstr_str(&i.ref->first), i.ref->second); - - SMap_drop(&m); -} -*/ -#include "priv/linkage.h" -#include "types.h" - -#ifndef STC_SMAP_H_INCLUDED -#define STC_SMAP_H_INCLUDED -#include "common.h" -#include -#endif // STC_SMAP_H_INCLUDED - -#ifndef _i_prefix - #define _i_prefix smap_ -#endif -#ifndef _i_is_set - #define _i_is_map - #define _i_MAP_ONLY c_true - #define _i_SET_ONLY c_false - #define _i_keyref(vp) (&(vp)->first) -#else - #define _i_MAP_ONLY c_false - #define _i_SET_ONLY c_true - #define _i_keyref(vp) (vp) -#endif -#define _i_sorted -#include "priv/template.h" -#ifndef i_declared - _c_DEFTYPES(_declare_aatree, Self, i_key, i_val, _i_MAP_ONLY, _i_SET_ONLY, _i_aux_def); -#endif - -_i_MAP_ONLY( struct _m_value { - _m_key first; - _m_mapped second; -}; ) -struct _m_node { - int32_t link[2]; - int8_t level; - _m_value value; -}; - -typedef i_keyraw _m_keyraw; -typedef i_valraw _m_rmapped; -typedef _i_SET_ONLY( _m_keyraw ) - _i_MAP_ONLY( struct { _m_keyraw first; _m_rmapped second; } ) - _m_raw; - -#ifndef i_no_emplace -STC_API _m_result _c_MEMB(_emplace)(Self* self, _m_keyraw rkey _i_MAP_ONLY(, _m_rmapped rmapped)); -#endif // !i_no_emplace - -#ifndef i_no_clone -STC_API Self _c_MEMB(_clone)(Self tree); -#endif // !i_no_clone - -STC_API void _c_MEMB(_drop)(const Self* cself); -STC_API bool _c_MEMB(_reserve)(Self* self, isize cap); -STC_API _m_value* _c_MEMB(_find_it)(const Self* self, _m_keyraw rkey, _m_iter* out); -STC_API _m_iter _c_MEMB(_lower_bound)(const Self* self, _m_keyraw rkey); -STC_API _m_value* _c_MEMB(_front)(const Self* self); -STC_API _m_value* _c_MEMB(_back)(const Self* self); -STC_API int _c_MEMB(_erase)(Self* self, _m_keyraw rkey); -STC_API _m_iter _c_MEMB(_erase_at)(Self* self, _m_iter it); -STC_API _m_iter _c_MEMB(_erase_range)(Self* self, _m_iter it1, _m_iter it2); -STC_API _m_iter _c_MEMB(_begin)(const Self* self); -STC_API void _c_MEMB(_next)(_m_iter* it); - -STC_INLINE bool _c_MEMB(_is_empty)(const Self* self) { return self->size == 0; } -STC_INLINE isize _c_MEMB(_size)(const Self* self) { return self->size; } -STC_INLINE isize _c_MEMB(_capacity)(const Self* self) { return self->capacity; } -STC_INLINE _m_iter _c_MEMB(_find)(const Self* self, _m_keyraw rkey) - { _m_iter it; _c_MEMB(_find_it)(self, rkey, &it); return it; } -STC_INLINE bool _c_MEMB(_contains)(const Self* self, _m_keyraw rkey) - { _m_iter it; return _c_MEMB(_find_it)(self, rkey, &it) != NULL; } -STC_INLINE const _m_value* _c_MEMB(_get)(const Self* self, _m_keyraw rkey) - { _m_iter it; return _c_MEMB(_find_it)(self, rkey, &it); } -STC_INLINE _m_value* _c_MEMB(_get_mut)(Self* self, _m_keyraw rkey) - { _m_iter it; return _c_MEMB(_find_it)(self, rkey, &it); } - -STC_INLINE _m_raw _c_MEMB(_value_toraw)(const _m_value* val) { - return _i_SET_ONLY( i_keytoraw(val) ) - _i_MAP_ONLY( c_literal(_m_raw){i_keytoraw((&val->first)), - i_valtoraw((&val->second))} ); -} - -STC_INLINE void _c_MEMB(_value_drop)(const Self* self, _m_value* val) { - (void)self; - i_keydrop(_i_keyref(val)); - _i_MAP_ONLY( i_valdrop((&val->second)); ) -} - -STC_INLINE Self _c_MEMB(_move)(Self *self) { - Self m = *self; - self->capacity = self->size = self->root = self->disp = self->head = 0; - self->nodes = NULL; - return m; -} - -STC_INLINE void _c_MEMB(_clear)(Self* self) { - _c_MEMB(_drop)(self); - (void)_c_MEMB(_move)(self); -} - -STC_INLINE void _c_MEMB(_take)(Self *self, Self unowned) { - _c_MEMB(_drop)(self); - *self = unowned; -} - -#ifndef i_no_clone -STC_INLINE _m_value _c_MEMB(_value_clone)(const Self* self, _m_value _val) { - (void)self; - *_i_keyref(&_val) = i_keyclone((*_i_keyref(&_val))); - _i_MAP_ONLY( _val.second = i_valclone(_val.second); ) - return _val; -} - -STC_INLINE void _c_MEMB(_copy)(Self *self, const Self* other) { - if (self == other) - return; - _c_MEMB(_drop)(self); - *self = _c_MEMB(_clone)(*other); -} - -STC_INLINE void _c_MEMB(_shrink_to_fit)(Self *self) { - Self tmp = _c_MEMB(_clone)(*self); - _c_MEMB(_drop)(self); *self = tmp; -} -#endif // !i_no_clone - -STC_API _m_result _c_MEMB(_insert_entry_)(Self* self, _m_keyraw rkey); - -#ifdef _i_is_map - STC_API _m_result _c_MEMB(_insert_or_assign)(Self* self, _m_key key, _m_mapped mapped); - #ifndef i_no_emplace - STC_API _m_result _c_MEMB(_emplace_or_assign)(Self* self, _m_keyraw rkey, _m_rmapped rmapped); - #endif - - STC_INLINE const _m_mapped* _c_MEMB(_at)(const Self* self, _m_keyraw rkey) - { _m_iter it; return &_c_MEMB(_find_it)(self, rkey, &it)->second; } - - STC_INLINE _m_mapped* _c_MEMB(_at_mut)(Self* self, _m_keyraw rkey) - { _m_iter it; return &_c_MEMB(_find_it)(self, rkey, &it)->second; } -#endif // _i_is_map - -STC_INLINE _m_iter _c_MEMB(_end)(const Self* self) { - _m_iter it; (void)self; - it.ref = NULL, it._top = 0, it._tn = 0; - return it; -} - -STC_INLINE _m_iter _c_MEMB(_advance)(_m_iter it, size_t n) { - while (n-- && it.ref) - _c_MEMB(_next)(&it); - return it; -} - -#if defined _i_has_eq -STC_INLINE bool -_c_MEMB(_eq)(const Self* self, const Self* other) { - if (_c_MEMB(_size)(self) != _c_MEMB(_size)(other)) return false; - _m_iter i = _c_MEMB(_begin)(self), j = _c_MEMB(_begin)(other); - for (; i.ref; _c_MEMB(_next)(&i), _c_MEMB(_next)(&j)) { - const _m_keyraw _rx = i_keytoraw(_i_keyref(i.ref)), _ry = i_keytoraw(_i_keyref(j.ref)); - if (!(i_eq((&_rx), (&_ry)))) return false; - } - return true; -} -#endif - -STC_INLINE _m_result -_c_MEMB(_insert)(Self* self, _m_key _key _i_MAP_ONLY(, _m_mapped _mapped)) { - _m_result _res = _c_MEMB(_insert_entry_)(self, i_keytoraw((&_key))); - if (_res.inserted) - { *_i_keyref(_res.ref) = _key; _i_MAP_ONLY( _res.ref->second = _mapped; )} - else - { i_keydrop((&_key)); _i_MAP_ONLY( i_valdrop((&_mapped)); )} - return _res; -} - -STC_INLINE _m_value* _c_MEMB(_push)(Self* self, _m_value _val) { - _m_result _res = _c_MEMB(_insert_entry_)(self, i_keytoraw(_i_keyref(&_val))); - if (_res.inserted) - *_res.ref = _val; - else - _c_MEMB(_value_drop)(self, &_val); - return _res.ref; -} - -#if defined _i_is_map && !defined _i_no_put -STC_INLINE _m_result _c_MEMB(_put)(Self* self, _m_keyraw rkey, _m_rmapped rmapped) { - #ifdef i_no_emplace - return _c_MEMB(_insert_or_assign)(self, rkey, rmapped); - #else - return _c_MEMB(_emplace_or_assign)(self, rkey, rmapped); - #endif -} -#endif - -#ifndef _i_no_put -STC_INLINE void _c_MEMB(_put_n)(Self* self, const _m_raw* raw, isize n) { - while (n--) - #if defined _i_is_set && defined i_no_emplace - _c_MEMB(_insert)(self, *raw++); - #elif defined _i_is_set - _c_MEMB(_emplace)(self, *raw++); - #else - _c_MEMB(_put)(self, raw->first, raw->second), ++raw; - #endif -} -#endif - -#ifndef _i_aux_alloc -STC_INLINE Self _c_MEMB(_init)(void) - { Self cx = {0}; return cx; } - -#ifndef _i_no_put -STC_INLINE Self _c_MEMB(_from_n)(const _m_raw* raw, isize n) - { Self cx = {0}; _c_MEMB(_put_n)(&cx, raw, n); return cx; } -#endif - -STC_INLINE Self _c_MEMB(_with_capacity)(const isize cap) - { Self cx = {0}; _c_MEMB(_reserve)(&cx, cap); return cx; } -#endif - -/* -------------------------- IMPLEMENTATION ------------------------- */ -#if defined i_implement - -STC_DEF void -_c_MEMB(_next)(_m_iter *it) { - int32_t tn = it->_tn; - if (it->_top || tn) { - while (tn) { - it->_st[it->_top++] = tn; - tn = it->_d[tn].link[0]; - } - tn = it->_st[--it->_top]; - it->_tn = it->_d[tn].link[1]; - it->ref = &it->_d[tn].value; - } else - it->ref = NULL; -} - -STC_DEF _m_iter -_c_MEMB(_begin)(const Self* self) { - _m_iter it; - it.ref = NULL; - it._d = self->nodes, it._top = 0; - it._tn = self->root; - if (it._tn) - _c_MEMB(_next)(&it); - return it; -} - -STC_DEF bool -_c_MEMB(_reserve)(Self* self, const isize cap) { - if (cap <= self->capacity) - return false; - _m_node* nodes = (_m_node*)_i_realloc_n(self->nodes, self->capacity + 1, cap + 1); - if (nodes == NULL) - return false; - nodes[0] = c_literal(_m_node){0}; - self->nodes = nodes; - self->capacity = (int32_t)cap; - return true; -} - -STC_DEF _m_value* -_c_MEMB(_front)(const Self* self) { - _m_node *d = self->nodes; - int32_t tn = self->root; - while (d[tn].link[0]) - tn = d[tn].link[0]; - return &d[tn].value; -} - -STC_DEF _m_value* -_c_MEMB(_back)(const Self* self) { - _m_node *d = self->nodes; - int32_t tn = self->root; - while (d[tn].link[1]) - tn = d[tn].link[1]; - return &d[tn].value; -} - -static int32_t -_c_MEMB(_new_node_)(Self* self, int level) { - int32_t tn; - if (self->disp != 0) { - tn = self->disp; - self->disp = self->nodes[tn].link[1]; - } else { - if (self->head == self->capacity) - if (!_c_MEMB(_reserve)(self, self->head*3/2 + 4)) - return 0; - tn = ++self->head; /* start with 1, 0 is nullnode. */ - } - _m_node* dn = &self->nodes[tn]; - dn->link[0] = dn->link[1] = 0; dn->level = (int8_t)level; - return tn; -} - -#ifdef _i_is_map - STC_DEF _m_result - _c_MEMB(_insert_or_assign)(Self* self, _m_key _key, _m_mapped _mapped) { - _m_result _res = _c_MEMB(_insert_entry_)(self, i_keytoraw((&_key))); - _m_mapped* _mp = _res.ref ? &_res.ref->second : &_mapped; - if (_res.inserted) - _res.ref->first = _key; - else - { i_keydrop((&_key)); i_valdrop(_mp); } - *_mp = _mapped; - return _res; - } - - #ifndef i_no_emplace - STC_DEF _m_result - _c_MEMB(_emplace_or_assign)(Self* self, _m_keyraw rkey, _m_rmapped rmapped) { - _m_result _res = _c_MEMB(_insert_entry_)(self, rkey); - if (_res.inserted) - _res.ref->first = i_keyfrom(rkey); - else { - if (_res.ref == NULL) return _res; - i_valdrop((&_res.ref->second)); - } - _res.ref->second = i_valfrom(rmapped); - return _res; - } - #endif // !i_no_emplace -#endif // !_i_is_map - -STC_DEF _m_value* -_c_MEMB(_find_it)(const Self* self, _m_keyraw rkey, _m_iter* out) { - int32_t tn = self->root; - _m_node *d = out->_d = self->nodes; - out->_top = 0; - while (tn) { - int c; const _m_keyraw _raw = i_keytoraw(_i_keyref(&d[tn].value)); - if ((c = i_cmp((&_raw), (&rkey))) < 0) - tn = d[tn].link[1]; - else if (c > 0) - { out->_st[out->_top++] = tn; tn = d[tn].link[0]; } - else - { out->_tn = d[tn].link[1]; return (out->ref = &d[tn].value); } - } - return (out->ref = NULL); -} - -STC_DEF _m_iter -_c_MEMB(_lower_bound)(const Self* self, _m_keyraw rkey) { - _m_iter it; - _c_MEMB(_find_it)(self, rkey, &it); - if (it.ref == NULL && it._top != 0) { - int32_t tn = it._st[--it._top]; - it._tn = it._d[tn].link[1]; - it.ref = &it._d[tn].value; - } - return it; -} - -STC_DEF int32_t -_c_MEMB(_skew_)(_m_node *d, int32_t tn) { - if (tn != 0 && d[d[tn].link[0]].level == d[tn].level) { - int32_t tmp = d[tn].link[0]; - d[tn].link[0] = d[tmp].link[1]; - d[tmp].link[1] = tn; - tn = tmp; - } - return tn; -} - -STC_DEF int32_t -_c_MEMB(_split_)(_m_node *d, int32_t tn) { - if (d[d[d[tn].link[1]].link[1]].level == d[tn].level) { - int32_t tmp = d[tn].link[1]; - d[tn].link[1] = d[tmp].link[0]; - d[tmp].link[0] = tn; - tn = tmp; - ++d[tn].level; - } - return tn; -} - -STC_DEF int32_t -_c_MEMB(_insert_entry_i_)(Self* self, int32_t tn, const _m_keyraw* rkey, _m_result* _res) { - int32_t up[64], tx = tn; - _m_node* d = self->nodes; - int c, top = 0, dir = 0; - while (tx) { - up[top++] = tx; - const _m_keyraw _raw = i_keytoraw(_i_keyref(&d[tx].value)); - if ((c = i_cmp((&_raw), rkey)) == 0) - { _res->ref = &d[tx].value; return tn; } - dir = (c < 0); - tx = d[tx].link[dir]; - } - if ((tx = _c_MEMB(_new_node_)(self, 1)) == 0) - return 0; - d = self->nodes; - _res->ref = &d[tx].value; - _res->inserted = true; - if (top == 0) - return tx; - d[up[top - 1]].link[dir] = tx; - while (top--) { - if (top != 0) - dir = (d[up[top - 1]].link[1] == up[top]); - up[top] = _c_MEMB(_skew_)(d, up[top]); - up[top] = _c_MEMB(_split_)(d, up[top]); - if (top) - d[up[top - 1]].link[dir] = up[top]; - } - return up[0]; -} - -STC_DEF _m_result -_c_MEMB(_insert_entry_)(Self* self, _m_keyraw rkey) { - _m_result res = {0}; - int32_t tn = _c_MEMB(_insert_entry_i_)(self, self->root, &rkey, &res); - self->root = tn; - self->size += res.inserted; - return res; -} - -STC_DEF int32_t -_c_MEMB(_erase_r_)(Self *self, int32_t tn, const _m_keyraw* rkey, int *erased) { - _m_node *d = self->nodes; - if (tn == 0) - return 0; - _m_keyraw raw = i_keytoraw(_i_keyref(&d[tn].value)); - int32_t tx; int c = i_cmp((&raw), rkey); - if (c != 0) - d[tn].link[c < 0] = _c_MEMB(_erase_r_)(self, d[tn].link[c < 0], rkey, erased); - else { - if ((*erased)++ == 0) - _c_MEMB(_value_drop)(self, &d[tn].value); // drop first time, not second. - if (d[tn].link[0] && d[tn].link[1]) { - tx = d[tn].link[0]; - while (d[tx].link[1]) - tx = d[tx].link[1]; - d[tn].value = d[tx].value; /* move */ - raw = i_keytoraw(_i_keyref(&d[tn].value)); - d[tn].link[0] = _c_MEMB(_erase_r_)(self, d[tn].link[0], &raw, erased); - } else { /* unlink node */ - tx = tn; - tn = d[tn].link[ d[tn].link[0] == 0 ]; - /* move it to disposed nodes list */ - d[tx].link[1] = self->disp; - self->disp = tx; - } - } - tx = d[tn].link[1]; - if (d[d[tn].link[0]].level < d[tn].level - 1 || d[tx].level < d[tn].level - 1) { - if (d[tx].level > --d[tn].level) - d[tx].level = d[tn].level; - tn = _c_MEMB(_skew_)(d, tn); - tx = d[tn].link[1] = _c_MEMB(_skew_)(d, d[tn].link[1]); - d[tx].link[1] = _c_MEMB(_skew_)(d, d[tx].link[1]); - tn = _c_MEMB(_split_)(d, tn); - d[tn].link[1] = _c_MEMB(_split_)(d, d[tn].link[1]); - } - return tn; -} - -STC_DEF int -_c_MEMB(_erase)(Self* self, _m_keyraw rkey) { - int erased = 0; - int32_t root = _c_MEMB(_erase_r_)(self, self->root, &rkey, &erased); - if (erased == 0) - return 0; - self->root = root; - --self->size; - return 1; -} - -STC_DEF _m_iter -_c_MEMB(_erase_at)(Self* self, _m_iter it) { - _m_keyraw raw = i_keytoraw(_i_keyref(it.ref)); - _c_MEMB(_next)(&it); - if (it.ref != NULL) { - _m_keyraw nxt = i_keytoraw(_i_keyref(it.ref)); - _c_MEMB(_erase)(self, raw); - _c_MEMB(_find_it)(self, nxt, &it); - } else - _c_MEMB(_erase)(self, raw); - return it; -} - -STC_DEF _m_iter -_c_MEMB(_erase_range)(Self* self, _m_iter it1, _m_iter it2) { - if (it2.ref == NULL) { - while (it1.ref != NULL) - it1 = _c_MEMB(_erase_at)(self, it1); - return it1; - } - _m_key k1 = *_i_keyref(it1.ref), k2 = *_i_keyref(it2.ref); - _m_keyraw r1 = i_keytoraw((&k1)); - for (;;) { - if (memcmp(&k1, &k2, sizeof k1) == 0) - return it1; - _c_MEMB(_next)(&it1); - k1 = *_i_keyref(it1.ref); - _c_MEMB(_erase)(self, r1); - r1 = i_keytoraw((&k1)); - _c_MEMB(_find_it)(self, r1, &it1); - } -} - -#ifndef i_no_clone -STC_DEF int32_t -_c_MEMB(_clone_r_)(Self* self, _m_node* src, int32_t sn) { - if (sn == 0) - return 0; - int32_t tx, tn = _c_MEMB(_new_node_)(self, src[sn].level); - self->nodes[tn].value = _c_MEMB(_value_clone)(self, src[sn].value); - tx = _c_MEMB(_clone_r_)(self, src, src[sn].link[0]); self->nodes[tn].link[0] = tx; - tx = _c_MEMB(_clone_r_)(self, src, src[sn].link[1]); self->nodes[tn].link[1] = tx; - return tn; -} - -STC_DEF Self -_c_MEMB(_clone)(Self tree) { - Self out = tree; - out.root = out.disp = out.head = out.size = out.capacity = 0; - out.nodes = NULL; _c_MEMB(_reserve)(&out, tree.size); - out.root = _c_MEMB(_clone_r_)(&out, tree.nodes, tree.root); - return out; -} -#endif // !i_no_clone - -#ifndef i_no_emplace -STC_DEF _m_result -_c_MEMB(_emplace)(Self* self, _m_keyraw rkey _i_MAP_ONLY(, _m_rmapped rmapped)) { - _m_result res = _c_MEMB(_insert_entry_)(self, rkey); - if (res.inserted) { - *_i_keyref(res.ref) = i_keyfrom(rkey); - _i_MAP_ONLY(res.ref->second = i_valfrom(rmapped);) - } - return res; -} -#endif // i_no_emplace - -static void -_c_MEMB(_drop_r_)(Self* s, int32_t tn) { - if (tn != 0) { - _c_MEMB(_drop_r_)(s, s->nodes[tn].link[0]); - _c_MEMB(_drop_r_)(s, s->nodes[tn].link[1]); - _c_MEMB(_value_drop)(s, &s->nodes[tn].value); - } -} - -STC_DEF void -_c_MEMB(_drop)(const Self* cself) { - Self* self = (Self*)cself; - if (self->capacity != 0) { - _c_MEMB(_drop_r_)(self, self->root); - _i_free_n(self->nodes, self->capacity + 1); - } -} - -#endif // i_implement -#undef _i_is_set -#undef _i_is_map -#undef _i_sorted -#undef _i_keyref -#undef _i_MAP_ONLY -#undef _i_SET_ONLY -#include "sys/finalize.h" diff --git a/src/finchlite/codegen/stc/stc/sort.h b/src/finchlite/codegen/stc/stc/sort.h deleted file mode 100644 index a2f95a57..00000000 --- a/src/finchlite/codegen/stc/stc/sort.h +++ /dev/null @@ -1,109 +0,0 @@ -/* MIT License - * - * Copyright (c) 2025 Tyge Løvset - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -/* Generic Quicksort in C, performs as fast as c++ std::sort(), and more robust. -template params: -#define i_key keytype - [required] (or use i_type, see below) -#define i_less(xp, yp) - optional less function. default: *xp < *yp -#define i_cmp(xp, yp) - alternative 3-way comparison. c_default_cmp(xp, yp) -#define T name - optional, defines {name}_sort(), else {i_key}s_sort(). -#define T name, key - alternative one-liner to define both i_type and i_key. - -// ex1: -#include -#define i_key int -#include - -int main(void) { - int nums[] = {23, 321, 5434, 25, 245, 1, 654, 33, 543, 21}; - - ints_sort(nums, c_arraylen(nums)); - - for (int i = 0; i < c_arraylen(nums); i++) - printf(" %d", nums[i]); - puts(""); - - isize idx = ints_binary_search(nums, 25, c_arraylen(nums)); - if (idx != c_NPOS) printf("found: %d\n", nums[idx]); - - idx = ints_lower_bound(nums, 200, c_arraylen(nums)); - if (idx != c_NPOS) printf("found lower 200: %d\n", nums[idx]); -} - -// ex2: Test on a deque !! -#include -#define T IDeq, int, (c_use_cmp) // enable comparison functions -#include - -int main(void) { - IDeq nums = c_make(IDeq, {5434, 25, 245, 1, 654, 33, 543, 21}); - IDeq_push_front(&nums, 23); - IDeq_push_front(&nums, 321); - - IDeq_sort(&nums); - - for (c_each (i, IDeq, nums)) - printf(" %d", *i.ref); - puts(""); - - isize idx = IDeq_binary_search(&nums, 25); - if (idx != c_NPOS) printf("found: %d\n", *IDeq_at(&nums, idx)); - - idx = IDeq_lower_bound(&nums, 200); - if (idx != c_NPOS) printf("found lower 200: %d\n", *IDeq_at(&nums, idx)); - - IDeq_drop(&nums); -} -*/ -#ifndef _i_template - #include "priv/linkage.h" - #include "common.h" - - #define _i_is_array - #if defined T && !defined i_type - #define i_type T - #endif - #if defined i_type && !defined i_key - #define Self c_GETARG(1, i_type) - #define i_key c_GETARG(2, i_type) - #elif defined i_type - #define Self i_type - #else - #define Self c_JOIN(i_key, s) - #endif - - typedef i_key Self; - typedef Self c_JOIN(Self, _value), c_JOIN(Self, _raw); - #define i_at(arr, idx) (&(arr)[idx]) - #define i_at_mut i_at - #include "priv/template.h" // IWYU pragma: keep -#endif - -#include "priv/sort_prv.h" - -#ifdef _i_is_array - #undef _i_is_array - #include "priv/linkage2.h" - #include "priv/template2.h" -#endif -#undef i_at -#undef i_at_mut diff --git a/src/finchlite/codegen/stc/stc/sortedmap.h b/src/finchlite/codegen/stc/stc/sortedmap.h deleted file mode 100644 index f293122d..00000000 --- a/src/finchlite/codegen/stc/stc/sortedmap.h +++ /dev/null @@ -1,46 +0,0 @@ -/* MIT License - * - * Copyright (c) 2025 Tyge Løvset - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -// Sorted map - implemented as an AA-tree (balanced binary tree). -/* -#include - -#define T Intmap, int, int -#include // sorted map of int - -int main(void) { - Intmap map = {0}; - Intmap_insert(&map, 5, 25); - Intmap_insert(&map, 8, 38); - Intmap_insert(&map, 3, 43); - Intmap_insert(&map, 5, 55); - - for (c_each_kv(k, v, Intmap, map)) - printf(" %d -> %d\n", *k, *v); - - Intmap_drop(&map); -} -*/ - -#define _i_prefix smap_ -#include "smap.h" diff --git a/src/finchlite/codegen/stc/stc/sortedset.h b/src/finchlite/codegen/stc/stc/sortedset.h deleted file mode 100644 index 17c847e9..00000000 --- a/src/finchlite/codegen/stc/stc/sortedset.h +++ /dev/null @@ -1,47 +0,0 @@ -/* MIT License - * - * Copyright (c) 2025 Tyge Løvset - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -// Sorted set - implemented as an AA-tree (balanced binary tree). -/* -#include - -#define T Intset, int -#include // sorted set of int - -int main(void) { - Intset set = {0}; - Intset_insert(&set, 5); - Intset_insert(&set, 8); - Intset_insert(&set, 3); - Intset_insert(&set, 5); - - for (c_each(k, Intset, set)) - printf(" %d\n", *k.ref); - - Intset_drop(&set); -} -*/ - -#define _i_prefix sset_ -#define _i_is_set -#include "smap.h" diff --git a/src/finchlite/codegen/stc/stc/sset.h b/src/finchlite/codegen/stc/stc/sset.h deleted file mode 100644 index 6558a1af..00000000 --- a/src/finchlite/codegen/stc/stc/sset.h +++ /dev/null @@ -1,46 +0,0 @@ -/* MIT License - * - * Copyright (c) 2025 Tyge Løvset - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -// Sorted set - implemented as an AA-tree (balanced binary tree). -/* -#include - -#define T Intset, int -#include // sorted set of int - -int main(void) { - Intset s = {0}; - Intset_insert(&s, 5); - Intset_insert(&s, 8); - Intset_insert(&s, 3); - Intset_insert(&s, 5); - - for (c_each(k, Intset, s)) - printf("set %d\n", *k.ref); - Intset_drop(&s); -} -*/ - -#define _i_prefix sset_ -#define _i_is_set -#include "smap.h" diff --git a/src/finchlite/codegen/stc/stc/stack.h b/src/finchlite/codegen/stc/stc/stack.h deleted file mode 100644 index 871b0c75..00000000 --- a/src/finchlite/codegen/stc/stc/stack.h +++ /dev/null @@ -1,285 +0,0 @@ -/* MIT License - * - * Copyright (c) 2025 Tyge Løvset - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include "priv/linkage.h" -#include "types.h" - -// Stack - a simplified vec type without linear search and insert/erase inside the stack. - -#ifndef STC_STACK_H_INCLUDED -#define STC_STACK_H_INCLUDED -#include "common.h" -#include -#endif // STC_STACK_H_INCLUDED - -#ifndef _i_prefix - #define _i_prefix stack_ -#endif -#include "priv/template.h" -#ifndef i_declared - #if c_NUMARGS(i_type) == 4 - #define i_capacity i_val - #endif - #ifdef i_capacity - #define i_no_clone - _c_DEFTYPES(declare_stack_fixed, Self, i_key, i_capacity); - #else - _c_DEFTYPES(_declare_stack, Self, i_key, _i_aux_def); - #endif -#endif -typedef i_keyraw _m_raw; - -#ifdef i_capacity - STC_INLINE void _c_MEMB(_init)(Self* news) - { news->size = 0; } - - STC_INLINE isize _c_MEMB(_capacity)(const Self* self) - { (void)self; return i_capacity; } - - STC_INLINE bool _c_MEMB(_reserve)(Self* self, isize n) - { (void)self; return n <= i_capacity; } -#else - STC_INLINE Self _c_MEMB(_move)(Self *self) { - Self m = *self; - self->capacity = self->size = 0; - self->data = NULL; - return m; - } - - STC_INLINE isize _c_MEMB(_capacity)(const Self* self) - { return self->capacity; } - - STC_INLINE bool _c_MEMB(_reserve)(Self* self, isize n) { - if (n > self->capacity || (n && n == self->size)) { - _m_value *d = (_m_value *)_i_realloc_n(self->data, self->capacity, n); - if (d == NULL) - return false; - self->data = d; - self->capacity = n; - } - return self->data != NULL; - } -#endif // i_capacity - -STC_INLINE void _c_MEMB(_clear)(Self* self) { - if (self->size == 0) return; - _m_value *p = self->data + self->size; - while (p-- != self->data) { i_keydrop(p); } - self->size = 0; -} - -STC_INLINE void _c_MEMB(_drop)(const Self* cself) { - Self* self = (Self*)cself; - _c_MEMB(_clear)(self); -#ifndef i_capacity - _i_free_n(self->data, self->capacity); -#endif -} - -STC_INLINE void _c_MEMB(_take)(Self *self, Self unowned) { - _c_MEMB(_drop)(self); - *self = unowned; -} - -STC_INLINE isize _c_MEMB(_size)(const Self* self) - { return self->size; } - -STC_INLINE bool _c_MEMB(_is_empty)(const Self* self) - { return !self->size; } - -STC_INLINE void _c_MEMB(_value_drop)(const Self* self, _m_value* val) - { (void)self; i_keydrop(val); } - -STC_INLINE _m_value* _c_MEMB(_append_uninit)(Self *self, isize n) { - isize len = self->size; - if (len + n >= _c_MEMB(_capacity)(self)) - if (!_c_MEMB(_reserve)(self, len*3/2 + n)) - return NULL; - self->size += n; - return self->data + len; -} - -STC_INLINE void _c_MEMB(_shrink_to_fit)(Self* self) - { _c_MEMB(_reserve)(self, self->size); } - -STC_INLINE const _m_value* _c_MEMB(_front)(const Self* self) - { return &self->data[0]; } -STC_INLINE _m_value* _c_MEMB(_front_mut)(Self* self) - { return &self->data[0]; } - -STC_INLINE const _m_value* _c_MEMB(_back)(const Self* self) - { return &self->data[self->size - 1]; } -STC_INLINE _m_value* _c_MEMB(_back_mut)(Self* self) - { return &self->data[self->size - 1]; } - -STC_INLINE const _m_value* _c_MEMB(_top)(const Self* self) - { return _c_MEMB(_back)(self); } -STC_INLINE _m_value* _c_MEMB(_top_mut)(Self* self) - { return _c_MEMB(_back_mut)(self); } - -STC_INLINE _m_value* _c_MEMB(_push)(Self* self, _m_value val) { - if (self->size == _c_MEMB(_capacity)(self)) - if (!_c_MEMB(_reserve)(self, self->size*3/2 + 4)) - return NULL; - _m_value* vp = self->data + self->size++; - *vp = val; return vp; -} - -STC_INLINE void _c_MEMB(_pop)(Self* self) - { c_assert(self->size); _m_value* p = &self->data[--self->size]; i_keydrop(p); } - -STC_INLINE _m_value _c_MEMB(_pull)(Self* self) - { c_assert(self->size); return self->data[--self->size]; } - -#ifndef _i_no_put -STC_INLINE void _c_MEMB(_put_n)(Self* self, const _m_raw* raw, isize n) - { while (n--) _c_MEMB(_push)(self, i_keyfrom((*raw))), ++raw; } -#endif - -#if !defined _i_aux_alloc && !defined i_capacity - STC_INLINE Self _c_MEMB(_init)(void) - { Self out = {0}; return out; } - - STC_INLINE Self _c_MEMB(_with_capacity)(isize cap) - { Self out = {_i_new_n(_m_value, cap), 0, cap}; return out; } - - STC_INLINE Self _c_MEMB(_with_size_uninit)(isize size) - { Self out = {_i_new_n(_m_value, size), size, size}; return out; } - - STC_INLINE Self _c_MEMB(_with_size)(isize size, _m_raw default_raw) { - Self out = {_i_new_n(_m_value, size), size, size}; - while (size) out.data[--size] = i_keyfrom(default_raw); - return out; - } - - #ifndef _i_no_put - STC_INLINE Self _c_MEMB(_from_n)(const _m_raw* raw, isize n) { - Self out = _c_MEMB(_with_capacity)(n); - _c_MEMB(_put_n)(&out, raw, n); return out; - } - #endif -#endif - -STC_INLINE const _m_value* _c_MEMB(_at)(const Self* self, isize idx) - { c_assert(c_uless(idx, self->size)); return self->data + idx; } - -STC_INLINE _m_value* _c_MEMB(_at_mut)(Self* self, isize idx) - { c_assert(c_uless(idx, self->size)); return self->data + idx; } - -#ifndef i_no_emplace -STC_INLINE _m_value* _c_MEMB(_emplace)(Self* self, _m_raw raw) - { return _c_MEMB(_push)(self, i_keyfrom(raw)); } -#endif // !i_no_emplace - -#ifndef i_no_clone -STC_INLINE Self _c_MEMB(_clone)(Self stk) { - Self out = stk, *self = &out; (void)self; // i_keyclone may use self via i_aux - out.data = NULL; out.size = out.capacity = 0; - _c_MEMB(_reserve)(&out, stk.size); - out.size = stk.size; - for (c_range(i, stk.size)) - out.data[i] = i_keyclone(stk.data[i]); - return out; -} - -STC_INLINE void _c_MEMB(_copy)(Self *self, const Self* other) { - if (self == other) return; - _c_MEMB(_clear)(self); - _c_MEMB(_reserve)(self, other->size); - for (c_range(i, other->size)) - self->data[self->size++] = i_keyclone((other->data[i])); -} - -STC_INLINE _m_value _c_MEMB(_value_clone)(const Self* self, _m_value val) - { (void)self; return i_keyclone(val); } - -STC_INLINE _m_raw _c_MEMB(_value_toraw)(const _m_value* val) - { return i_keytoraw(val); } -#endif // !i_no_clone - -// iteration - -STC_INLINE _m_iter _c_MEMB(_begin)(const Self* self) { - _m_iter it = {(_m_value*)self->data, (_m_value*)self->data}; - if (self->size) it.end += self->size; - else it.ref = NULL; - return it; -} - -STC_INLINE _m_iter _c_MEMB(_rbegin)(const Self* self) { - _m_iter it = {(_m_value*)self->data, (_m_value*)self->data}; - if (self->size) { it.ref += self->size - 1; it.end -= 1; } - else it.ref = NULL; - return it; -} - -STC_INLINE _m_iter _c_MEMB(_end)(const Self* self) - { (void)self; _m_iter it = {0}; return it; } - -STC_INLINE _m_iter _c_MEMB(_rend)(const Self* self) - { (void)self; _m_iter it = {0}; return it; } - -STC_INLINE void _c_MEMB(_next)(_m_iter* it) - { if (++it->ref == it->end) it->ref = NULL; } - -STC_INLINE void _c_MEMB(_rnext)(_m_iter* it) - { if (--it->ref == it->end) it->ref = NULL; } - -STC_INLINE _m_iter _c_MEMB(_advance)(_m_iter it, size_t n) - { if ((it.ref += n) >= it.end) it.ref = NULL ; return it; } - -STC_INLINE isize _c_MEMB(_index)(const Self* self, _m_iter it) - { return (it.ref - self->data); } - -STC_INLINE void _c_MEMB(_adjust_end_)(Self* self, isize n) - { self->size += n; } - -#if defined _i_has_cmp -#include "priv/sort_prv.h" -#endif // _i_has_cmp - -#if defined _i_has_eq -STC_INLINE _m_iter _c_MEMB(_find_in)(const Self* self, _m_iter i1, _m_iter i2, _m_raw raw) { - (void)self; - const _m_value* p2 = i2.ref ? i2.ref : i1.end; - for (; i1.ref != p2; ++i1.ref) { - const _m_raw r = i_keytoraw(i1.ref); - if (i_eq((&raw), (&r))) - return i1; - } - i2.ref = NULL; - return i2; -} - -STC_INLINE _m_iter _c_MEMB(_find)(const Self* self, _m_raw raw) - { return _c_MEMB(_find_in)(self, _c_MEMB(_begin)(self), _c_MEMB(_end)(self), raw); } - -STC_INLINE bool _c_MEMB(_eq)(const Self* self, const Self* other) { - if (self->size != other->size) return false; - for (isize i = 0; i < self->size; ++i) { - const _m_raw _rx = i_keytoraw((self->data+i)), _ry = i_keytoraw((other->data+i)); - if (!(i_eq((&_rx), (&_ry)))) return false; - } - return true; -} -#endif // _i_has_eq -#include "sys/finalize.h" diff --git a/src/finchlite/codegen/stc/stc/sys/crange.h b/src/finchlite/codegen/stc/stc/sys/crange.h deleted file mode 100644 index c71cef61..00000000 --- a/src/finchlite/codegen/stc/stc/sys/crange.h +++ /dev/null @@ -1,118 +0,0 @@ -/* MIT License - * - * Copyright (c) 2025 Tyge Løvset - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. -*/ -/* -#include -#include - -int main(void) -{ - crange r1 = crange_make(80, 90); - for (c_each(i, crange, r1)) - printf(" %d", *i.ref); - puts(""); - - c_filter(crange, c_iota(100, INT_MAX, 10), true - && c_flt_skip(25) - && c_flt_take(3) - && printf(" %d", *value) - ); - puts(""); -} -*/ -// IWYU pragma: private, include "stc/algorithm.h" -#ifndef STC_CRANGE_H_INCLUDED -#define STC_CRANGE_H_INCLUDED - -#include "../priv/linkage.h" -#include "../common.h" - -// crange: isize range ----- - -typedef isize crange_value; -typedef struct { crange_value start, end, step, value; } crange; -typedef struct { crange_value *ref, end, step; } crange_iter; - -STC_INLINE crange crange_make_3(crange_value start, crange_value stop, crange_value step) - { crange r = {start, stop - (step > 0), step}; return r; } - -#define crange_make(...) c_MACRO_OVERLOAD(crange_make, __VA_ARGS__) -#define crange_make_1(stop) crange_make_3(0, stop, 1) // NB! arg is stop -#define crange_make_2(start, stop) crange_make_3(start, stop, 1) - -STC_INLINE crange_iter crange_begin(crange* self) { - self->value = self->start; - crange_iter it = {&self->value, self->end, self->step}; - return it; -} - -STC_INLINE void crange_next(crange_iter* it) { - if ((it->step > 0) == ((*it->ref += it->step) > it->end)) - it->ref = NULL; -} - -STC_INLINE crange_iter crange_advance(crange_iter it, size_t n) { - if ((it.step > 0) == ((*it.ref += it.step*(isize)n) > it.end)) - it.ref = NULL; - return it; -} - -// iota: c++-like std::iota, use in iterations on-the-fly ----- -// Note: c_iota() does not compile with c++, crange does. -#define c_iota(...) c_MACRO_OVERLOAD(c_iota, __VA_ARGS__) -#define c_iota_1(start) c_iota_3(start, INTPTR_MAX, 1) // NB! arg is start. -#define c_iota_2(start, stop) c_iota_3(start, stop, 1) -#define c_iota_3(start, stop, step) ((crange[]){crange_make_3(start, stop, step)})[0] - - -// crange32 ----- - -typedef int32_t crange32_value; -typedef struct { crange32_value start, end, step, value; } crange32; -typedef struct { crange32_value *ref, end, step; } crange32_iter; - -STC_INLINE crange32 crange32_make_3(crange32_value start, crange32_value stop, crange32_value step) - { crange32 r = {start, stop - (step > 0), step}; return r; } - -#define crange32_make(...) c_MACRO_OVERLOAD(crange32_make, __VA_ARGS__) -#define crange32_make_1(stop) crange32_make_3(0, stop, 1) // NB! arg is stop -#define crange32_make_2(start, stop) crange32_make_3(start, stop, 1) - -STC_INLINE crange32_iter crange32_begin(crange32* self) { - self->value = self->start; - crange32_iter it = {&self->value, self->end, self->step}; - return it; -} - -STC_INLINE void crange32_next(crange32_iter* it) { - if ((it->step > 0) == ((*it->ref += it->step) > it->end)) - it->ref = NULL; -} - -STC_INLINE crange32_iter crange32_advance(crange32_iter it, uint32_t n) { - if ((it.step > 0) == ((*it.ref += it.step*(int32_t)n) > it.end)) - it.ref = NULL; - return it; -} - -#include "../priv/linkage2.h" -#endif // STC_CRANGE_H_INCLUDE diff --git a/src/finchlite/codegen/stc/stc/sys/filter.h b/src/finchlite/codegen/stc/stc/sys/filter.h deleted file mode 100644 index 512e68db..00000000 --- a/src/finchlite/codegen/stc/stc/sys/filter.h +++ /dev/null @@ -1,185 +0,0 @@ -/* MIT License - * - * Copyright (c) 2025 Tyge Løvset - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. -*/ -/* -#include -#define T Vec, int -#include -#include - -int main(void) -{ - Vec vec = c_make(Vec, {1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 9, 10, 11, 12, 5}); - - c_filter(Vec, vec, true - && c_flt_skipwhile(*value < 3) // skip leading values < 3 - && (*value & 1) == 1 // then use odd values only - && c_flt_map(*value * 2) // multiply by 2 - && c_flt_takewhile(*value < 20) // stop if mapped *value >= 20 - && printf(" %d", *value) // print value - ); - // 6 10 14 2 6 18 - puts(""); - Vec_drop(&vec); -} -*/ -// IWYU pragma: private, include "stc/algorithm.h" -#ifndef STC_FILTER_H_INCLUDED -#define STC_FILTER_H_INCLUDED - -#include "../common.h" - -// ------- c_filter -------- -#define c_flt_take(n) _flt_take(&fltbase, n) -#define c_flt_skip(n) (c_flt_counter() > (n)) -#define c_flt_takewhile(pred) _flt_takewhile(&fltbase, pred) -#define c_flt_skipwhile(pred) (fltbase.sb[fltbase.sb_top++] |= !(pred)) -#define c_flt_counter() (++fltbase.sn[++fltbase.sn_top]) -#define c_flt_getcount() (fltbase.sn[fltbase.sn_top]) -#define c_flt_map(expr) (_mapped = (expr), value = &_mapped) -#define c_flt_src _it.ref - -#define c_filter(C, cnt, pred) \ - _c_filter(C, C##_begin(&cnt), _, pred) - -#define c_filter_from(C, start, pred) \ - _c_filter(C, start, _, pred) - -#define c_filter_reverse(C, cnt, pred) \ - _c_filter(C, C##_rbegin(&cnt), _r, pred) - -#define c_filter_reverse_from(C, start, pred) \ - _c_filter(C, start, _r, pred) - -#define _c_filter(C, start, rev, pred) do { \ - struct _flt_base fltbase = {0}; \ - C##_iter _it = start; \ - C##_value *value = _it.ref, _mapped = {0}; \ - for ((void)_mapped ; !fltbase.done & (_it.ref != NULL) ; \ - C##rev##next(&_it), value = _it.ref, fltbase.sn_top=0, fltbase.sb_top=0) \ - (void)(pred); \ -} while (0) - -// ------- c_filter_zip -------- -#define c_filter_zip(...) c_MACRO_OVERLOAD(c_filter_zip, __VA_ARGS__) -#define c_filter_zip_4(C, cnt1, cnt2, pred) \ - c_filter_zip_5(C, cnt1, C, cnt2, pred) -#define c_filter_zip_5(C1, cnt1, C2, cnt2, pred) \ - _c_filter_zip(C1, C1##_begin(&cnt1), C2, C2##_begin(&cnt2), _, pred) - -#define c_filter_reverse_zip(...) c_MACRO_OVERLOAD(c_filter_reverse_zip, __VA_ARGS__) -#define c_filter_reverse_zip_4(C, cnt1, cnt2, pred) \ - c_filter_reverse_zip_5(C, cnt1, C, cnt2, pred) -#define c_filter_reverse_zip_5(C1, cnt1, C2, cnt2, pred) \ - _c_filter_zip(C1, C1##_rbegin(&cnt1), C2, C2##_rbegin(&cnt2), _r, pred) - -#define c_filter_pairwise(C, cnt, pred) \ - _c_filter_zip(C, C##_begin(&cnt), C, C##_advance(_it1, 1), _, pred) - -#define c_flt_map1(expr) (_mapped1 = (expr), value1 = &_mapped1) -#define c_flt_map2(expr) (_mapped2 = (expr), value2 = &_mapped2) -#define c_flt_src1 _it1.ref -#define c_flt_src2 _it2.ref - -#define _c_filter_zip(C1, start1, C2, start2, rev, pred) do { \ - struct _flt_base fltbase = {0}; \ - C1##_iter _it1 = start1; \ - C2##_iter _it2 = start2; \ - C1##_value* value1 = _it1.ref, _mapped1; (void)_mapped1; \ - C2##_value* value2 = _it2.ref, _mapped2; (void)_mapped2; \ - for (; !fltbase.done & (_it1.ref != NULL) & (_it2.ref != NULL); \ - C1##rev##next(&_it1), value1 = _it1.ref, C2##rev##next(&_it2), value2 = _it2.ref, \ - fltbase.sn_top=0, fltbase.sb_top=0) \ - (void)(pred); \ -} while (0) - -// ------- c_ffilter -------- -// c_ffilter allows to execute imperative statements for each element -// in a for-loop, e.g., calling nested generic statements instead -// of defining a function/expression for it: -/* - Vec vec = ..., vec2 = ...; - for (c_ffilter(i, Vec, vec, true - && c_fflt_skipwhile(i, *i.ref < 3) // skip leading values < 3 - && (*i.ref & 1) == 1 // then use odd values only - && c_fflt_map(i, *i.ref * 2) // multiply by 2 - && c_fflt_takewhile(i, *i.ref < 20) // stop if mapped *i.ref >= 20 - )){ - c_eraseremove_if(Vec, &vec2, *value == *i.ref); - } -*/ -#define c_fflt_take(i, n) _flt_take(&i.base, n) -#define c_fflt_skip(i, n) (c_fflt_counter(i) > (n)) -#define c_fflt_takewhile(i, pred) _flt_takewhile(&i.base, pred) -#define c_fflt_skipwhile(i, pred) (i.base.sb[i.base.sb_top++] |= !(pred)) -#define c_fflt_counter(i) (++i.base.sn[++i.base.sn_top]) -#define c_fflt_getcount(i) (i.base.sn[i.base.sn_top]) -#define c_fflt_map(i, expr) (i.mapped = (expr), i.ref = &i.mapped) -#define c_fflt_src(i) i.iter.ref - -#define c_forfilter(...) for (c_ffilter(__VA_ARGS__)) -#define c_forfilter_from(...) for (c_ffilter_from(__VA_ARGS__)) -#define c_forfilter_reverse(...) for (c_ffilter_reverse(__VA_ARGS__)) -#define c_forfilter_reverse_from(...) for (c_ffilter_reverse_from(__VA_ARGS__)) - -#define c_ffilter(i, C, cnt, pred) \ - _c_ffilter(i, C, C##_begin(&cnt), _, pred) - -#define c_ffilter_from(i, C, start, pred) \ - _c_ffilter(i, C, start, _, pred) - -#define c_ffilter_reverse(i, C, cnt,pred) \ - _c_ffilter(i, C, C##_rbegin(&cnt), _r, pred) - -#define c_ffilter_reverse_from(i, C, start, pred) \ - _c_ffilter(i, C, start, _r, pred) - -#define _c_ffilter(i, C, start, rev, pred) \ - struct {C##_iter iter; C##_value *ref, mapped; struct _flt_base base;} \ - i = {.iter=start, .ref=i.iter.ref} ; !i.base.done & (i.iter.ref != NULL) ; \ - C##rev##next(&i.iter), i.ref = i.iter.ref, i.base.sn_top=0, i.base.sb_top=0) \ - if (!(pred)) ; else if (1 - -// ------------------------ private ------------------------- -#ifndef c_NFILTERS -#define c_NFILTERS 20 -#endif - -struct _flt_base { - uint8_t sn_top, sb_top; - bool done, sb[c_NFILTERS]; - uint32_t sn[c_NFILTERS]; -}; - -static inline bool _flt_take(struct _flt_base* base, uint32_t n) { - uint32_t k = ++base->sn[++base->sn_top]; - base->done |= (k >= n); - return n > 0; -} - -static inline bool _flt_takewhile(struct _flt_base* base, bool pred) { - bool skip = (base->sb[base->sb_top++] |= !pred); - base->done |= skip; - return !skip; -} - -#endif // STC_FILTER_H_INCLUDED diff --git a/src/finchlite/codegen/stc/stc/sys/finalize.h b/src/finchlite/codegen/stc/stc/sys/finalize.h deleted file mode 100644 index e7271fa4..00000000 --- a/src/finchlite/codegen/stc/stc/sys/finalize.h +++ /dev/null @@ -1,5 +0,0 @@ -#ifndef i_extend - #include "../priv/linkage2.h" - #include "../priv/template2.h" -#endif -#undef i_extend diff --git a/src/finchlite/codegen/stc/stc/sys/sumtype.h b/src/finchlite/codegen/stc/stc/sys/sumtype.h deleted file mode 100644 index 747a1064..00000000 --- a/src/finchlite/codegen/stc/stc/sys/sumtype.h +++ /dev/null @@ -1,172 +0,0 @@ -/* MIT License - * - * Copyright (c) 2025 Tyge Løvset - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -/* -// https://stackoverflow.com/questions/70935435/how-to-create-variants-in-rust -#include -#include -#include - -c_union (Action, - (ActionSpeak, cstr), - (ActionQuit, bool), - (ActionRunFunc, struct { - int32_t (*func)(int32_t, int32_t); - int32_t v1, v2; - }) -); - -void Action_drop(Action* self) { - if (c_is(self, ActionSpeak, s)) - cstr_drop(s); -} - -void action(Action* action) { - c_when (action) { - c_is(ActionSpeak, s) { - printf("Asked to speak: %s\n", cstr_str(s)); - } - c_is(ActionQuit) { - printf("Asked to quit!\n"); - } - c_is(ActionRunFunc, r) { - int32_t res = r->func(r->v1, r->v2); - printf("v1: %d, v2: %d, res: %d\n", r->v1, r->v2, res); - } - c_otherwise assert(!"no match"); - } -} - -int32_t add(int32_t a, int32_t b) { - return a + b; -} - -int main(void) { - Action act1 = c_variant(ActionSpeak, cstr_from("Hello")); - Action act2 = c_variant(ActionQuit, 1); - Action act3 = c_variant(ActionRunFunc, {add, 5, 6}); - - action(&act1); - action(&act2); - action(&act3); - - c_drop(Action, &act1, &act2, &act3); -} -*/ -#ifndef STC_SUMTYPE_H_INCLUDED -#define STC_SUMTYPE_H_INCLUDED - -#include "../common.h" - -#define _c_EMPTY() -#define _c_LOOP_INDIRECTION() c_LOOP -#define _c_LOOP_END_1 ,_c_LOOP1 -#define _c_LOOP0(f,T,x,...) f c_EXPAND((T, c_EXPAND x)) _c_LOOP_INDIRECTION _c_EMPTY()()(f,T,__VA_ARGS__) -#define _c_LOOP1(...) -#define _c_CHECK(x,...) c_TUPLE_AT_1(__VA_ARGS__,x,) -#define _c_E0(...) __VA_ARGS__ -#define _c_E1(...) _c_E0(_c_E0(_c_E0(_c_E0(_c_E0(_c_E0(__VA_ARGS__)))))) -#define _c_E2(...) _c_E1(_c_E1(_c_E1(_c_E1(_c_E1(_c_E1(__VA_ARGS__)))))) -#define c_EVAL(...) _c_E2(_c_E2(_c_E2(__VA_ARGS__))) // currently supports up to 130 variants -#define c_LOOP(f,T,x,...) _c_CHECK(_c_LOOP0, c_JOIN(_c_LOOP_END_, c_NUMARGS(c_EXPAND x)))(f,T,x,__VA_ARGS__) - - -#define _c_enum_1(x,...) (x=__LINE__*1000, __VA_ARGS__) -#define _c_vartuple_tag(T, Tag, ...) Tag, -#define _c_vartuple_type(T, Tag, ...) typedef __VA_ARGS__ Tag##_type; typedef T Tag##_sumtype; -#define _c_vartuple_var(T, Tag, ...) struct { enum enum_##T tag; Tag##_type get; } Tag; - -#define c_union(T, ...) \ - typedef union T T; \ - enum enum_##T { c_EVAL(c_LOOP(_c_vartuple_tag, T, _c_enum_1 __VA_ARGS__, (0),)) }; \ - c_EVAL(c_LOOP(_c_vartuple_type, T, __VA_ARGS__, (0),)) \ - union T { \ - struct { enum enum_##T tag; } _any_; \ - c_EVAL(c_LOOP(_c_vartuple_var, T, __VA_ARGS__, (0),)) \ - } -#define c_sumtype c_union - -#if defined STC_HAS_TYPEOF && STC_HAS_TYPEOF - #define c_when(varptr) \ - for (__typeof__(varptr) _vp1 = (varptr); _vp1; _vp1 = NULL) \ - switch (_vp1->_any_.tag) - - #define c_is_2(Tag, x) \ - break; case Tag: \ - for (__typeof__(_vp1->Tag.get)* x = &_vp1->Tag.get; x; x = NULL) - - #define c_is_3(varptr, Tag, x) \ - false) ; else for (__typeof__(varptr) _vp2 = (varptr); _vp2; _vp2 = NULL) \ - if (c_is_variant(_vp2, Tag)) \ - for (__typeof__(_vp2->Tag.get) *x = &_vp2->Tag.get; x; x = NULL -#else - typedef union { struct { int tag; } _any_; } _c_any_variant; - #define c_when(varptr) \ - for (_c_any_variant* _vp1 = (_c_any_variant *)(varptr); \ - _vp1; _vp1 = NULL, (void)sizeof((varptr)->_any_.tag)) \ - switch (_vp1->_any_.tag) - - #define c_is_2(Tag, x) \ - break; case Tag: \ - for (Tag##_type *x = &((Tag##_sumtype *)_vp1)->Tag.get; x; x = NULL) - - #define c_is_3(varptr, Tag, x) \ - false) ; else for (Tag##_sumtype* _vp2 = c_const_cast(Tag##_sumtype*, varptr); _vp2; _vp2 = NULL) \ - if (c_is_variant(_vp2, Tag)) \ - for (Tag##_type *x = &_vp2->Tag.get; x; x = NULL -#endif - -// Handling multiple tags with different payloads: -#define c_is(...) c_MACRO_OVERLOAD(c_is, __VA_ARGS__) -#define c_is_1(Tag) \ - break; case Tag: - -#define c_or_is(Tag) \ - ; case Tag: - -// Type checked multiple tags with same payload: -#define c_is_same(...) c_MACRO_OVERLOAD(c_is_same, __VA_ARGS__) -#define _c_chk(Tag1, Tag2) \ - case 1 ? Tag1 : sizeof((Tag1##_type*)0 == (Tag2##_type*)0): -#define c_is_same_2(Tag1, Tag2) \ - break; _c_chk(Tag1, Tag2) case Tag2: -#define c_is_same_3(Tag1, Tag2, Tag3) \ - break; _c_chk(Tag1, Tag2) _c_chk(Tag2, Tag3) case Tag3: -#define c_is_same_4(Tag1, Tag2, Tag3, Tag4) \ - break; _c_chk(Tag1, Tag2) _c_chk(Tag2, Tag3) _c_chk(Tag3, Tag4) case Tag4: - -#define c_otherwise \ - break; default: - -#define c_variant(Tag, ...) \ - (c_literal(Tag##_sumtype){.Tag={.tag=Tag, .get=__VA_ARGS__}}) - -#define c_is_variant(varptr, Tag) \ - ((varptr)->Tag.tag == Tag) - -#define c_get_if(varptr, Tag) \ - (c_is_variant(varptr, Tag) ? &(varptr)->Tag.get : NULL) - -#define c_variant_id(varptr) \ - ((int)(varptr)->_any_.tag) - -#endif // STC_SUMTYPE_H_INCLUDED diff --git a/src/finchlite/codegen/stc/stc/sys/utility.h b/src/finchlite/codegen/stc/stc/sys/utility.h deleted file mode 100644 index b4ed1b64..00000000 --- a/src/finchlite/codegen/stc/stc/sys/utility.h +++ /dev/null @@ -1,188 +0,0 @@ -/* MIT License - * - * Copyright (c) 2025 Tyge Løvset - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. -*/ -// IWYU pragma: private, include "stc/algorithm.h" -#ifndef STC_UTILITY_H_INCLUDED -#define STC_UTILITY_H_INCLUDED - -// -------------------------------- -// c_find_if, c_find_reverse_if -// -------------------------------- - -#define c_find_if(...) c_MACRO_OVERLOAD(c_find_if, __VA_ARGS__) -#define c_find_if_4(C, cnt, outit_ptr, pred) \ - _c_find(C, C##_begin(&cnt), NULL, _, outit_ptr, pred) - -#define c_find_if_5(C, start, finish, outit_ptr, pred) \ - _c_find(C, start, (finish).ref, _, outit_ptr, pred) - -#define c_find_reverse_if(...) c_MACRO_OVERLOAD(c_find_reverse_if, __VA_ARGS__) -#define c_find_reverse_if_4(C, cnt, outit_ptr, pred) \ - _c_find(C, C##_rbegin(&cnt), NULL, _r, outit_ptr, pred) - -#define c_find_reverse_if_5(C, rstart, rfinish, outit_ptr, pred) \ - _c_find(C, rstart, (rfinish).ref, _r, outit_ptr, pred) - -// private -#define _c_find(C, start, endref, rev, outit_ptr, pred) do { \ - C##_iter* _out = outit_ptr; \ - const C##_value *value, *_endref = endref; \ - for (*_out = start; (value = _out->ref) != _endref; C##rev##next(_out)) \ - if (pred) goto c_JOIN(findif_, __LINE__); \ - _out->ref = NULL; c_JOIN(findif_, __LINE__):; \ -} while (0) - -// -------------------------------- -// c_reverse -// -------------------------------- - -#define c_reverse_array(array, n) do { \ - typedef struct { char d[sizeof 0[array]]; } _etype; \ - _etype* _arr = (_etype *)(array); \ - for (isize _i = 0, _j = (n) - 1; _i < _j; ++_i, --_j) \ - c_swap(_arr + _i, _arr + _j); \ -} while (0) - -// Compiles with vec, stack, and deque, and cspan container types: -#define c_reverse(CntType, self) do { \ - CntType* _self = self; \ - for (isize _i = 0, _j = CntType##_size(_self) - 1; _i < _j; ++_i, --_j) \ - c_swap(CntType##_at_mut(_self, _i), CntType##_at_mut(_self, _j)); \ -} while (0) - -// -------------------------------- -// c_erase_if -// -------------------------------- - -// Use with: list, hashmap, hashset, sortedmap, sortedset: -#define c_erase_if(C, cnt_ptr, pred) do { \ - C* _cnt = cnt_ptr; \ - const C##_value* value; \ - for (C##_iter _it = C##_begin(_cnt); (value = _it.ref); ) { \ - if (pred) _it = C##_erase_at(_cnt, _it); \ - else C##_next(&_it); \ - } \ -} while (0) - -// -------------------------------- -// c_eraseremove_if -// -------------------------------- - -// Use with: stack, vec, deque, queue: -#define c_eraseremove_if(C, cnt_ptr, pred) do { \ - C* _cnt = cnt_ptr; \ - isize _n = 0; \ - const C##_value* value; \ - C##_iter _i, _it = C##_begin(_cnt); \ - while ((value = _it.ref) && !(pred)) \ - C##_next(&_it); \ - for (_i = _it; (value = _it.ref); C##_next(&_it)) { \ - if (pred) C##_value_drop(_cnt, _it.ref), ++_n; \ - else *_i.ref = *_it.ref, C##_next(&_i); \ - } \ - C##_adjust_end_(_cnt, -_n); \ -} while (0) - -// -------------------------------- -// c_copy_to, c_copy_if -// -------------------------------- - -#define c_copy_to(...) c_MACRO_OVERLOAD(c_copy_to, __VA_ARGS__) -#define c_copy_to_3(C, outcnt_ptr, cnt) \ - _c_copy_if(C, outcnt_ptr, _, C, cnt, true) - -#define c_copy_to_4(C_out, outcnt_ptr, C, cnt) \ - _c_copy_if(C_out, outcnt_ptr, _, C, cnt, true) - -#define c_copy_if(...) c_MACRO_OVERLOAD(c_copy_if, __VA_ARGS__) -#define c_copy_if_4(C, outcnt_ptr, cnt, pred) \ - _c_copy_if(C, outcnt_ptr, _, C, cnt, pred) - -#define c_copy_if_5(C_out, outcnt_ptr, C, cnt, pred) \ - _c_copy_if(C_out, outcnt_ptr, _, C, cnt, pred) - -// private -#define _c_copy_if(C_out, outcnt_ptr, rev, C, cnt, pred) do { \ - C_out *_out = outcnt_ptr; \ - C _cnt = cnt; \ - const C##_value* value; \ - for (C##_iter _it = C##rev##begin(&_cnt); (value = _it.ref); C##rev##next(&_it)) \ - if (pred) C_out##_push(_out, C_out##_value_clone(_out, *_it.ref)); \ -} while (0) - -// -------------------------------- -// c_all_of, c_any_of, c_none_of -// -------------------------------- - -#define c_all_of(C, cnt, outbool_ptr, pred) do { \ - C##_iter _it; \ - c_find_if_4(C, cnt, &_it, !(pred)); \ - *(outbool_ptr) = _it.ref == NULL; \ -} while (0) - -#define c_any_of(C, cnt, outbool_ptr, pred) do { \ - C##_iter _it; \ - c_find_if_4(C, cnt, &_it, pred); \ - *(outbool_ptr) = _it.ref != NULL; \ -} while (0) - -#define c_none_of(C, cnt, outbool_ptr, pred) do { \ - C##_iter _it; \ - c_find_if_4(C, cnt, &_it, pred); \ - *(outbool_ptr) = _it.ref == NULL; \ -} while (0) - -// -------------------------------- -// c_min, c_max, c_min_n, c_max_n -// -------------------------------- -#define _c_minmax_call(fn, T, ...) \ - fn(c_make_array(T, {__VA_ARGS__}), c_sizeof((T[]){__VA_ARGS__})/c_sizeof(T)) - -#define c_min(...) _c_minmax_call(c_min_n, isize, __VA_ARGS__) -#define c_umin(...) _c_minmax_call(c_umin_n, size_t, __VA_ARGS__) -#define c_min32(...) _c_minmax_call(c_min32_n, int32_t, __VA_ARGS__) -#define c_fmin(...) _c_minmax_call(c_fmin_n, float, __VA_ARGS__) -#define c_dmin(...) _c_minmax_call(c_dmin_n, double, __VA_ARGS__) -#define c_max(...) _c_minmax_call(c_max_n, isize, __VA_ARGS__) -#define c_umax(...) _c_minmax_call(c_umax_n, size_t, __VA_ARGS__) -#define c_max32(...) _c_minmax_call(c_max32_n, int32_t, __VA_ARGS__) -#define c_fmax(...) _c_minmax_call(c_fmax_n, float, __VA_ARGS__) -#define c_dmax(...) _c_minmax_call(c_dmax_n, double, __VA_ARGS__) - -#define _c_minmax_def(fn, T, opr) \ - static inline T fn(const T a[], isize n) { \ - T x = a[0]; \ - for (isize i = 1; i < n; ++i) if (a[i] opr x) x = a[i]; \ - return x; \ - } -_c_minmax_def(c_min32_n, int32_t, <) -_c_minmax_def(c_min_n, isize, <) -_c_minmax_def(c_umin_n, size_t, <) -_c_minmax_def(c_fmin_n, float, <) -_c_minmax_def(c_dmin_n, double, <) -_c_minmax_def(c_max32_n, int32_t, >) -_c_minmax_def(c_max_n, isize, >) -_c_minmax_def(c_umax_n, size_t, >) -_c_minmax_def(c_fmax_n, float, >) -_c_minmax_def(c_dmax_n, double, >) - -#endif // STC_UTILITY_H_INCLUDED diff --git a/src/finchlite/codegen/stc/stc/types.h b/src/finchlite/codegen/stc/stc/types.h deleted file mode 100644 index 73af610c..00000000 --- a/src/finchlite/codegen/stc/stc/types.h +++ /dev/null @@ -1,223 +0,0 @@ -/* MIT License - * - * Copyright (c) 2025 Tyge Løvset - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef STC_TYPES_H_INCLUDED -#define STC_TYPES_H_INCLUDED - -#include -#include -#include - -#define declare_rc(C, KEY) declare_arc(C, KEY) -#define declare_list(C, KEY) _declare_list(C, KEY,) -#define declare_stack(C, KEY) _declare_stack(C, KEY,) -#define declare_vec(C, KEY) _declare_stack(C, KEY,) -#define declare_pqueue(C, KEY) _declare_stack(C, KEY,) -#define declare_queue(C, KEY) _declare_queue(C, KEY,) -#define declare_deque(C, KEY) _declare_queue(C, KEY,) -#define declare_hashmap(C, KEY, VAL) _declare_htable(C, KEY, VAL, c_true, c_false,) -#define declare_hashset(C, KEY) _declare_htable(C, KEY, KEY, c_false, c_true,) -#define declare_sortedmap(C, KEY, VAL) _declare_aatree(C, KEY, VAL, c_true, c_false,) -#define declare_sortedset(C, KEY) _declare_aatree(C, KEY, KEY, c_false, c_true,) - -#define declare_list_aux(C, KEY, AUX) _declare_list(C, KEY, AUX aux;) -#define declare_stack_aux(C, KEY, AUX) _declare_stack(C, KEY, AUX aux;) -#define declare_vec_aux(C, KEY, AUX) _declare_stack(C, KEY, AUX aux;) -#define declare_pqueue_aux(C, KEY, AUX) _declare_stack(C, KEY, AUX aux;) -#define declare_queue_aux(C, KEY, AUX) _declare_queue(C, KEY, AUX aux;) -#define declare_deque_aux(C, KEY, AUX) _declare_queue(C, KEY, AUX aux;) -#define declare_hashmap_aux(C, KEY, VAL, AUX) _declare_htable(C, KEY, VAL, c_true, c_false, AUX aux;) -#define declare_hashset_aux(C, KEY, AUX) _declare_htable(C, KEY, KEY, c_false, c_true, AUX aux;) -#define declare_sortedmap_aux(C, KEY, VAL, AUX) _declare_aatree(C, KEY, VAL, c_true, c_false, AUX aux;) -#define declare_sortedset_aux(C, KEY, AUX) _declare_aatree(C, KEY, KEY, c_false, c_true, AUX aux;) - -// csview : non-null terminated string view -typedef const char csview_value; -typedef struct csview { - csview_value* buf; - ptrdiff_t size; -} csview; - -typedef union { - csview_value* ref; - csview chr; - struct { csview chr; csview_value* end; } u8; -} csview_iter; - -#define c_sv(...) c_MACRO_OVERLOAD(c_sv, __VA_ARGS__) -#define c_sv_1(literal) c_sv_2(literal, c_litstrlen(literal)) -#define c_sv_2(str, n) (c_literal(csview){str, n}) -#define c_svfmt "%.*s" -#define c_svarg(sv) (int)(sv).size, (sv).buf // printf(c_svfmt "\n", c_svarg(sv)); - -// zsview : zero-terminated string view -typedef csview_value zsview_value; -typedef struct zsview { - zsview_value* str; - ptrdiff_t size; -} zsview; - -typedef union { - zsview_value* ref; - csview chr; -} zsview_iter; - -#define c_zv(literal) (c_literal(zsview){literal, c_litstrlen(literal)}) - -// cstr : zero-terminated owning string (short string optimized - sso) -typedef char cstr_value; -typedef struct { cstr_value* data; intptr_t size, cap; } cstr_buf; -typedef union cstr { - struct { cstr_buf *a, *b, *c; } _dummy; - struct { cstr_value* data; uintptr_t size; uintptr_t ncap; } lon; - struct { cstr_value data[ sizeof(cstr_buf) - 1 ]; uint8_t size; } sml; -} cstr; - -typedef union { - csview chr; // utf8 character/codepoint - const cstr_value* ref; -} cstr_iter; - -#define c_true(...) __VA_ARGS__ -#define c_false(...) - -#define declare_arc(SELF, VAL) \ - typedef VAL SELF##_value; \ - typedef struct SELF##_ctrl SELF##_ctrl; \ -\ - typedef union SELF { \ - SELF##_value* get; \ - SELF##_ctrl* ctrl; \ - } SELF - -#define declare_arc2(SELF, VAL) \ - typedef VAL SELF##_value; \ - typedef struct SELF##_ctrl SELF##_ctrl; \ -\ - typedef struct SELF { \ - SELF##_value* get; \ - SELF##_ctrl* ctrl2; \ - } SELF - -#define declare_box(SELF, VAL) \ - typedef VAL SELF##_value; \ -\ - typedef struct SELF { \ - SELF##_value* get; \ - } SELF - -#define _declare_queue(SELF, VAL, AUXDEF) \ - typedef VAL SELF##_value; \ -\ - typedef struct SELF { \ - SELF##_value *cbuf; \ - ptrdiff_t start, end, capmask; \ - AUXDEF \ - } SELF; \ -\ - typedef struct { \ - SELF##_value *ref; \ - ptrdiff_t pos; \ - const SELF* _s; \ - } SELF##_iter - -#define _declare_list(SELF, VAL, AUXDEF) \ - typedef VAL SELF##_value; \ - typedef struct SELF##_node SELF##_node; \ -\ - typedef struct { \ - SELF##_value *ref; \ - SELF##_node *const *_last, *prev; \ - } SELF##_iter; \ -\ - typedef struct SELF { \ - SELF##_node *last; \ - AUXDEF \ - } SELF - -#define _declare_htable(SELF, KEY, VAL, MAP_ONLY, SET_ONLY, AUXDEF) \ - typedef KEY SELF##_key; \ - typedef VAL SELF##_mapped; \ -\ - typedef SET_ONLY( SELF##_key ) \ - MAP_ONLY( struct SELF##_value ) \ - SELF##_value, SELF##_entry; \ -\ - typedef struct { \ - SELF##_value *ref; \ - size_t idx; \ - bool inserted; \ - uint8_t hashx; \ - uint16_t dist; \ - } SELF##_result; \ -\ - typedef struct { \ - SELF##_value *ref, *_end; \ - struct hmap_meta *_mref; \ - } SELF##_iter; \ -\ - typedef struct SELF { \ - SELF##_value* table; \ - struct hmap_meta* meta; \ - ptrdiff_t size, bucket_count; \ - AUXDEF \ - } SELF - -#define _declare_aatree(SELF, KEY, VAL, MAP_ONLY, SET_ONLY, AUXDEF) \ - typedef KEY SELF##_key; \ - typedef VAL SELF##_mapped; \ - typedef struct SELF##_node SELF##_node; \ -\ - typedef SET_ONLY( SELF##_key ) \ - MAP_ONLY( struct SELF##_value ) \ - SELF##_value, SELF##_entry; \ -\ - typedef struct { \ - SELF##_value *ref; \ - bool inserted; \ - } SELF##_result; \ -\ - typedef struct { \ - SELF##_value *ref; \ - SELF##_node *_d; \ - int _top; \ - int32_t _tn, _st[36]; \ - } SELF##_iter; \ -\ - typedef struct SELF { \ - SELF##_node *nodes; \ - int32_t root, disp, head, size, capacity; \ - AUXDEF \ - } SELF - -#define declare_stack_fixed(SELF, VAL, CAP) \ - typedef VAL SELF##_value; \ - typedef struct { SELF##_value *ref, *end; } SELF##_iter; \ - typedef struct SELF { SELF##_value data[CAP]; ptrdiff_t size; } SELF - -#define _declare_stack(SELF, VAL, AUXDEF) \ - typedef VAL SELF##_value; \ - typedef struct { SELF##_value *ref, *end; } SELF##_iter; \ - typedef struct SELF { SELF##_value *data; ptrdiff_t size, capacity; AUXDEF } SELF - -#endif // STC_TYPES_H_INCLUDED diff --git a/src/finchlite/codegen/stc/stc/utf8.h b/src/finchlite/codegen/stc/stc/utf8.h deleted file mode 100644 index 3f91b652..00000000 --- a/src/finchlite/codegen/stc/stc/utf8.h +++ /dev/null @@ -1,37 +0,0 @@ -/* MIT License - * - * Copyright (c) 2025 Tyge Løvset - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include "priv/linkage.h" - -#ifndef STC_UTF8_H_INCLUDED -#define STC_UTF8_H_INCLUDED - -#include "common.h" // IWYU pragma: keep -#include "types.h" -#include "priv/utf8_prv.h" // IWYU pragma: keep - -#endif // STC_UTF8_H_INCLUDED - -#if defined i_implement - #include "priv/utf8_prv.c" -#endif -#include "priv/linkage2.h" diff --git a/src/finchlite/codegen/stc/stc/vec.h b/src/finchlite/codegen/stc/stc/vec.h deleted file mode 100644 index bf5f7faa..00000000 --- a/src/finchlite/codegen/stc/stc/vec.h +++ /dev/null @@ -1,397 +0,0 @@ -/* MIT License - * - * Copyright (c) 2025 Tyge Løvset - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/* -#define i_implement -#include -#include - -declare_vec(vec_i32, int); - -typedef struct MyStruct { - vec_i32 int_vec; - cstr name; -} MyStruct; - -#define i_key float -#include - -#define i_keypro cstr // cstr is a "pro"-type -#include - -#define T vec_i32, int32_t, (c_declared) -#include - -int main(void) { - vec_i32 vec = {0}; - vec_i32_push(&vec, 123); - vec_i32_drop(&vec); - - vec_float fvec = {0}; - vec_float_push(&fvec, 123.3); - vec_float_drop(&fvec); - - vec_cstr svec = {0}; - vec_cstr_emplace(&svec, "Hello, friend"); - vec_cstr_drop(&svec); -} -*/ -#include "priv/linkage.h" -#include "types.h" - -#ifndef STC_VEC_H_INCLUDED -#define STC_VEC_H_INCLUDED -#include "common.h" -#include - -#define _it2_ptr(it1, it2) (it1.ref && !it2.ref ? it1.end : it2.ref) -#define _it_ptr(it) (it.ref ? it.ref : it.end) -#endif // STC_VEC_H_INCLUDED - -#ifndef _i_prefix - #define _i_prefix vec_ -#endif -#include "priv/template.h" - -#ifndef i_declared - _c_DEFTYPES(_declare_stack, Self, i_key, _i_aux_def); -#endif -typedef i_keyraw _m_raw; -STC_API void _c_MEMB(_drop)(const Self* cself); -STC_API void _c_MEMB(_clear)(Self* self); -STC_API bool _c_MEMB(_reserve)(Self* self, isize cap); -STC_API bool _c_MEMB(_resize)(Self* self, isize size, _m_value null); -STC_API _m_iter _c_MEMB(_erase_n)(Self* self, isize idx, isize n); -STC_API _m_iter _c_MEMB(_insert_uninit)(Self* self, isize idx, isize n); -#if defined _i_has_eq -STC_API _m_iter _c_MEMB(_find_in)(const Self* self, _m_iter it1, _m_iter it2, _m_raw raw); -#endif // _i_has_eq - -STC_INLINE void _c_MEMB(_value_drop)(const Self* self, _m_value* val) - { (void)self; i_keydrop(val); } - -STC_INLINE Self _c_MEMB(_move)(Self *self) { - Self m = *self; - self->capacity = self->size = 0; - self->data = NULL; - return m; -} - -STC_INLINE void _c_MEMB(_take)(Self *self, Self unowned) { - _c_MEMB(_drop)(self); - *self = unowned; -} - -STC_INLINE _m_value* _c_MEMB(_push)(Self* self, _m_value value) { - if (self->size == self->capacity) - if (!_c_MEMB(_reserve)(self, self->size*2 + 4)) - return NULL; - _m_value *v = self->data + self->size++; - *v = value; - return v; -} - -#ifndef _i_no_put -STC_INLINE void _c_MEMB(_put_n)(Self* self, const _m_raw* raw, isize n) - { while (n--) _c_MEMB(_push)(self, i_keyfrom((*raw))), ++raw; } -#endif - -#ifndef i_no_emplace -STC_API _m_iter _c_MEMB(_emplace_n)(Self* self, isize idx, const _m_raw raw[], isize n); - -STC_INLINE _m_value* _c_MEMB(_emplace)(Self* self, _m_raw raw) - { return _c_MEMB(_push)(self, i_keyfrom(raw)); } - -STC_INLINE _m_value* _c_MEMB(_emplace_back)(Self* self, _m_raw raw) - { return _c_MEMB(_push)(self, i_keyfrom(raw)); } - -STC_INLINE _m_iter _c_MEMB(_emplace_at)(Self* self, _m_iter it, _m_raw raw) - { return _c_MEMB(_emplace_n)(self, _it_ptr(it) - self->data, &raw, 1); } -#endif // !i_no_emplace - -#ifndef i_no_clone -STC_API void _c_MEMB(_copy)(Self* self, const Self* other); -STC_API _m_iter _c_MEMB(_copy_to)(Self* self, isize idx, const _m_value arr[], isize n); -STC_API Self _c_MEMB(_clone)(Self vec); -STC_INLINE _m_value _c_MEMB(_value_clone)(const Self* self, _m_value val) - { (void)self; return i_keyclone(val); } -#endif // !i_no_clone - -STC_INLINE isize _c_MEMB(_size)(const Self* self) { return self->size; } -STC_INLINE isize _c_MEMB(_capacity)(const Self* self) { return self->capacity; } -STC_INLINE bool _c_MEMB(_is_empty)(const Self* self) { return !self->size; } -STC_INLINE _m_raw _c_MEMB(_value_toraw)(const _m_value* val) { return i_keytoraw(val); } -STC_INLINE const _m_value* _c_MEMB(_front)(const Self* self) { return self->data; } -STC_INLINE _m_value* _c_MEMB(_front_mut)(Self* self) { return self->data; } -STC_INLINE const _m_value* _c_MEMB(_back)(const Self* self) { return &self->data[self->size - 1]; } -STC_INLINE _m_value* _c_MEMB(_back_mut)(Self* self) { return &self->data[self->size - 1]; } - -STC_INLINE void _c_MEMB(_pop)(Self* self) - { c_assert(self->size); _m_value* p = &self->data[--self->size]; i_keydrop(p); } -STC_INLINE _m_value _c_MEMB(_pull)(Self* self) - { c_assert(self->size); return self->data[--self->size]; } -STC_INLINE _m_value* _c_MEMB(_push_back)(Self* self, _m_value value) - { return _c_MEMB(_push)(self, value); } -STC_INLINE void _c_MEMB(_pop_back)(Self* self) { _c_MEMB(_pop)(self); } - -#ifndef _i_aux_alloc - STC_INLINE Self _c_MEMB(_init)(void) - { return c_literal(Self){0}; } - - STC_INLINE Self _c_MEMB(_with_capacity)(isize cap) - { Self out = {_i_new_n(_m_value, cap), 0, cap}; return out; } - - STC_INLINE Self _c_MEMB(_with_size_uninit)(isize size) - { Self out = {_i_new_n(_m_value, size), size, size}; return out; } - - STC_INLINE Self _c_MEMB(_with_size)(isize size, _m_raw default_raw) { - Self out = {_i_new_n(_m_value, size), size, size}; - while (size) out.data[--size] = i_keyfrom(default_raw); - return out; - } - - #ifndef _i_no_put - STC_INLINE Self _c_MEMB(_from_n)(const _m_raw* raw, isize n) { - Self out = _c_MEMB(_with_capacity)(n); - _c_MEMB(_put_n)(&out, raw, n); return out; - } - #endif -#endif - -STC_INLINE void _c_MEMB(_shrink_to_fit)(Self* self) - { _c_MEMB(_reserve)(self, _c_MEMB(_size)(self)); } - -STC_INLINE _m_iter -_c_MEMB(_insert_n)(Self* self, const isize idx, const _m_value arr[], const isize n) { - _m_iter it = _c_MEMB(_insert_uninit)(self, idx, n); - if (it.ref) - c_memcpy(it.ref, arr, n*c_sizeof *arr); - return it; -} - -STC_INLINE _m_iter _c_MEMB(_insert_at)(Self* self, _m_iter it, const _m_value value) { - return _c_MEMB(_insert_n)(self, _it_ptr(it) - self->data, &value, 1); -} - -STC_INLINE _m_iter _c_MEMB(_erase_at)(Self* self, _m_iter it) { - return _c_MEMB(_erase_n)(self, it.ref - self->data, 1); -} - -STC_INLINE _m_iter _c_MEMB(_erase_range)(Self* self, _m_iter i1, _m_iter i2) { - return _c_MEMB(_erase_n)(self, i1.ref - self->data, _it2_ptr(i1, i2) - i1.ref); -} - -STC_INLINE const _m_value* _c_MEMB(_at)(const Self* self, const isize idx) { - c_assert(c_uless(idx, self->size)); return self->data + idx; -} - -STC_INLINE _m_value* _c_MEMB(_at_mut)(Self* self, const isize idx) { - c_assert(c_uless(idx, self->size)); return self->data + idx; -} - -// iteration - -STC_INLINE _m_iter _c_MEMB(_begin)(const Self* self) { - _m_iter it = {(_m_value*)self->data, (_m_value*)self->data}; - if (self->size) it.end += self->size; - else it.ref = NULL; - return it; -} - -STC_INLINE _m_iter _c_MEMB(_rbegin)(const Self* self) { - _m_iter it = {(_m_value*)self->data, (_m_value*)self->data}; - if (self->size) { it.ref += self->size - 1; it.end -= 1; } - else it.ref = NULL; - return it; -} - -STC_INLINE _m_iter _c_MEMB(_end)(const Self* self) - { (void)self; _m_iter it = {0}; return it; } - -STC_INLINE _m_iter _c_MEMB(_rend)(const Self* self) - { (void)self; _m_iter it = {0}; return it; } - -STC_INLINE void _c_MEMB(_next)(_m_iter* it) - { if (++it->ref == it->end) it->ref = NULL; } - -STC_INLINE void _c_MEMB(_rnext)(_m_iter* it) - { if (--it->ref == it->end) it->ref = NULL; } - -STC_INLINE _m_iter _c_MEMB(_advance)(_m_iter it, size_t n) { - if ((it.ref += n) >= it.end) it.ref = NULL; - return it; -} - -STC_INLINE isize _c_MEMB(_index)(const Self* self, _m_iter it) - { return (it.ref - self->data); } - -STC_INLINE void _c_MEMB(_adjust_end_)(Self* self, isize n) - { self->size += n; } - -#if defined _i_has_eq -STC_INLINE _m_iter _c_MEMB(_find)(const Self* self, _m_raw raw) { - return _c_MEMB(_find_in)(self, _c_MEMB(_begin)(self), _c_MEMB(_end)(self), raw); -} - -STC_INLINE bool _c_MEMB(_eq)(const Self* self, const Self* other) { - if (self->size != other->size) return false; - for (isize i = 0; i < self->size; ++i) { - const _m_raw _rx = i_keytoraw((self->data+i)), _ry = i_keytoraw((other->data+i)); - if (!(i_eq((&_rx), (&_ry)))) return false; - } - return true; -} -#endif // _i_has_eq - -#if defined _i_has_cmp -#include "priv/sort_prv.h" -#endif // _i_has_cmp - -/* -------------------------- IMPLEMENTATION ------------------------- */ -#if defined i_implement - -STC_DEF void -_c_MEMB(_clear)(Self* self) { - if (self->size == 0) return; - _m_value *p = self->data + self->size; - while (p-- != self->data) { i_keydrop(p); } - self->size = 0; -} - -STC_DEF void -_c_MEMB(_drop)(const Self* cself) { - Self* self = (Self*)cself; - if (self->capacity == 0) - return; - _c_MEMB(_clear)(self); - _i_free_n(self->data, self->capacity); -} - -STC_DEF bool -_c_MEMB(_reserve)(Self* self, const isize cap) { - if (cap > self->capacity || (cap && cap == self->size)) { - _m_value* d = (_m_value*)_i_realloc_n(self->data, self->capacity, cap); - if (d == NULL) - return false; - self->data = d; - self->capacity = cap; - } - return self->data != NULL; -} - -STC_DEF bool -_c_MEMB(_resize)(Self* self, const isize len, _m_value null) { - if (!_c_MEMB(_reserve)(self, len)) - return false; - const isize n = self->size; - for (isize i = len; i < n; ++i) - { i_keydrop((self->data + i)); } - for (isize i = n; i < len; ++i) - self->data[i] = null; - self->size = len; - return true; -} - -STC_DEF _m_iter -_c_MEMB(_insert_uninit)(Self* self, const isize idx, const isize n) { - if (self->size + n >= self->capacity) - if (!_c_MEMB(_reserve)(self, self->size*3/2 + n)) - return _c_MEMB(_end)(self); - - _m_value *pos = self->data + idx; - c_memmove(pos + n, pos, (self->size - idx)*c_sizeof *pos); - self->size += n; - return c_literal(_m_iter){pos, self->data + self->size}; -} - -STC_DEF _m_iter -_c_MEMB(_erase_n)(Self* self, const isize idx, const isize len) { - c_assert(idx + len <= self->size); - _m_value* d = self->data + idx, *p = d, *end = self->data + self->size; - for (isize i = 0; i < len; ++i, ++p) - { i_keydrop(p); } - memmove(d, p, (size_t)(end - p)*sizeof *d); - self->size -= len; - return c_literal(_m_iter){p == end ? NULL : d, end - len}; -} - -#ifndef i_no_clone -STC_DEF void -_c_MEMB(_copy)(Self* self, const Self* other) { - if (self == other) return; - _c_MEMB(_clear)(self); - _c_MEMB(_reserve)(self, other->size); - self->size = other->size; - for (c_range(i, other->size)) - self->data[i] = i_keyclone((other->data[i])); -} - -STC_DEF Self -_c_MEMB(_clone)(Self vec) { - Self out = vec, *self = &out; (void)self; - out.data = NULL; out.size = out.capacity = 0; - _c_MEMB(_reserve)(&out, vec.size); - out.size = vec.size; - for (c_range(i, vec.size)) - out.data[i] = i_keyclone(vec.data[i]); - return out; -} - -STC_DEF _m_iter -_c_MEMB(_copy_to)(Self* self, const isize idx, - const _m_value arr[], const isize n) { - _m_iter it = _c_MEMB(_insert_uninit)(self, idx, n); - if (it.ref) - for (_m_value* p = it.ref, *q = p + n; p != q; ++arr) - *p++ = i_keyclone((*arr)); - return it; -} -#endif // !i_no_clone - -#ifndef i_no_emplace -STC_DEF _m_iter -_c_MEMB(_emplace_n)(Self* self, const isize idx, const _m_raw raw[], isize n) { - _m_iter it = _c_MEMB(_insert_uninit)(self, idx, n); - if (it.ref) - for (_m_value* p = it.ref; n--; ++raw, ++p) - *p = i_keyfrom((*raw)); - return it; -} -#endif // !i_no_emplace - -#if defined _i_has_eq -STC_DEF _m_iter -_c_MEMB(_find_in)(const Self* self, _m_iter i1, _m_iter i2, _m_raw raw) { - (void)self; - const _m_value* p2 = _it2_ptr(i1, i2); - for (; i1.ref != p2; ++i1.ref) { - const _m_raw r = i_keytoraw(i1.ref); - if (i_eq((&raw), (&r))) - return i1; - } - i2.ref = NULL; - return i2; -} -#endif // _i_has_eq -#endif // i_implement -#include "sys/finalize.h" diff --git a/src/finchlite/codegen/stc/stc/zsview.h b/src/finchlite/codegen/stc/stc/zsview.h deleted file mode 100644 index 8afb73a6..00000000 --- a/src/finchlite/codegen/stc/stc/zsview.h +++ /dev/null @@ -1,173 +0,0 @@ -/* MIT License - * - * Copyright (c) 2025 Tyge Løvset - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -// zsview is a zero-terminated string view. - -#ifndef STC_ZSVIEW_H_INCLUDED -#define STC_ZSVIEW_H_INCLUDED - -#include "common.h" -#include "types.h" -#include "priv/utf8_prv.h" - -#define zsview_init() c_zv("") -#define zsview_clone(zs) c_default_clone(zs) -#define zsview_drop(self) c_default_drop(self) -#define zsview_toraw(self) (self)->str - -STC_INLINE zsview zsview_from(const char* str) - { return c_literal(zsview){str, c_strlen(str)}; } -STC_INLINE void zsview_clear(zsview* self) { *self = c_zv(""); } -STC_INLINE csview zsview_sv(zsview zs) { return c_sv_2(zs.str, zs.size); } - -STC_INLINE isize zsview_size(zsview zs) { return zs.size; } -STC_INLINE bool zsview_is_empty(zsview zs) { return zs.size == 0; } - -STC_INLINE bool zsview_equals(zsview zs, const char* str) { - isize n = c_strlen(str); - return zs.size == n && !c_memcmp(zs.str, str, n); -} - -STC_INLINE isize zsview_find(zsview zs, const char* search) { - const char* res = strstr(zs.str, search); - return res ? (res - zs.str) : c_NPOS; -} - -STC_INLINE bool zsview_contains(zsview zs, const char* str) - { return zsview_find(zs, str) != c_NPOS; } - -STC_INLINE bool zsview_starts_with(zsview zs, const char* str) { - isize n = c_strlen(str); - return n <= zs.size && !c_memcmp(zs.str, str, n); -} - -STC_INLINE bool zsview_ends_with(zsview zs, const char* str) { - isize n = c_strlen(str); - return n <= zs.size && !c_memcmp(zs.str + zs.size - n, str, n); -} - -STC_INLINE zsview zsview_from_pos(zsview zs, isize pos) { - if (pos > zs.size) pos = zs.size; - zs.str += pos; zs.size -= pos; return zs; -} - -STC_INLINE csview zsview_subview(zsview zs, isize pos, isize len) { - c_assert(((size_t)pos <= (size_t)zs.size) & (len >= 0)); - if (pos + len > zs.size) len = zs.size - pos; - return c_literal(csview){zs.str + pos, len}; -} - -STC_INLINE zsview zsview_tail(zsview zs, isize len) { - c_assert(len >= 0); - if (len > zs.size) len = zs.size; - zs.str += zs.size - len; zs.size = len; - return zs; -} - -/* utf8 */ - -STC_INLINE zsview zsview_u8_from_pos(zsview zs, isize u8pos) - { return zsview_from_pos(zs, utf8_to_index(zs.str, u8pos)); } - -STC_INLINE zsview zsview_u8_tail(zsview zs, isize u8len) { - const char* p = &zs.str[zs.size]; - while (u8len && p != zs.str) - u8len -= (*--p & 0xC0) != 0x80; - zs.size -= p - zs.str, zs.str = p; - return zs; -} - -STC_INLINE csview zsview_u8_subview(zsview zs, isize u8pos, isize u8len) - { return utf8_subview(zs.str, u8pos, u8len); } - -STC_INLINE zsview_iter zsview_u8_at(zsview zs, isize u8pos) { - csview sv; - sv.buf = utf8_at(zs.str, u8pos); - sv.size = utf8_chr_size(sv.buf); - return c_literal(zsview_iter){.chr = sv}; -} - -STC_INLINE isize zsview_u8_size(zsview zs) - { return utf8_count(zs.str); } - -STC_INLINE bool zsview_u8_valid(zsview zs) // requires linking with utf8 symbols - { return utf8_valid_n(zs.str, zs.size); } - -/* utf8 iterator */ - -STC_INLINE zsview_iter zsview_begin(const zsview* self) { - zsview_iter it = {.chr = {self->str, utf8_chr_size(self->str)}}; return it; -} - -STC_INLINE zsview_iter zsview_end(const zsview* self) { - (void)self; zsview_iter it = {0}; return it; -} - -STC_INLINE void zsview_next(zsview_iter* it) { - it->ref += it->chr.size; - it->chr.size = utf8_chr_size(it->ref); - if (*it->ref == '\0') it->ref = NULL; -} - -STC_INLINE zsview_iter zsview_advance(zsview_iter it, isize u8pos) { - it.ref = utf8_offset(it.ref, u8pos); - it.chr.size = utf8_chr_size(it.ref); - if (*it.ref == '\0') it.ref = NULL; - return it; -} - -/* ---- Container helper functions ---- */ - -STC_INLINE size_t zsview_hash(const zsview *self) - { return c_hash_str(self->str); } - -STC_INLINE int zsview_cmp(const zsview* x, const zsview* y) - { return strcmp(x->str, y->str); } - -STC_INLINE bool zsview_eq(const zsview* x, const zsview* y) - { return x->size == y->size && !c_memcmp(x->str, y->str, x->size); } - -STC_INLINE int zsview_icmp(const zsview* x, const zsview* y) - { return utf8_icmp(x->str, y->str); } - -STC_INLINE bool zsview_ieq(const zsview* x, const zsview* y) - { return x->size == y->size && !utf8_icmp(x->str, y->str); } - -/* ---- case insensitive ---- */ - -STC_INLINE bool zsview_iequals(zsview zs, const char* str) - { return c_strlen(str) == zs.size && !utf8_icmp(zs.str, str); } - -STC_INLINE bool zsview_istarts_with(zsview zs, const char* str) - { return c_strlen(str) <= zs.size && !utf8_icmp(zs.str, str); } - -STC_INLINE bool zsview_iends_with(zsview zs, const char* str) { - isize n = c_strlen(str); - return n <= zs.size && !utf8_icmp(zs.str + zs.size - n, str); -} - -#endif // STC_ZSVIEW_H_INCLUDED - -#if defined i_import - #include "priv/utf8_prv.c" -#endif From def082a14b8f4bff3e072de0d6eb34fef0f5b40c Mon Sep 17 00:00:00 2001 From: Juni Kim Date: Tue, 2 Dec 2025 09:26:57 -0500 Subject: [PATCH 29/44] turn STC into a submodule tests seem to be working --- .gitmodules | 3 +++ src/finchlite/codegen/hashtable.py | 2 +- src/finchlite/codegen/stc | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 .gitmodules create mode 160000 src/finchlite/codegen/stc diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..18159e86 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "src/finchlite/codegen/stc"] + path = src/finchlite/codegen/stc + url = https://github.com/stclib/stc diff --git a/src/finchlite/codegen/hashtable.py b/src/finchlite/codegen/hashtable.py index c523339a..6e0a5e17 100644 --- a/src/finchlite/codegen/hashtable.py +++ b/src/finchlite/codegen/hashtable.py @@ -24,7 +24,7 @@ from finchlite.finch_assembly.nodes import AssemblyExpression, Stack from finchlite.finch_assembly.struct import AssemblyStructFType, TupleFType -stcpath = Path(__file__).parent / "stc" +stcpath = Path(__file__).parent / "stc" / "include" hashmap_h = stcpath / "stc" / "hashmap.h" diff --git a/src/finchlite/codegen/stc b/src/finchlite/codegen/stc new file mode 160000 index 00000000..19db5051 --- /dev/null +++ b/src/finchlite/codegen/stc @@ -0,0 +1 @@ +Subproject commit 19db5051157da5525b3b4cfced4e33810985bd94 From 6efc28a8cf7eda7138723fa1f57b10c1045bfc0f Mon Sep 17 00:00:00 2001 From: Juni Kim Date: Tue, 2 Dec 2025 09:32:52 -0500 Subject: [PATCH 30/44] recursive submodules for checkout --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 475290ac..111ff5fb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,6 +19,8 @@ jobs: steps: - name: Checkout Repo uses: actions/checkout@v4 + with: + submodules: recursive - uses: actions/setup-python@v5 with: python-version: ${{ matrix.python }} From 7f70069ed07cd80cbadd2b413d358921e03d9971 Mon Sep 17 00:00:00 2001 From: Juni Kim Date: Tue, 2 Dec 2025 09:33:41 -0500 Subject: [PATCH 31/44] recursive submodules on the publish file too --- .github/workflows/publish.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index dcf2d3f0..8d95ebca 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -7,6 +7,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 + with: + submodules: recursive - name: Set up Python uses: actions/setup-python@v2 with: From 69d89e1fc529ea1043e0449d3cf25e3f71f2daf5 Mon Sep 17 00:00:00 2001 From: Juni Kim Date: Tue, 2 Dec 2025 09:58:04 -0500 Subject: [PATCH 32/44] simplify numpy serialization --- src/finchlite/codegen/c.py | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/src/finchlite/codegen/c.py b/src/finchlite/codegen/c.py index 1600060c..953536ef 100644 --- a/src/finchlite/codegen/c.py +++ b/src/finchlite/codegen/c.py @@ -13,14 +13,13 @@ import numpy as np -from finchlite.finch_assembly.struct import MutableStructFType - from .. import finch_assembly as asm from ..algebra import query_property, register_property from ..finch_assembly import ( AssemblyStructFType, BufferFType, ImmutableStructFType, + MutableStructFType, TupleFType, ) from ..symbolic import Context, Namespace, ScopedDict, fisinstance, ftype @@ -212,21 +211,11 @@ def construct_from_c(fmt, c_obj): register_property(t, "numba_type", "__attr__", lambda t: t) -def scalar_to_ctypes_copy(fmt, obj): - """ - This hack is required because it turns out that scalars don't own memory or smth - """ - arr = np.array([obj], dtype=obj.dtype, copy=True) - scalar_ctype = np.ctypeslib.as_ctypes_type(obj.dtype) - ptr_ctype = ctypes.POINTER(scalar_ctype) - return arr.ctypes.data_as(ptr_ctype).contents - - register_property( np.generic, "serialize_to_c", "__attr__", - scalar_to_ctypes_copy, + lambda fmt, obj: np.ctypeslib.as_ctypes(np.array(obj)), ) # pass by value -> no op From 27193c04226ea0e94cee6bd05c23e59a4b941d75 Mon Sep 17 00:00:00 2001 From: Juni Kim Date: Tue, 2 Dec 2025 15:54:13 -0500 Subject: [PATCH 33/44] allow the c hashtable to accept arbitrary assembly structs as POD --- src/finchlite/codegen/hashtable.py | 112 +++++++++++------------------ tests/test_codegen.py | 35 +++++---- 2 files changed, 64 insertions(+), 83 deletions(-) diff --git a/src/finchlite/codegen/hashtable.py b/src/finchlite/codegen/hashtable.py index 6e0a5e17..4e0e87b2 100644 --- a/src/finchlite/codegen/hashtable.py +++ b/src/finchlite/codegen/hashtable.py @@ -13,6 +13,7 @@ c_type, construct_from_c, load_shared_lib, + serialize_to_c ) from finchlite.codegen.numba_backend import ( NumbaContext, @@ -93,25 +94,20 @@ class CHashTable(Dict): CHashTable class that basically connects up to an STC library. """ - libraries: dict[tuple[int, int], CHashTableLibrary] = {} + libraries: dict[tuple[AssemblyStructFType, AssemblyStructFType], CHashTableLibrary] = {} @classmethod def gen_code( cls, ctx: "CContext", - key_type: "TupleFType", - value_type: "TupleFType", + key_type: "AssemblyStructFType", + value_type: "AssemblyStructFType", inline: bool = False, ) -> tuple[CHashMethods, str]: - assert isinstance(key_type, TupleFType) - assert isinstance(value_type, TupleFType) - - key_len = len(key_type.struct_fields) - value_len = len(value_type.struct_fields) # dereference both key and value types; as given, they are both pointers. keytype_c = ctx.ctype_name(c_type(key_type)) valuetype_c = ctx.ctype_name(c_type(value_type)) - hmap_t = ctx.freshen("hmap", key_len, value_len) + hmap_t = ctx.freshen("hmap") ctx.add_header("#include ") @@ -122,14 +118,14 @@ def gen_code( ctx.headers.append(f'#include "{hashmap_h}"') methods: CHashMethods = { - "init": ctx.freshen("finch_hmap_init", key_len, value_len), - "exists": ctx.freshen("finch_hmap_exists", key_len, value_len), - "load": ctx.freshen("finch_hmap_load", key_len, value_len), - "store": ctx.freshen("finch_hmap_store", key_len, value_len), - "cleanup": ctx.freshen("finch_hmap_cleanup", key_len, value_len), + "init": ctx.freshen("finch_hmap_init"), + "exists": ctx.freshen("finch_hmap_exists"), + "load": ctx.freshen("finch_hmap_load"), + "store": ctx.freshen("finch_hmap_store"), + "cleanup": ctx.freshen("finch_hmap_cleanup"), } # register these methods in the datastructures. - ctx.datastructures[CHashTableFType(key_len, value_len)] = methods + ctx.datastructures[CHashTableFType(key_type, value_type)] = methods inline_s = "static inline " if inline else "" # basically for the load functions, you need to provide a variable that @@ -179,15 +175,12 @@ def gen_code( return methods, hmap_t @classmethod - def compile(cls, key_len: int, value_len: int) -> CHashTableLibrary: + def compile(cls, key_type: AssemblyStructFType, value_type: AssemblyStructFType) -> CHashTableLibrary: """ compile a library to use for the c hash table. """ - if (key_len, value_len) in cls.libraries: - return cls.libraries[(key_len, value_len)] - key_type = _int_tuple_ftype(key_len) - value_type = _int_tuple_ftype(value_len) - + if (key_type, value_type) in cls.libraries: + return cls.libraries[(key_type, value_type)] ctx = CContext() methods, hmap_t = cls.gen_code(ctx, key_type, value_type) code = ctx.emit_global() @@ -228,66 +221,47 @@ def compile(cls, key_len: int, value_len: int) -> CHashTableLibrary: cleanup_func.argtypes = [ctypes.c_void_p] cleanup_func.restype = None - cls.libraries[(key_len, value_len)] = CHashTableLibrary(lib, methods, hmap_t) - return cls.libraries[(key_len, value_len)] + cls.libraries[(key_type, value_type)] = CHashTableLibrary(lib, methods, hmap_t) + return cls.libraries[(key_type, value_type)] def __init__( - self, key_len: int, value_len: int, map: "dict[tuple,tuple] | None" = None + self, key_type, value_type, map: "dict | None" = None ): """ Constructor for the C Hash Table """ - self.lib = self.__class__.compile(key_len, value_len) - - self.key_len = key_len - self.value_len = value_len + self.lib = self.__class__.compile(key_type, value_type) # these are blank fields we need when serializing or smth self._struct: Any = None self._self_obj: Any = None + self._key_type = key_type + self._value_type = value_type + if map is None: map = {} self.dct = getattr(self.lib.library, self.lib.methods["init"])() for key, value in map.items(): - if not _is_integer_tuple(key, key_len): - raise TypeError( - f"Supplied key {key} is not a tuple of {key_len} integers" - ) - if not _is_integer_tuple(value, value_len): - raise TypeError( - f"Supplied value {key} is not a tuple of {value_len} integers" - ) + # if some error happens, the serialization will handle it. self.store(key, value) def __del__(self): getattr(self.lib.library, self.lib.methods["cleanup"])(self.dct) def exists(self, idx) -> bool: - idx = _tuplify(self.ftype.key_type, idx) - assert _is_integer_tuple(idx, self.key_len) - KeyStruct = c_type(self.ftype.key_type) - c_key = KeyStruct(*idx) + c_key = serialize_to_c(self.ftype.key_type, idx) c_value = getattr(self.lib.library, self.lib.methods["exists"])(self.dct, c_key) return bool(c_value) def load(self, idx): - idx = _tuplify(self.ftype.key_type, idx) - assert _is_integer_tuple(idx, self.key_len) - KeyStruct = c_type(self.ftype.key_type) - c_key = KeyStruct(*idx) + c_key = serialize_to_c(self.ftype.key_type, idx) c_value = getattr(self.lib.library, self.lib.methods["load"])(self.dct, c_key) return construct_from_c(self.ftype.value_type, c_value) def store(self, idx, val): - idx = _tuplify(self.ftype.key_type, idx) - val = _tuplify(self.ftype.value_type, val) - assert _is_integer_tuple(idx, self.key_len) - assert _is_integer_tuple(val, self.value_len) - KeyStruct = c_type(self.ftype.key_type) - ValueStruct = c_type(self.ftype.value_type) - c_key = KeyStruct(*idx) - c_value = ValueStruct(*val) + c_key = serialize_to_c(self.ftype.key_type, idx) + c_value = serialize_to_c(self.ftype.value_type, val) getattr(self.lib.library, self.lib.methods["store"])(self.dct, c_key, c_value) def __str__(self): @@ -295,7 +269,7 @@ def __str__(self): @property def ftype(self): - return CHashTableFType(self.key_len, self.value_len) + return CHashTableFType(self._key_type, self._value_type) class CHashTableFType(CDictFType, CStackFType): @@ -303,25 +277,25 @@ class CHashTableFType(CDictFType, CStackFType): An implementation of Hash Tables using the stc library. """ - def __init__(self, key_len: int, value_len: int): - self.key_len = key_len - self.value_len = value_len - self._key_type = _int_tuple_ftype(key_len) - self._value_type = _int_tuple_ftype(value_len) + def __init__(self, key_type: AssemblyStructFType, value_type: AssemblyStructFType): + # these should both be immutable structs/POD types. + # we will enforce this once the immutable struct PR is merged. + self._key_type = key_type + self._value_type = value_type def __eq__(self, other): if not isinstance(other, CHashTableFType): return False - return self.key_len == other.key_len and self.value_len == other.value_len + return self.key_type == other.key_type and self.value_type == other.value_type def __call__(self): - return CHashTable(self.key_len, self.value_len, {}) + return CHashTable(self.key_type, self.value_type, {}) def __str__(self): - return f"chashtable_t({self.key_len}, {self.value_len})" + return f"chashtable_t({self.key_type}, {self.value_type})" def __repr__(self): - return f"CHashTableFType({self.key_len}, {self.value_len})" + return f"CHashTableFType({self.key_type}, {self.value_type})" @property def key_type(self): @@ -344,7 +318,7 @@ def __hash__(self): This method needs to be here because you are going to be using this type as a key in dictionaries. """ - return hash(("CHashTableFType", self.key_len, self.value_len)) + return hash(("CHashTableFType", self.key_type, self.value_type)) """ Methods for the C Backend @@ -389,7 +363,7 @@ def c_unpack(self, ctx: "CContext", var_n: str, val: AssemblyExpression): data = ctx.freshen(var_n, "data") # Add all the stupid header stuff from above. ctx.add_datastructure( - ("CHashTableFType", self.key_len, self.value_len), + ("CHashTableFType", self.key_type, self.value_type), lambda ctx: CHashTable.gen_code( ctx, self.key_type, self.value_type, inline=True ), @@ -410,8 +384,8 @@ def serialize_to_c(self, obj: CHashTable): This datatype will then immediately get turned into a struct. """ assert isinstance(obj, CHashTable) - map = ctypes.c_void_p(obj.dct) - struct = CHashTableStruct(map, obj) + dct = ctypes.c_void_p(obj.dct) + struct = CHashTableStruct(dct, obj) # We NEED this for stupid ownership reasons. obj._self_obj = ctypes.py_object(obj) obj._struct = struct @@ -426,7 +400,7 @@ def deserialize_from_c(self, obj: CHashTable, res): obj.dct = res.contents.map - def construct_from_c(self, c_map): + def construct_from_c(self, c_dct): """ Construct a CHashTable from a C-compatible structure. @@ -634,8 +608,8 @@ def construct_from_numba(self, numba_map): if __name__ == "__main__": - table = NumbaHashTable(2, 3, {(1, 2): (1, 4, 3)}) - print(numba_type(table.ftype.key_type)) + table = CHashTable(2, 3, {(1, 2): (1, 4, 3)}) + print(c_type(table.ftype.key_type)) table.store((2, 3), (3, 2, 3)) print(table.exists((2, 3))) print(table.load((2, 3))) diff --git a/tests/test_codegen.py b/tests/test_codegen.py index 7ca75fa8..b8c1dcdb 100644 --- a/tests/test_codegen.py +++ b/tests/test_codegen.py @@ -6,9 +6,8 @@ from collections import namedtuple from pathlib import Path -import pytest - import numpy as np +import pytest from numpy.testing import assert_equal import finchlite @@ -23,11 +22,7 @@ NumpyBufferFType, SafeBuffer, ) -from finchlite.codegen.c import ( - construct_from_c, - deserialize_from_c, - serialize_to_c, -) +from finchlite.codegen.c import construct_from_c, deserialize_from_c, serialize_to_c from finchlite.codegen.hashtable import CHashTable, NumbaHashTable from finchlite.codegen.malloc_buffer import MallocBuffer from finchlite.codegen.numba_backend import ( @@ -933,16 +928,28 @@ def test_e2e_numba(): @pytest.mark.parametrize( - ["compiler", "tabletype"], + ["compiler", "constructor"], [ - (CCompiler(), CHashTable), - (asm.AssemblyInterpreter(), CHashTable), - (NumbaCompiler(), NumbaHashTable), - (asm.AssemblyInterpreter(), NumbaHashTable), + ( + CCompiler(), + lambda: CHashTable( + asm.TupleFType.from_tuple((int, int)), + asm.TupleFType.from_tuple((int, int, int)), + ), + ), + ( + asm.AssemblyInterpreter(), + lambda: CHashTable( + asm.TupleFType.from_tuple((int, int)), + asm.TupleFType.from_tuple((int, int, int)), + ), + ), + (NumbaCompiler(), lambda: NumbaHashTable(2, 3)), + (asm.AssemblyInterpreter(), lambda: NumbaHashTable(2, 3)), ], ) -def test_hashtable(compiler, tabletype): - table = tabletype(2, 3) +def test_hashtable(compiler, constructor): + table = constructor() table_v = asm.Variable("a", ftype(table)) table_slt = asm.Slot("a_", ftype(table)) From ed10c5cc6f0857fcaa04a8f0405a2071a80c4006 Mon Sep 17 00:00:00 2001 From: Juni Kim Date: Tue, 2 Dec 2025 17:47:50 -0500 Subject: [PATCH 34/44] add additional test for c hash table definitions --- tests/test_codegen.py | 63 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/tests/test_codegen.py b/tests/test_codegen.py index b8c1dcdb..7f70fa39 100644 --- a/tests/test_codegen.py +++ b/tests/test_codegen.py @@ -1000,3 +1000,66 @@ def test_hashtable(compiler, constructor): assert not compiled.exists(table, key_type(1, 3)) assert not compiled.exists(table, val_type(2, 3)) + + +@pytest.mark.parametrize( + ["compiler"], + [ + (CCompiler(),), + (asm.AssemblyInterpreter(),), + ], +) +def test_multiple_c_hashtable(compiler): + """ + This test exists because in the case of C, we might need to dump multiple + hash table definitions into the context. + """ + + def _tupletype(arity): + return asm.TupleFType.from_tuple(tuple(int for _ in range(arity))) + + def func(table: CHashTable, num: int): + key_type = table.ftype.key_type + val_type = table.ftype.value_type + key_v = asm.Variable("key", key_type) + val_v = asm.Variable("val", val_type) + table_v = asm.Variable("a", ftype(table)) + table_slt = asm.Slot("a_", ftype(table)) + return asm.Function( + asm.Variable(f"setidx_{num}", val_type), + (table_v, key_v, val_v), + asm.Block( + ( + asm.Unpack(table_slt, table_v), + asm.StoreDict( + table_slt, + key_v, + val_v, + ), + asm.Repack(table_slt), + asm.Return(asm.LoadDict(table_slt, key_v)), + ) + ), + ) + + table1 = CHashTable(_tupletype(2), _tupletype(3)) + table2 = CHashTable(_tupletype(1), _tupletype(4)) + + mod = compiler( + asm.Module( + ( + func(table1, 1), + func(table2, 2), + ) + ) + ) + + # what's important here is that you can call setidx_1 on table1 and + # setidx_2 on table2. + assert mod.setidx_1( + table1, table1.key_type(1, 2), table1.value_type(2, 3, 4) + ) == table1.value_type(2, 3, 4) + + assert mod.setidx_2( + table2, table2.key_type(1), table2.value_type(2, 3, 4, 5) + ) == table2.value_type(2, 3, 4, 5) From e5653ec5e179e232d280cc762442b204cf439137 Mon Sep 17 00:00:00 2001 From: Juni Kim Date: Tue, 2 Dec 2025 18:13:29 -0500 Subject: [PATCH 35/44] fix the type checker test bad constructor --- tests/test_assembly_type_checker.py | 30 +++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/tests/test_assembly_type_checker.py b/tests/test_assembly_type_checker.py index 6ce50a8f..13fea56a 100644 --- a/tests/test_assembly_type_checker.py +++ b/tests/test_assembly_type_checker.py @@ -689,14 +689,19 @@ def test_simple_struct(): @pytest.mark.parametrize( - ["tabletype"], + ["constructor"], [ - (CHashTable,), - (NumbaHashTable,), + ( + lambda: CHashTable( + asm.TupleFType.from_tuple((int, int)), + asm.TupleFType.from_tuple((int, int, int)), + ), + ), + (lambda: NumbaHashTable(2, 3),), ], ) -def test_hashtable(tabletype): - table = tabletype(2, 3) +def test_hashtable(constructor): + table = constructor() table_v = asm.Variable("a", ftype(table)) table_slt = asm.Slot("a_", ftype(table)) @@ -742,14 +747,19 @@ def test_hashtable(tabletype): @pytest.mark.parametrize( - ["tabletype"], + ["constructor"], [ - (CHashTable,), - (NumbaHashTable,), + ( + lambda: CHashTable( + asm.TupleFType.from_tuple((int, int)), + asm.TupleFType.from_tuple((int, int, int)), + ), + ), + (lambda: NumbaHashTable(2, 3),), ], ) -def test_hashtable_fail(tabletype): - table = tabletype(2, 3) +def test_hashtable_fail(constructor): + table = constructor() table_v = asm.Variable("a", ftype(table)) table_slt = asm.Slot("a_", ftype(table)) From e608efdce1afb9f0cfa52b517b5f99b511a5752b Mon Sep 17 00:00:00 2001 From: Juni Kim Date: Tue, 2 Dec 2025 18:21:31 -0500 Subject: [PATCH 36/44] test floats valid --- tests/test_codegen.py | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/tests/test_codegen.py b/tests/test_codegen.py index 7f70fa39..c7aa87cd 100644 --- a/tests/test_codegen.py +++ b/tests/test_codegen.py @@ -1015,7 +1015,7 @@ def test_multiple_c_hashtable(compiler): hash table definitions into the context. """ - def _tupletype(arity): + def _int_tupletype(arity): return asm.TupleFType.from_tuple(tuple(int for _ in range(arity))) def func(table: CHashTable, num: int): @@ -1042,14 +1042,24 @@ def func(table: CHashTable, num: int): ), ) - table1 = CHashTable(_tupletype(2), _tupletype(3)) - table2 = CHashTable(_tupletype(1), _tupletype(4)) + table1 = CHashTable(asm.TupleFType.from_tuple((int, float)), _int_tupletype(3)) + table2 = CHashTable(_int_tupletype(1), _int_tupletype(4)) + table3 = CHashTable( + asm.TupleFType.from_tuple((int, float)), + asm.TupleFType.from_tuple( + ( + float, + float, + ) + ), + ) mod = compiler( asm.Module( ( func(table1, 1), func(table2, 2), + func(table3, 3), ) ) ) @@ -1057,9 +1067,13 @@ def func(table: CHashTable, num: int): # what's important here is that you can call setidx_1 on table1 and # setidx_2 on table2. assert mod.setidx_1( - table1, table1.key_type(1, 2), table1.value_type(2, 3, 4) + table1, table1.key_type(1, 2.0), table1.value_type(2, 3, 4) ) == table1.value_type(2, 3, 4) assert mod.setidx_2( table2, table2.key_type(1), table2.value_type(2, 3, 4, 5) ) == table2.value_type(2, 3, 4, 5) + + assert mod.setidx_3( + table3, table3.key_type(1, 2.0), table3.value_type(1.434, 1.4) + ) == table3.value_type(1.434, 1.4) From 08caa87746b0f1efa5e7481bd85fc300b7d3316d Mon Sep 17 00:00:00 2001 From: Juni Kim Date: Tue, 2 Dec 2025 18:47:39 -0500 Subject: [PATCH 37/44] mypy? --- pyproject.toml | 2 +- src/finchlite/codegen/hashtable.py | 2 +- tests/test_codegen.py | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 6f597405..f090f75a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -58,4 +58,4 @@ section-order = [ [tool.mypy] ignore_missing_imports = true -exclude = ["tests/reference"] +exclude = ["tests/reference", "src/finchlite/codegen/stc"] diff --git a/src/finchlite/codegen/hashtable.py b/src/finchlite/codegen/hashtable.py index 4e0e87b2..03fe078f 100644 --- a/src/finchlite/codegen/hashtable.py +++ b/src/finchlite/codegen/hashtable.py @@ -13,7 +13,7 @@ c_type, construct_from_c, load_shared_lib, - serialize_to_c + serialize_to_c, ) from finchlite.codegen.numba_backend import ( NumbaContext, diff --git a/tests/test_codegen.py b/tests/test_codegen.py index c7aa87cd..561b4250 100644 --- a/tests/test_codegen.py +++ b/tests/test_codegen.py @@ -6,8 +6,9 @@ from collections import namedtuple from pathlib import Path -import numpy as np import pytest + +import numpy as np from numpy.testing import assert_equal import finchlite From ec01d7dae6f3747dd85a78cc839c2e6a8d55da5c Mon Sep 17 00:00:00 2001 From: Juni Kim Date: Tue, 2 Dec 2025 19:14:56 -0500 Subject: [PATCH 38/44] ruff --- src/finchlite/codegen/hashtable.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/finchlite/codegen/hashtable.py b/src/finchlite/codegen/hashtable.py index 03fe078f..61ce661e 100644 --- a/src/finchlite/codegen/hashtable.py +++ b/src/finchlite/codegen/hashtable.py @@ -94,7 +94,9 @@ class CHashTable(Dict): CHashTable class that basically connects up to an STC library. """ - libraries: dict[tuple[AssemblyStructFType, AssemblyStructFType], CHashTableLibrary] = {} + libraries: dict[ + tuple[AssemblyStructFType, AssemblyStructFType], CHashTableLibrary + ] = {} @classmethod def gen_code( @@ -175,7 +177,9 @@ def gen_code( return methods, hmap_t @classmethod - def compile(cls, key_type: AssemblyStructFType, value_type: AssemblyStructFType) -> CHashTableLibrary: + def compile( + cls, key_type: AssemblyStructFType, value_type: AssemblyStructFType + ) -> CHashTableLibrary: """ compile a library to use for the c hash table. """ @@ -224,9 +228,7 @@ def compile(cls, key_type: AssemblyStructFType, value_type: AssemblyStructFType) cls.libraries[(key_type, value_type)] = CHashTableLibrary(lib, methods, hmap_t) return cls.libraries[(key_type, value_type)] - def __init__( - self, key_type, value_type, map: "dict | None" = None - ): + def __init__(self, key_type, value_type, map: "dict | None" = None): """ Constructor for the C Hash Table """ From 21cc9942e9b872b5d4861e180922b5bf4fb00d85 Mon Sep 17 00:00:00 2001 From: Juni Kim Date: Tue, 2 Dec 2025 19:52:25 -0500 Subject: [PATCH 39/44] I can't believe I'm debugging in prod --- src/finchlite/codegen/hashtable.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/finchlite/codegen/hashtable.py b/src/finchlite/codegen/hashtable.py index 61ce661e..cc217fc6 100644 --- a/src/finchlite/codegen/hashtable.py +++ b/src/finchlite/codegen/hashtable.py @@ -112,6 +112,7 @@ def gen_code( hmap_t = ctx.freshen("hmap") ctx.add_header("#include ") + ctx.add_header("#include ") # these headers should just be added to the headers list. # deduplication is catastrophic here. @@ -152,7 +153,9 @@ def gen_code( {methods["load"]}( {hmap_t} *map, {keytype_c} key ) {{ + printf("Loading happening\\n"); const {valuetype_c}* internal_val = {hmap_t}_at(map, key); + printf("Loading Done\\n"); return *internal_val; }} @@ -160,7 +163,9 @@ def gen_code( {methods["store"]}( {hmap_t} *map, {keytype_c} key, {valuetype_c} value ) {{ + printf("Storing happening\\n"); {hmap_t}_insert_or_assign(map, key, value); + printf("Storing Done\\n"); }} {inline_s}void From 37fe6cf6ee407b258668518bc687f4b23174ed36 Mon Sep 17 00:00:00 2001 From: Juni Kim Date: Tue, 2 Dec 2025 19:54:47 -0500 Subject: [PATCH 40/44] try to emit standard output --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 111ff5fb..ad60eb37 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,7 +38,7 @@ jobs: poetry install --extras test - name: Run tests run: | - poetry run pytest --junit-xml=test-${{ matrix.os }}-Python-${{ matrix.python }}.xml + poetry run pytest -s --junit-xml=test-${{ matrix.os }}-Python-${{ matrix.python }}.xml - uses: codecov/codecov-action@v5 on: From 7a0878c415fb741bc7ea07588191b1a628b8339f Mon Sep 17 00:00:00 2001 From: Juni Kim Date: Tue, 2 Dec 2025 20:04:48 -0500 Subject: [PATCH 41/44] try clearing all macros --- src/finchlite/codegen/hashtable.py | 35 +++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/src/finchlite/codegen/hashtable.py b/src/finchlite/codegen/hashtable.py index cc217fc6..0fed9981 100644 --- a/src/finchlite/codegen/hashtable.py +++ b/src/finchlite/codegen/hashtable.py @@ -94,6 +94,29 @@ class CHashTable(Dict): CHashTable class that basically connects up to an STC library. """ + macros = [ + "T", + "i_key", + "i_keyclass", + "i_keypro", + "i_val", + "i_valclass", + "i_valpro", + "i_hash", + "i_eq", + "i_keydrop", + "i_keyclone", + "i_keyraw", + "i_cmpclass", + "i_keyfrom", + "i_keytoraw", + "i_valdrop", + "i_valclone", + "i_valraw", + "i_valfrom", + "i_valtoraw", + ] + libraries: dict[ tuple[AssemblyStructFType, AssemblyStructFType], CHashTableLibrary ] = {} @@ -116,6 +139,8 @@ def gen_code( # these headers should just be added to the headers list. # deduplication is catastrophic here. + for macro in cls.macros: + ctx.headers.append(f"#undef {macro}") ctx.headers.append(f"#define T {hmap_t}, {keytype_c}, {valuetype_c}") ctx.headers.append("#define i_eq c_memcmp_eq") ctx.headers.append(f'#include "{hashmap_h}"') @@ -134,7 +159,8 @@ def gen_code( # basically for the load functions, you need to provide a variable that # can be copied. # Yeah, so which API's should we use for load and store? - lib_code = dedent(f""" + lib_code = dedent( + f""" {inline_s}void* {methods["init"]}() {{ void* ptr = malloc(sizeof({hmap_t})); @@ -153,9 +179,7 @@ def gen_code( {methods["load"]}( {hmap_t} *map, {keytype_c} key ) {{ - printf("Loading happening\\n"); const {valuetype_c}* internal_val = {hmap_t}_at(map, key); - printf("Loading Done\\n"); return *internal_val; }} @@ -163,9 +187,7 @@ def gen_code( {methods["store"]}( {hmap_t} *map, {keytype_c} key, {valuetype_c} value ) {{ - printf("Storing happening\\n"); {hmap_t}_insert_or_assign(map, key, value); - printf("Storing Done\\n"); }} {inline_s}void @@ -176,7 +198,8 @@ def gen_code( {hmap_t}_drop(hptr); free(hptr); }} - """) + """ + ) ctx.add_header(lib_code) return methods, hmap_t From 88732b435ea743114bcc4b9571d8afc96968ddfe Mon Sep 17 00:00:00 2001 From: Juni Kim Date: Tue, 2 Dec 2025 20:08:44 -0500 Subject: [PATCH 42/44] we will try putting the int only struct first --- tests/test_codegen.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_codegen.py b/tests/test_codegen.py index 561b4250..757e03ec 100644 --- a/tests/test_codegen.py +++ b/tests/test_codegen.py @@ -1067,14 +1067,14 @@ def func(table: CHashTable, num: int): # what's important here is that you can call setidx_1 on table1 and # setidx_2 on table2. - assert mod.setidx_1( - table1, table1.key_type(1, 2.0), table1.value_type(2, 3, 4) - ) == table1.value_type(2, 3, 4) - assert mod.setidx_2( table2, table2.key_type(1), table2.value_type(2, 3, 4, 5) ) == table2.value_type(2, 3, 4, 5) + assert mod.setidx_1( + table1, table1.key_type(1, 2.0), table1.value_type(2, 3, 4) + ) == table1.value_type(2, 3, 4) + assert mod.setidx_3( table3, table3.key_type(1, 2.0), table3.value_type(1.434, 1.4) ) == table3.value_type(1.434, 1.4) From a508a3cfdbdc8b6de37bdf08707c2c49562986be Mon Sep 17 00:00:00 2001 From: Juni Kim Date: Tue, 2 Dec 2025 20:13:57 -0500 Subject: [PATCH 43/44] try setidx_3 --- tests/test_codegen.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_codegen.py b/tests/test_codegen.py index 757e03ec..22a54203 100644 --- a/tests/test_codegen.py +++ b/tests/test_codegen.py @@ -1071,10 +1071,10 @@ def func(table: CHashTable, num: int): table2, table2.key_type(1), table2.value_type(2, 3, 4, 5) ) == table2.value_type(2, 3, 4, 5) - assert mod.setidx_1( - table1, table1.key_type(1, 2.0), table1.value_type(2, 3, 4) - ) == table1.value_type(2, 3, 4) - assert mod.setidx_3( table3, table3.key_type(1, 2.0), table3.value_type(1.434, 1.4) ) == table3.value_type(1.434, 1.4) + + assert mod.setidx_1( + table1, table1.key_type(1, 2.0), table1.value_type(2, 3, 4) + ) == table1.value_type(2, 3, 4) From 3df995d905a490609a5522c7787d204e7bcb3e07 Mon Sep 17 00:00:00 2001 From: Juni Kim Date: Tue, 2 Dec 2025 20:43:30 -0500 Subject: [PATCH 44/44] just stick to homogeneous tuples for now --- src/finchlite/codegen/hashtable.py | 26 -------------------------- tests/test_codegen.py | 27 ++++++++------------------- 2 files changed, 8 insertions(+), 45 deletions(-) diff --git a/src/finchlite/codegen/hashtable.py b/src/finchlite/codegen/hashtable.py index 0fed9981..beda3726 100644 --- a/src/finchlite/codegen/hashtable.py +++ b/src/finchlite/codegen/hashtable.py @@ -94,29 +94,6 @@ class CHashTable(Dict): CHashTable class that basically connects up to an STC library. """ - macros = [ - "T", - "i_key", - "i_keyclass", - "i_keypro", - "i_val", - "i_valclass", - "i_valpro", - "i_hash", - "i_eq", - "i_keydrop", - "i_keyclone", - "i_keyraw", - "i_cmpclass", - "i_keyfrom", - "i_keytoraw", - "i_valdrop", - "i_valclone", - "i_valraw", - "i_valfrom", - "i_valtoraw", - ] - libraries: dict[ tuple[AssemblyStructFType, AssemblyStructFType], CHashTableLibrary ] = {} @@ -135,12 +112,9 @@ def gen_code( hmap_t = ctx.freshen("hmap") ctx.add_header("#include ") - ctx.add_header("#include ") # these headers should just be added to the headers list. # deduplication is catastrophic here. - for macro in cls.macros: - ctx.headers.append(f"#undef {macro}") ctx.headers.append(f"#define T {hmap_t}, {keytype_c}, {valuetype_c}") ctx.headers.append("#define i_eq c_memcmp_eq") ctx.headers.append(f'#include "{hashmap_h}"') diff --git a/tests/test_codegen.py b/tests/test_codegen.py index 22a54203..10ea2f84 100644 --- a/tests/test_codegen.py +++ b/tests/test_codegen.py @@ -1014,6 +1014,9 @@ def test_multiple_c_hashtable(compiler): """ This test exists because in the case of C, we might need to dump multiple hash table definitions into the context. + + So I am not gonna touch heterogeneous structs right now because the hasher + hashes the padding bytes too (even though they are worse than useless) """ def _int_tupletype(arity): @@ -1043,38 +1046,24 @@ def func(table: CHashTable, num: int): ), ) - table1 = CHashTable(asm.TupleFType.from_tuple((int, float)), _int_tupletype(3)) + table1 = CHashTable(_int_tupletype(2), _int_tupletype(3)) table2 = CHashTable(_int_tupletype(1), _int_tupletype(4)) - table3 = CHashTable( - asm.TupleFType.from_tuple((int, float)), - asm.TupleFType.from_tuple( - ( - float, - float, - ) - ), - ) mod = compiler( asm.Module( ( func(table1, 1), func(table2, 2), - func(table3, 3), ) ) ) # what's important here is that you can call setidx_1 on table1 and # setidx_2 on table2. + assert mod.setidx_1( + table1, table1.key_type(1, 2), table1.value_type(2, 3, 4) + ) == table1.value_type(2, 3, 4) + assert mod.setidx_2( table2, table2.key_type(1), table2.value_type(2, 3, 4, 5) ) == table2.value_type(2, 3, 4, 5) - - assert mod.setidx_3( - table3, table3.key_type(1, 2.0), table3.value_type(1.434, 1.4) - ) == table3.value_type(1.434, 1.4) - - assert mod.setidx_1( - table1, table1.key_type(1, 2.0), table1.value_type(2, 3, 4) - ) == table1.value_type(2, 3, 4)