mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
Move collection types. Improve linked list interface. Update map.destroy => map.free
This commit is contained in:
83
lib/std/collections/enumset.c3
Normal file
83
lib/std/collections/enumset.c3
Normal file
@@ -0,0 +1,83 @@
|
||||
// TODO: ensure the type is an enum first.
|
||||
module std::collections::enumset<Enum>;
|
||||
|
||||
$assert(Enum.elements < 64, "Maximum number of elements for an enum used as enum set is 63");
|
||||
|
||||
$switch ($$C_INT_SIZE):
|
||||
$case 64:
|
||||
private define EnumSetType = ulong;
|
||||
$case 32:
|
||||
$if (Enum.elements < 32):
|
||||
private define EnumSetType = uint;
|
||||
$else:
|
||||
private define EnumSetType = ulong;
|
||||
$endif;
|
||||
$default:
|
||||
$if (Enum.elements < 16):
|
||||
private define EnumSetType = ushort;
|
||||
$elif (Enum.elements < 31):
|
||||
private define EnumSetType = uint;
|
||||
$else:
|
||||
private define EnumSetType = ulong;
|
||||
$endif;
|
||||
$endswitch;
|
||||
|
||||
define EnumSet = distinct EnumSetType;
|
||||
|
||||
fn void EnumSet.add(EnumSet* this, Enum v)
|
||||
{
|
||||
*this = (EnumSet)((EnumSetType)*this | 1u << (EnumSetType)v);
|
||||
}
|
||||
|
||||
fn void EnumSet.clear(EnumSet* this)
|
||||
{
|
||||
*this = 0;
|
||||
}
|
||||
|
||||
fn bool EnumSet.remove(EnumSet* this, Enum v)
|
||||
{
|
||||
EnumSetType old = (EnumSetType)*this;
|
||||
EnumSetType new = old & ~(1u << (EnumSetType)v);
|
||||
*this = (EnumSet)new;
|
||||
return old != new;
|
||||
}
|
||||
|
||||
fn bool EnumSet.has(EnumSet* this, Enum v)
|
||||
{
|
||||
return ((EnumSetType)*this & (1u << (EnumSetType)v)) != 0;
|
||||
}
|
||||
|
||||
fn void EnumSet.add_all(EnumSet* this, EnumSet s)
|
||||
{
|
||||
*this = (EnumSet)((EnumSetType)*this | (EnumSetType)s);
|
||||
}
|
||||
|
||||
fn void EnumSet.retain_all(EnumSet* this, EnumSet s)
|
||||
{
|
||||
*this = (EnumSet)((EnumSetType)*this & (EnumSetType)s);
|
||||
}
|
||||
|
||||
fn void EnumSet.remove_all(EnumSet* this, EnumSet s)
|
||||
{
|
||||
*this = (EnumSet)((EnumSetType)*this & ~(EnumSetType)s);
|
||||
}
|
||||
|
||||
fn EnumSet EnumSet.and_of(EnumSet* this, EnumSet s)
|
||||
{
|
||||
return (EnumSet)((EnumSetType)*this & (EnumSetType)s);
|
||||
}
|
||||
|
||||
fn EnumSet EnumSet.or_of(EnumSet* this, EnumSet s)
|
||||
{
|
||||
return (EnumSet)((EnumSetType)*this | (EnumSetType)s);
|
||||
}
|
||||
|
||||
fn EnumSet EnumSet.diff_of(EnumSet* this, EnumSet s)
|
||||
{
|
||||
return (EnumSet)((EnumSetType)*this & ~(EnumSetType)s);
|
||||
}
|
||||
|
||||
fn EnumSet EnumSet.xor_of(EnumSet* this, EnumSet s)
|
||||
{
|
||||
return (EnumSet)((EnumSetType)*this ^ (EnumSetType)s);
|
||||
}
|
||||
312
lib/std/collections/linkedlist.c3
Normal file
312
lib/std/collections/linkedlist.c3
Normal file
@@ -0,0 +1,312 @@
|
||||
// Copyright (c) 2021 Christoffer Lerno. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license
|
||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||
module std::collections::linkedlist<Type>;
|
||||
|
||||
private struct Node
|
||||
{
|
||||
Node *next;
|
||||
Node *prev;
|
||||
Type value;
|
||||
}
|
||||
|
||||
struct LinkedList
|
||||
{
|
||||
Allocator *allocator;
|
||||
usz size;
|
||||
Node *_first;
|
||||
Node *_last;
|
||||
}
|
||||
|
||||
fn void LinkedList.push(LinkedList* list, Type value)
|
||||
{
|
||||
list.link_first(value);
|
||||
}
|
||||
|
||||
fn void LinkedList.push_last(LinkedList* list, Type value)
|
||||
{
|
||||
list.link_last(value);
|
||||
}
|
||||
|
||||
fn void LinkedList.init(LinkedList* list, Allocator* alloc = mem::current_allocator())
|
||||
{
|
||||
*list = { .allocator = alloc };
|
||||
}
|
||||
|
||||
fn void LinkedList.tinit(LinkedList* list) => list.init(mem::temp_allocator()) @inline;
|
||||
|
||||
/**
|
||||
* @require list.allocator
|
||||
**/
|
||||
private macro void LinkedList.@free_node(LinkedList &list, Node* node)
|
||||
{
|
||||
list.allocator.free(node)!!;
|
||||
}
|
||||
private macro Node* LinkedList.@alloc_node(LinkedList &list)
|
||||
{
|
||||
if (!list.allocator) list.allocator = mem::current_allocator();
|
||||
return list.allocator.alloc(Node.sizeof)!!;
|
||||
}
|
||||
|
||||
private fn void LinkedList.link_first(LinkedList* list, Type value)
|
||||
{
|
||||
Node *first = list._first;
|
||||
Node *new_node = list.@alloc_node();
|
||||
*new_node = { .next = first, .value = value };
|
||||
list._first = new_node;
|
||||
if (!first)
|
||||
{
|
||||
list._last = new_node;
|
||||
}
|
||||
else
|
||||
{
|
||||
first.prev = new_node;
|
||||
}
|
||||
list.size++;
|
||||
}
|
||||
|
||||
private fn void LinkedList.link_last(LinkedList* list, Type value)
|
||||
{
|
||||
Node *last = list._last;
|
||||
Node *new_node = list.@alloc_node();
|
||||
*new_node = { .prev = last, .value = value };
|
||||
list._last = new_node;
|
||||
if (!last)
|
||||
{
|
||||
list._first = new_node;
|
||||
}
|
||||
else
|
||||
{
|
||||
last.next = new_node;
|
||||
}
|
||||
list.size++;
|
||||
}
|
||||
|
||||
fn Type! peek(LinkedList* list) => list.first() @inline;
|
||||
fn Type! peek_last(LinkedList* list) => list.last() @inline;
|
||||
|
||||
fn Type! LinkedList.first(LinkedList *list)
|
||||
{
|
||||
if (!list._first) return IteratorResult.NO_MORE_ELEMENT!;
|
||||
return list._first.value;
|
||||
}
|
||||
|
||||
fn Type! LinkedList.last(LinkedList* list)
|
||||
{
|
||||
if (!list._last) return IteratorResult.NO_MORE_ELEMENT!;
|
||||
return list._last.value;
|
||||
}
|
||||
|
||||
fn void LinkedList.free(LinkedList* list) => list.clear() @inline;
|
||||
|
||||
fn void LinkedList.clear(LinkedList* list)
|
||||
{
|
||||
for (Node* node = list._first; node != null;)
|
||||
{
|
||||
Node* next = node.next;
|
||||
list.@free_node(node);
|
||||
node = next;
|
||||
}
|
||||
list._first = null;
|
||||
list._last = null;
|
||||
list.size = 0;
|
||||
}
|
||||
|
||||
fn usz LinkedList.len(LinkedList* list) @inline => list.size;
|
||||
|
||||
/**
|
||||
* @require index < list.size
|
||||
**/
|
||||
macro Node* LinkedList.node_at_index(LinkedList* list, usz index)
|
||||
{
|
||||
if (index * 2 >= list.size)
|
||||
{
|
||||
Node* node = list._last;
|
||||
index = list.size - index - 1;
|
||||
while (index--) node = node.prev;
|
||||
return node;
|
||||
}
|
||||
Node* node = list._first;
|
||||
while (index--) node = node.next;
|
||||
return node;
|
||||
}
|
||||
/**
|
||||
* @require index < list.size
|
||||
**/
|
||||
fn Type LinkedList.get(LinkedList* list, usz index)
|
||||
{
|
||||
return list.node_at_index(index).value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require index < list.size
|
||||
**/
|
||||
fn void LinkedList.set(LinkedList* list, usz index, Type element)
|
||||
{
|
||||
list.node_at_index(index).value = element;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require index < list.size
|
||||
**/
|
||||
fn void LinkedList.remove(LinkedList* list, usz index)
|
||||
{
|
||||
list.unlink(list.node_at_index(index));
|
||||
}
|
||||
|
||||
/**
|
||||
* @require index <= list.size
|
||||
**/
|
||||
fn void LinkedList.insert(LinkedList* list, usz index, Type element)
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
list.push(element);
|
||||
case list.size:
|
||||
list.push_last(element);
|
||||
default:
|
||||
list.link_before(list.node_at_index(index), element);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @require succ != null
|
||||
**/
|
||||
private fn void LinkedList.link_before(LinkedList *list, Node *succ, Type value)
|
||||
{
|
||||
Node* pred = succ.prev;
|
||||
Node* new_node = mem::alloc(Node);
|
||||
*new_node = { .prev = pred, .next = succ, .value = value };
|
||||
succ.prev = new_node;
|
||||
if (!pred)
|
||||
{
|
||||
list._first = new_node;
|
||||
}
|
||||
else
|
||||
{
|
||||
pred.next = new_node;
|
||||
}
|
||||
list.size++;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require list && list._first
|
||||
**/
|
||||
private fn void LinkedList.unlink_first(LinkedList* list)
|
||||
{
|
||||
Node* f = list._first;
|
||||
Node* next = f.next;
|
||||
list.@free_node(f);
|
||||
list._first = next;
|
||||
if (!next)
|
||||
{
|
||||
list._last = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
next.prev = null;
|
||||
}
|
||||
list.size--;
|
||||
}
|
||||
|
||||
fn bool LinkedList.remove_value(LinkedList* list, Type t)
|
||||
{
|
||||
for (Node* node = list._first; node != null; node = node.next)
|
||||
{
|
||||
if (node.value == t)
|
||||
{
|
||||
list.unlink(node);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
fn bool LinkedList.remove_last_value(LinkedList* list, Type t)
|
||||
{
|
||||
for (Node* node = list._last; node != null; node = node.prev)
|
||||
{
|
||||
if (node.value == t)
|
||||
{
|
||||
list.unlink(node);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&inout] list
|
||||
**/
|
||||
fn Type! LinkedList.pop(LinkedList* list)
|
||||
{
|
||||
if (!list._first) return IteratorResult.NO_MORE_ELEMENT!;
|
||||
defer list.unlink_first();
|
||||
return list._first.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&inout] list
|
||||
**/
|
||||
fn void! LinkedList.remove_last(LinkedList* list)
|
||||
{
|
||||
if (!list._first) return IteratorResult.NO_MORE_ELEMENT!;
|
||||
list.unlink_last();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&inout] list
|
||||
**/
|
||||
fn void! LinkedList.remove_first(LinkedList* list)
|
||||
{
|
||||
if (!list._first) return IteratorResult.NO_MORE_ELEMENT!;
|
||||
list.unlink_first();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&inout] list
|
||||
* @require list._last
|
||||
**/
|
||||
private fn void LinkedList.unlink_last(LinkedList *list) @inline
|
||||
{
|
||||
Node* l = list._last;
|
||||
Node* prev = l.prev;
|
||||
list._last = prev;
|
||||
list.@free_node(l);
|
||||
if (!prev)
|
||||
{
|
||||
list._first = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
prev.next = null;
|
||||
}
|
||||
list.size--;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require list != null, x != null
|
||||
**/
|
||||
private fn void LinkedList.unlink(LinkedList* list, Node* x)
|
||||
{
|
||||
Node* next = x.next;
|
||||
Node* prev = x.prev;
|
||||
if (!prev)
|
||||
{
|
||||
list._first = next;
|
||||
}
|
||||
else
|
||||
{
|
||||
prev.next = next;
|
||||
}
|
||||
if (!next)
|
||||
{
|
||||
list._last = prev;
|
||||
}
|
||||
else
|
||||
{
|
||||
next.prev = prev;
|
||||
}
|
||||
list.@free_node(x);
|
||||
list.size--;
|
||||
}
|
||||
177
lib/std/collections/list.c3
Normal file
177
lib/std/collections/list.c3
Normal file
@@ -0,0 +1,177 @@
|
||||
// Copyright (c) 2021 Christoffer Lerno. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license
|
||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||
module std::collections::list<Type>;
|
||||
import std::math;
|
||||
|
||||
struct List
|
||||
{
|
||||
usz size;
|
||||
usz capacity;
|
||||
Allocator *allocator;
|
||||
Type *entries;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require allocator != null "A valid allocator must be provided"
|
||||
**/
|
||||
fn void List.init(List* list, usz initial_capacity = 16, Allocator* allocator = mem::current_allocator())
|
||||
{
|
||||
list.allocator = allocator;
|
||||
list.size = 0;
|
||||
if (initial_capacity > 0)
|
||||
{
|
||||
initial_capacity = math::next_power_of_2(initial_capacity);
|
||||
list.entries = allocator.alloc_aligned(Type.sizeof * initial_capacity, Type[1].alignof)!!;
|
||||
}
|
||||
else
|
||||
{
|
||||
list.entries = null;
|
||||
}
|
||||
list.capacity = initial_capacity;
|
||||
}
|
||||
|
||||
fn void List.tinit(List* list, usz initial_capacity = 16)
|
||||
{
|
||||
list.init(initial_capacity, mem::temp_allocator()) @inline;
|
||||
}
|
||||
|
||||
fn void List.push(List* list, Type element) @inline
|
||||
{
|
||||
list.append(element);
|
||||
}
|
||||
|
||||
fn void List.append(List* list, Type element)
|
||||
{
|
||||
list.ensure_capacity();
|
||||
list.entries[list.size++] = element;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require list.size > 0
|
||||
*/
|
||||
fn Type List.pop(List* list)
|
||||
{
|
||||
return list.entries[--list.size];
|
||||
}
|
||||
|
||||
fn void List.clear(List* list)
|
||||
{
|
||||
list.size = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require list.size > 0
|
||||
*/
|
||||
fn Type List.pop_first(List* list)
|
||||
{
|
||||
Type value = list.entries[0];
|
||||
list.remove_at(0);
|
||||
return value;
|
||||
}
|
||||
|
||||
fn void List.remove_at(List* list, usz index)
|
||||
{
|
||||
for (usz i = index + 1; i < list.size; i++)
|
||||
{
|
||||
list.entries[i - 1] = list.entries[i];
|
||||
}
|
||||
list.size--;
|
||||
}
|
||||
|
||||
fn void List.push_front(List* list, Type type) @inline
|
||||
{
|
||||
list.insert_at(0, type);
|
||||
}
|
||||
|
||||
fn void List.insert_at(List* list, usz index, Type type)
|
||||
{
|
||||
list.ensure_capacity();
|
||||
for (usz i = list.size; i > index; i--)
|
||||
{
|
||||
list.entries[i] = list.entries[i - 1];
|
||||
}
|
||||
list.size++;
|
||||
list.entries[index] = type;
|
||||
}
|
||||
|
||||
fn void List.remove_last(List* list)
|
||||
{
|
||||
list.size--;
|
||||
}
|
||||
|
||||
fn void List.remove_first(List* list)
|
||||
{
|
||||
list.remove_at(0);
|
||||
}
|
||||
|
||||
fn Type* List.first(List* list)
|
||||
{
|
||||
return list.size ? &list.entries[0] : null;
|
||||
}
|
||||
|
||||
fn Type* List.last(List* list)
|
||||
{
|
||||
return list.size ? &list.entries[list.size - 1] : null;
|
||||
}
|
||||
|
||||
fn bool List.is_empty(List* list)
|
||||
{
|
||||
return !list.size;
|
||||
}
|
||||
|
||||
fn usz List.len(List* list) @operator(len)
|
||||
{
|
||||
return list.size;
|
||||
}
|
||||
|
||||
fn Type List.get(List* list, usz index)
|
||||
{
|
||||
return list.entries[index];
|
||||
}
|
||||
|
||||
fn void List.free(List* list)
|
||||
{
|
||||
if (!list.allocator) return;
|
||||
list.allocator.free_aligned(list.entries)!!;
|
||||
list.capacity = 0;
|
||||
list.size = 0;
|
||||
list.entries = null;
|
||||
}
|
||||
|
||||
fn void List.swap(List* list, usz i, usz j)
|
||||
{
|
||||
@swap(list.entries[i], list.entries[j]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reserve at least min_capacity
|
||||
**/
|
||||
fn void List.reserve(List* list, usz min_capacity)
|
||||
{
|
||||
if (!min_capacity) return;
|
||||
if (list.capacity >= min_capacity) return;
|
||||
if (!list.allocator) list.allocator = mem::temp_allocator();
|
||||
min_capacity = math::next_power_of_2(min_capacity);
|
||||
list.entries = list.allocator.realloc_aligned(list.entries, Type.sizeof * min_capacity, Type[1].alignof) ?? null;
|
||||
list.capacity = min_capacity;
|
||||
}
|
||||
|
||||
macro Type List.@item_at(List &list, usz index) @operator([])
|
||||
{
|
||||
return list.entries[index];
|
||||
}
|
||||
|
||||
fn Type* List.get_ref(List* list, usz index) @operator(&[]) @inline
|
||||
{
|
||||
return &list.entries[index];
|
||||
}
|
||||
|
||||
|
||||
private fn void List.ensure_capacity(List* list) @inline
|
||||
{
|
||||
if (list.capacity == list.size)
|
||||
{
|
||||
list.reserve(list.capacity ? 2 * list.capacity : 16);
|
||||
}
|
||||
}
|
||||
342
lib/std/collections/map.c3
Normal file
342
lib/std/collections/map.c3
Normal file
@@ -0,0 +1,342 @@
|
||||
module std::collections::map<Key, Value>;
|
||||
import std::math;
|
||||
|
||||
const uint DEFAULT_INITIAL_CAPACITY = 16;
|
||||
const uint MAXIMUM_CAPACITY = 1u << 31;
|
||||
const float DEFAULT_LOAD_FACTOR = 0.75;
|
||||
|
||||
private struct Entry
|
||||
{
|
||||
uint hash;
|
||||
Key key;
|
||||
Value value;
|
||||
Entry* next;
|
||||
}
|
||||
|
||||
struct HashMap
|
||||
{
|
||||
Entry*[] table;
|
||||
Allocator* allocator;
|
||||
uint count; // Number of elements
|
||||
uint threshold; // Resize limit
|
||||
float load_factor;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require capacity > 0 "The capacity must be 1 or higher"
|
||||
* @require load_factor > 0.0 "The load factor must be higher than 0"
|
||||
* @require !map.allocator "Map was already initialized"
|
||||
* @require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
|
||||
* @require allocator != null "The allocator must be non-null"
|
||||
**/
|
||||
fn void HashMap.init(HashMap* map, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator* allocator = mem::current_allocator())
|
||||
{
|
||||
capacity = math::next_power_of_2(capacity);
|
||||
map.allocator = allocator;
|
||||
map.load_factor = load_factor;
|
||||
map.threshold = (uint)(capacity * load_factor);
|
||||
map.table = array::make(Entry*, capacity, allocator);
|
||||
}
|
||||
|
||||
/**
|
||||
* @require capacity > 0 "The capacity must be 1 or higher"
|
||||
* @require load_factor > 0.0 "The load factor must be higher than 0"
|
||||
* @require !map.allocator "Map was already initialized"
|
||||
* @require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
|
||||
**/
|
||||
fn void HashMap.tinit(HashMap* map, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
|
||||
{
|
||||
map.init(capacity, load_factor, mem::temp_allocator());
|
||||
}
|
||||
|
||||
fn void HashMap.init_from_map(HashMap* map, HashMap* other_map, Allocator* allocator = mem::current_allocator())
|
||||
{
|
||||
map.init(other_map.table.len, other_map.load_factor, allocator);
|
||||
map.put_all_for_create(other_map);
|
||||
}
|
||||
|
||||
fn void HashMap.tinit_from_map(HashMap* map, HashMap* other_map)
|
||||
{
|
||||
map.init_from_map(other_map, mem::temp_allocator()) @inline;
|
||||
}
|
||||
|
||||
fn bool HashMap.is_empty(HashMap* map) @inline
|
||||
{
|
||||
return !map.count;
|
||||
}
|
||||
|
||||
fn Value*! HashMap.get_ref(HashMap* map, Key key)
|
||||
{
|
||||
if (!map.count) return SearchResult.MISSING!;
|
||||
uint hash = rehash(key.hash());
|
||||
for (Entry *e = map.table[index_for(hash, map.table.len)]; e != null; e = e.next)
|
||||
{
|
||||
if (e.hash == hash && equals(key, e.key)) return &e.value;
|
||||
}
|
||||
return SearchResult.MISSING!;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value or update and
|
||||
**/
|
||||
macro Value HashMap.@get_or_set(HashMap* map, Key key, Value #expr)
|
||||
{
|
||||
if (!map.count)
|
||||
{
|
||||
Value val = #expr;
|
||||
map.set(key, val);
|
||||
return val;
|
||||
}
|
||||
uint hash = rehash(key.hash());
|
||||
uint index = index_for(hash, map.table.len);
|
||||
for (Entry *e = map.table[index]; e != null; e = e.next)
|
||||
{
|
||||
if (e.hash == hash && equals(key, e.key)) return e.value;
|
||||
}
|
||||
Value val = #expr;
|
||||
map.add_entry(hash, key, val, index);
|
||||
return val;
|
||||
}
|
||||
|
||||
fn Value! HashMap.get(HashMap* map, Key key) @operator([])
|
||||
{
|
||||
return *map.get_ref(key) @inline;
|
||||
}
|
||||
|
||||
fn bool HashMap.has_key(HashMap* map, Key key)
|
||||
{
|
||||
return try(map.get_ref(key));
|
||||
}
|
||||
|
||||
fn bool HashMap.set(HashMap* map, Key key, Value value) @operator([]=)
|
||||
{
|
||||
// If the map isn't initialized, use the defaults to initialize it.
|
||||
if (!map.allocator)
|
||||
{
|
||||
map.init();
|
||||
}
|
||||
uint hash = rehash(key.hash());
|
||||
uint index = index_for(hash, map.table.len);
|
||||
for (Entry *e = map.table[index]; e != null; e = e.next)
|
||||
{
|
||||
if (e.hash == hash && equals(key, e.key))
|
||||
{
|
||||
e.value = value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
map.add_entry(hash, key, value, index);
|
||||
return false;
|
||||
}
|
||||
|
||||
fn void! HashMap.remove(HashMap* map, Key key) @maydiscard
|
||||
{
|
||||
if (!map.remove_entry_for_key(key)) return SearchResult.MISSING!;
|
||||
}
|
||||
|
||||
fn void HashMap.clear(HashMap* map)
|
||||
{
|
||||
if (!map.count) return;
|
||||
foreach (Entry** &entry_ref : map.table)
|
||||
{
|
||||
Entry* entry = *entry_ref;
|
||||
if (!entry) continue;
|
||||
map.free_internal(entry);
|
||||
*entry_ref = null;
|
||||
}
|
||||
map.count = 0;
|
||||
}
|
||||
|
||||
fn void HashMap.free(HashMap* map)
|
||||
{
|
||||
if (!map.allocator) return;
|
||||
map.clear();
|
||||
map.free_internal(map.table.ptr);
|
||||
map.table = Entry*[] {};
|
||||
}
|
||||
|
||||
fn Key[] HashMap.key_tlist(HashMap* map)
|
||||
{
|
||||
return map.key_list(mem::temp_allocator()) @inline;
|
||||
}
|
||||
|
||||
fn Key[] HashMap.key_list(HashMap* map, Allocator* allocator = mem::current_allocator())
|
||||
{
|
||||
if (!map.count) return Key[] {};
|
||||
|
||||
Key[] list = array::make(Key, map.count, allocator);
|
||||
usz index = 0;
|
||||
foreach (Entry* entry : map.table)
|
||||
{
|
||||
while (entry)
|
||||
{
|
||||
list[index++] = entry.key;
|
||||
entry = entry.next;
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
fn Value[] HashMap.value_tlist(HashMap* map)
|
||||
{
|
||||
return map.value_list(mem::temp_allocator()) @inline;
|
||||
}
|
||||
|
||||
fn Value[] HashMap.value_list(HashMap* map, Allocator* allocator = mem::current_allocator())
|
||||
{
|
||||
if (!map.count) return Value[] {};
|
||||
Value[] list = array::make(Value, map.count, allocator);
|
||||
usz index = 0;
|
||||
foreach (Entry* entry : map.table)
|
||||
{
|
||||
while (entry)
|
||||
{
|
||||
list[index++] = entry.value;
|
||||
entry = entry.next;
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
$if (types::is_equatable(Value)):
|
||||
fn bool HashMap.has_value(HashMap* map, Value v)
|
||||
{
|
||||
if (!map.count) return false;
|
||||
foreach (Entry* entry : map.table)
|
||||
{
|
||||
while (entry)
|
||||
{
|
||||
if (equals(v, entry.value)) return true;
|
||||
entry = entry.next;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
$endif;
|
||||
|
||||
// --- private methods
|
||||
|
||||
private fn void HashMap.add_entry(HashMap* map, uint hash, Key key, Value value, uint bucket_index)
|
||||
{
|
||||
Entry* entry = map.allocator.alloc(Entry.sizeof)!!;
|
||||
*entry = { .hash = hash, .key = key, .value = value, .next = map.table[bucket_index] };
|
||||
map.table[bucket_index] = entry;
|
||||
if (map.count++ >= map.threshold)
|
||||
{
|
||||
map.resize(map.table.len * 2);
|
||||
}
|
||||
}
|
||||
|
||||
private fn void HashMap.resize(HashMap* map, uint new_capacity)
|
||||
{
|
||||
Entry*[] old_table = map.table;
|
||||
uint old_capacity = old_table.len;
|
||||
if (old_capacity == MAXIMUM_CAPACITY)
|
||||
{
|
||||
map.threshold = uint.max;
|
||||
return;
|
||||
}
|
||||
Entry*[] new_table = array::make(Entry*, new_capacity, map.allocator);
|
||||
map.transfer(new_table);
|
||||
map.table = new_table;
|
||||
map.free_internal(old_table.ptr);
|
||||
map.threshold = (uint)(new_capacity * map.load_factor);
|
||||
}
|
||||
|
||||
private fn uint rehash(uint hash) @inline
|
||||
{
|
||||
hash ^= (hash >> 20) ^ (hash >> 12);
|
||||
return hash ^ ((hash >> 7) ^ (hash >> 4));
|
||||
}
|
||||
|
||||
private macro uint index_for(uint hash, uint capacity)
|
||||
{
|
||||
return hash & (capacity - 1);
|
||||
}
|
||||
|
||||
private fn void HashMap.transfer(HashMap* map, Entry*[] new_table)
|
||||
{
|
||||
Entry*[] src = map.table;
|
||||
uint new_capacity = new_table.len;
|
||||
foreach (uint j, Entry *e : src)
|
||||
{
|
||||
if (!e) continue;
|
||||
do
|
||||
{
|
||||
Entry* next = e.next;
|
||||
uint i = index_for(e.hash, new_capacity);
|
||||
e.next = new_table[i];
|
||||
new_table[i] = e;
|
||||
e = next;
|
||||
}
|
||||
while (e);
|
||||
}
|
||||
}
|
||||
|
||||
private fn void HashMap.put_all_for_create(HashMap* map, HashMap* other_map)
|
||||
{
|
||||
if (!other_map.count) return;
|
||||
foreach (Entry *e : other_map.table)
|
||||
{
|
||||
if (!e) continue;
|
||||
map.put_for_create(e.key, e.value);
|
||||
}
|
||||
}
|
||||
|
||||
private fn void HashMap.put_for_create(HashMap* map, Key key, Value value)
|
||||
{
|
||||
uint hash = rehash(key.hash());
|
||||
uint i = index_for(hash, map.table.len);
|
||||
for (Entry *e = map.table[i]; e != null; e = e.next)
|
||||
{
|
||||
if (e.hash == hash && equals(key, e.key))
|
||||
{
|
||||
e.value = value;
|
||||
return;
|
||||
}
|
||||
}
|
||||
map.create_entry(hash, key, value, i);
|
||||
}
|
||||
|
||||
private fn void HashMap.free_internal(HashMap* map, void* ptr) @inline
|
||||
{
|
||||
map.allocator.free(ptr)!!;
|
||||
}
|
||||
|
||||
private fn bool HashMap.remove_entry_for_key(HashMap* map, Key key)
|
||||
{
|
||||
uint hash = rehash(key.hash());
|
||||
uint i = index_for(hash, map.table.len);
|
||||
Entry* prev = map.table[i];
|
||||
Entry* e = prev;
|
||||
while (e)
|
||||
{
|
||||
Entry *next = e.next;
|
||||
if (e.hash == hash && equals(key, e.key))
|
||||
{
|
||||
map.count--;
|
||||
if (prev == e)
|
||||
{
|
||||
map.table[i] = next;
|
||||
}
|
||||
else
|
||||
{
|
||||
prev.next = next;
|
||||
}
|
||||
map.free_internal(e);
|
||||
return true;
|
||||
}
|
||||
prev = e;
|
||||
e = next;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private fn void HashMap.create_entry(HashMap* map, uint hash, Key key, Value value, int bucket_index)
|
||||
{
|
||||
Entry *e = map.table[bucket_index];
|
||||
Entry* entry = map.allocator.alloc(Entry.sizeof)!!;
|
||||
*entry = { .hash = hash, .key = key, .value = value, .next = map.table[bucket_index] };
|
||||
map.table[bucket_index] = entry;
|
||||
map.count++;
|
||||
}
|
||||
113
lib/std/collections/priorityqueue.c3
Normal file
113
lib/std/collections/priorityqueue.c3
Normal file
@@ -0,0 +1,113 @@
|
||||
// priorityqueue.c3
|
||||
// A priority queue using a classic binary heap for C3.
|
||||
//
|
||||
// Copyright (c) 2022 David Kopec
|
||||
//
|
||||
// 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.
|
||||
module std::collections::priorityqueue<Type>;
|
||||
import std::collections::list;
|
||||
|
||||
define Heap = List<Type>;
|
||||
|
||||
struct PriorityQueue
|
||||
{
|
||||
Heap heap;
|
||||
bool max; // true if max-heap, false if min-heap
|
||||
}
|
||||
|
||||
fn void PriorityQueue.push(PriorityQueue* pq, Type element)
|
||||
{
|
||||
pq.heap.push(element);
|
||||
usz i = pq.heap.len() - 1;
|
||||
while (i > 0)
|
||||
{
|
||||
usz parent = (i - 1) / 2;
|
||||
if ((pq.max && greater(pq.heap.get(i), pq.heap.get(parent))) || (!pq.max && less(pq.heap.get(i), pq.heap.get(parent))))
|
||||
{
|
||||
pq.heap.swap(i, parent);
|
||||
i = parent;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @require pq != null
|
||||
*/
|
||||
fn Type! PriorityQueue.pop(PriorityQueue* pq)
|
||||
{
|
||||
usz i = 0;
|
||||
usz len = pq.heap.len() @inline;
|
||||
if (!len) return IteratorResult.NO_MORE_ELEMENT!;
|
||||
usz newCount = len - 1;
|
||||
pq.heap.swap(0, newCount);
|
||||
while ((2 * i + 1) < newCount)
|
||||
{
|
||||
usz j = 2 * i + 1;
|
||||
if (((j + 1) < newCount) &&
|
||||
((pq.max && greater(pq.heap.get(j + 1), pq.heap[j]))
|
||||
|| (!pq.max && less(pq.heap.get(j + 1), pq.heap.get(j)))))
|
||||
{
|
||||
j++;
|
||||
}
|
||||
if ((pq.max && less(pq.heap.get(i), pq.heap.get(j))) || (!pq.max && greater(pq.heap.get(i), pq.heap.get(j))))
|
||||
{
|
||||
pq.heap.swap(i, j);
|
||||
i = j;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return pq.heap.pop();
|
||||
}
|
||||
|
||||
/**
|
||||
* @require pq != null
|
||||
*/
|
||||
fn Type! PriorityQueue.peek(PriorityQueue* pq)
|
||||
{
|
||||
if (!pq.len()) return IteratorResult.NO_MORE_ELEMENT!;
|
||||
return pq.heap.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @require pq != null
|
||||
*/
|
||||
fn void PriorityQueue.free(PriorityQueue* pq)
|
||||
{
|
||||
pq.heap.free();
|
||||
}
|
||||
|
||||
/**
|
||||
* @require pq != null
|
||||
*/
|
||||
fn usz PriorityQueue.len(PriorityQueue* pq) @operator(len)
|
||||
{
|
||||
return pq.heap.len();
|
||||
}
|
||||
|
||||
/**
|
||||
* @require pq != null, index < pq.len()
|
||||
*/
|
||||
fn Type PriorityQueue.peek_at(PriorityQueue* pq, usz index) @operator([])
|
||||
{
|
||||
return pq.heap[index];
|
||||
}
|
||||
Reference in New Issue
Block a user