mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
264 lines
5.1 KiB
C
264 lines
5.1 KiB
C
// 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 using != null "A valid allocator must be provided"
|
|
**/
|
|
fn void List.init(List* list, usz initial_capacity = 16, Allocator* using = mem::heap())
|
|
{
|
|
list.allocator = using;
|
|
list.size = 0;
|
|
if (initial_capacity > 0)
|
|
{
|
|
initial_capacity = math::next_power_of_2(initial_capacity);
|
|
list.entries = malloc_aligned(Type, initial_capacity, .alignment = Type[1].alignof, .using = using)!!;
|
|
}
|
|
else
|
|
{
|
|
list.entries = null;
|
|
}
|
|
list.capacity = initial_capacity;
|
|
}
|
|
|
|
fn void List.tinit(List* list, usz initial_capacity = 16)
|
|
{
|
|
list.init(initial_capacity, mem::temp()) @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.add_all(List* list, List* other_list)
|
|
{
|
|
if (!other_list.size) return;
|
|
list.reserve(other_list.size);
|
|
foreach (&value : other_list)
|
|
{
|
|
list.entries[list.size++] = *value;
|
|
}
|
|
}
|
|
|
|
fn Type[] List.to_array(List* list, Allocator* using = mem::heap())
|
|
{
|
|
if (!list.size) return Type[] {};
|
|
Type[] result = malloc(Type, list.size, .using = using);
|
|
result[..] = list.entries[:list.size];
|
|
return result;
|
|
}
|
|
|
|
fn void List.reverse(List* list)
|
|
{
|
|
if (list.size < 2) return;
|
|
usz half = list.size / 2U;
|
|
usz end = list.size - 1;
|
|
for (usz i = 0; i < half; i++)
|
|
{
|
|
@swap(list.entries[i], list.entries[end - i]);
|
|
}
|
|
}
|
|
|
|
fn Type[] List.array_view(List* list)
|
|
{
|
|
return list.entries[:list.size];
|
|
}
|
|
|
|
fn void List.add_array(List* list, Type[] array)
|
|
{
|
|
if (!array.len) return;
|
|
list.reserve(array.len);
|
|
foreach (&value : array)
|
|
{
|
|
list.entries[list.size++] = *value;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* @require index < list.size
|
|
**/
|
|
fn void List.set_at(List* list, usz index, Type type)
|
|
{
|
|
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;
|
|
free_aligned(list.entries, .using = list.allocator);
|
|
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::heap();
|
|
min_capacity = math::next_power_of_2(min_capacity);
|
|
list.entries = realloc_aligned(list.entries, Type.sizeof * min_capacity, .alignment = Type[1].alignof, .using = list.allocator) ?? 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];
|
|
}
|
|
|
|
$if (types::is_equatable_type(Type))
|
|
|
|
fn usz! List.index_of(List* list, Type type)
|
|
{
|
|
foreach (i, v : list)
|
|
{
|
|
if (v == type) return i;
|
|
}
|
|
return SearchResult.MISSING?;
|
|
}
|
|
|
|
fn bool List.equals(List* list, List other_list)
|
|
{
|
|
if (list.size != other_list.size) return false;
|
|
foreach (i, v : list)
|
|
{
|
|
if (v != other_list.entries[i]) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
fn bool List.contains(List* list, Type type)
|
|
{
|
|
foreach (i, v : list)
|
|
{
|
|
if (v == type) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
$endif
|
|
|
|
fn void List.ensure_capacity(List* list, usz added = 1) @inline @private
|
|
{
|
|
usz new_size = list.size + added;
|
|
if (list.capacity > new_size) return;
|
|
|
|
assert(new_size < usz.max / 2U);
|
|
usz new_capacity = list.capacity ? 2U * list.capacity : 16U;
|
|
while (new_size >= new_capacity) new_capacity *= 2U;
|
|
list.reserve(new_capacity);
|
|
}
|