mirror of
https://github.com/Kapendev/parin.git
synced 2025-04-25 20:49:57 +03:00
419 lines
9.2 KiB
D
419 lines
9.2 KiB
D
// Copyright 2024 Alexandros F. G. Kapretsos
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
module popka.core.container;
|
|
|
|
/// The container module serves as a hub for various data structures,
|
|
/// offering a cohesive set of tools for efficient storage and retrieval of information within a program.
|
|
|
|
import lib = core.stdc.stdlib;
|
|
import popka.core.fmt;
|
|
|
|
struct List(T) {
|
|
T[] items;
|
|
size_t capacity;
|
|
|
|
this(size_t length) {
|
|
foreach (i; 0 .. length) {
|
|
append(T.init);
|
|
}
|
|
}
|
|
|
|
this(TT)(const(TT)[] args) {
|
|
foreach (arg; args) {
|
|
append(arg);
|
|
}
|
|
}
|
|
|
|
this(TT)(List!TT list) {
|
|
foreach (item; list.items) {
|
|
append(item);
|
|
}
|
|
}
|
|
|
|
this(TT)(FlagList!TT list) {
|
|
foreach (item; list.items) {
|
|
append(item);
|
|
}
|
|
}
|
|
|
|
T[] opSlice(size_t dim)(size_t i, size_t j) {
|
|
return items[i .. j];
|
|
}
|
|
|
|
T[] opIndex() {
|
|
return items[];
|
|
}
|
|
|
|
T[] opIndex(T[] slice) {
|
|
// D calls this when the slice op is used and it works like this: opIndex(opSlice(i, j))
|
|
return slice;
|
|
}
|
|
|
|
ref T opIndex(size_t i) {
|
|
// Returning a ref will let people take a pointer to that value.
|
|
return items[i];
|
|
}
|
|
|
|
void opIndexAssign(TT)(TT rhs, size_t i) {
|
|
items[i] = rhs;
|
|
}
|
|
|
|
void opIndexOpAssign(string op, TT)(TT rhs, size_t i) {
|
|
mixin("items[i] " ~ op ~ "= rhs;");
|
|
}
|
|
|
|
size_t opDollar(size_t dim)() {
|
|
return items.length;
|
|
}
|
|
|
|
size_t length() {
|
|
return items.length;
|
|
}
|
|
|
|
void append(TT)(const(TT)[] args...) {
|
|
foreach (arg; args) {
|
|
size_t newLength = length + 1;
|
|
if (newLength > capacity) {
|
|
capacity = findListCapacity(newLength);
|
|
items = (cast(T*) lib.realloc(items.ptr, capacity * T.sizeof))[0 .. newLength];
|
|
} else {
|
|
items = items.ptr[0 .. newLength];
|
|
}
|
|
// We cast here because of the type system.
|
|
items[$ - 1] = cast(TT) arg;
|
|
}
|
|
}
|
|
|
|
void remove(size_t i) {
|
|
items[i] = items[$ - 1];
|
|
items = items[0 .. $ - 1];
|
|
}
|
|
|
|
T pop() {
|
|
T temp = items[$ - 1];
|
|
remove(length - 1);
|
|
return temp;
|
|
}
|
|
|
|
void resize(size_t length) {
|
|
if (length < this.length) {
|
|
items = items[0 .. length];
|
|
} else {
|
|
foreach (i; 0 .. length - this.length) {
|
|
this.append(T.init);
|
|
}
|
|
}
|
|
}
|
|
|
|
void fill(TT)(TT value) {
|
|
foreach (ref item; items) {
|
|
item = value;
|
|
}
|
|
}
|
|
|
|
void clear() {
|
|
items = items[0 .. 0];
|
|
}
|
|
|
|
void free() {
|
|
if (items.ptr != null) {
|
|
lib.free(items.ptr);
|
|
items = [];
|
|
capacity = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
struct FlagList(T) {
|
|
List!T data;
|
|
List!bool flags;
|
|
size_t hotIndex;
|
|
size_t openIndex;
|
|
|
|
this(size_t length) {
|
|
foreach (i; 0 .. length) {
|
|
append(T.init);
|
|
}
|
|
}
|
|
|
|
this(TT)(const(TT)[] args...) {
|
|
foreach (arg; args) {
|
|
append(arg);
|
|
}
|
|
}
|
|
|
|
this(TT)(List!TT list) {
|
|
foreach (item; list.items) {
|
|
append(item);
|
|
}
|
|
}
|
|
|
|
this(TT)(FlagList!TT list) {
|
|
data.resize(list.data.length);
|
|
flags.resize(list.flags.length);
|
|
foreach (i; 0 .. flags.length) {
|
|
data[i] = list.data[i];
|
|
flags[i] = list.flags[i];
|
|
}
|
|
hotIndex = list.hotIndex;
|
|
openIndex = list.openIndex;
|
|
}
|
|
|
|
ref T opIndex(size_t i, const(char)[] file = __FILE__, size_t line = __LINE__) {
|
|
if (!flags[i]) {
|
|
assert(0, "{}({}): ID {} doesn't exist.".fmt(file, line, i));
|
|
}
|
|
return data[i];
|
|
}
|
|
|
|
void opIndexAssign(TT)(TT rhs, size_t i) {
|
|
data[i] = rhs;
|
|
flags[i] = true;
|
|
hotIndex = i;
|
|
}
|
|
|
|
void opIndexOpAssign(string op, TT)(TT rhs, size_t i, const(char)[] file = __FILE__, size_t line = __LINE__) {
|
|
if (!flags[i]) {
|
|
assert(0, "{}({}): ID {} doesn't exist.".fmt(file, line, i));
|
|
}
|
|
mixin("data[i] " ~ op ~ "= rhs;");
|
|
}
|
|
|
|
size_t length() {
|
|
size_t result = 0;
|
|
foreach (id; ids) {
|
|
result += 1;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void append(TT)(const(TT)[] args...) {
|
|
foreach (arg; args) {
|
|
if (openIndex == flags.length) {
|
|
data.append(arg);
|
|
flags.append(true);
|
|
hotIndex = openIndex;
|
|
openIndex += 1;
|
|
} else {
|
|
foreach (i; openIndex .. flags.length) {
|
|
if (!flags[i]) {
|
|
data[i] = arg;
|
|
flags[i] = true;
|
|
hotIndex = i;
|
|
openIndex = i + 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void remove(size_t i, const(char)[] file = __FILE__, size_t line = __LINE__) {
|
|
if (!flags[i]) {
|
|
assert(0, "{}({}): ID {} doesn't exist.".fmt(file, line, i));
|
|
}
|
|
flags[i] = false;
|
|
hotIndex = i;
|
|
if (i < openIndex) {
|
|
openIndex = i;
|
|
}
|
|
}
|
|
|
|
void resize(size_t length) {
|
|
if (length < flags.length) {
|
|
// I could write more code but I don't think that people will really resize.
|
|
hotIndex = 0;
|
|
openIndex = 0;
|
|
}
|
|
data.resize(length);
|
|
flags.resize(length);
|
|
}
|
|
|
|
void fill(TT)(TT value) {
|
|
data.fill(value);
|
|
flags.fill(true);
|
|
hotIndex = flags.length - 1;
|
|
openIndex = flags.length;
|
|
}
|
|
|
|
void clear() {
|
|
data.clear();
|
|
flags.clear();
|
|
hotIndex = 0;
|
|
openIndex = 0;
|
|
}
|
|
|
|
void free() {
|
|
data.free();
|
|
flags.free();
|
|
hotIndex = 0;
|
|
openIndex = 0;
|
|
}
|
|
|
|
auto ids() {
|
|
struct Range {
|
|
bool[] flags;
|
|
size_t id;
|
|
|
|
bool empty() {
|
|
return id == flags.length;
|
|
}
|
|
|
|
size_t front() {
|
|
return id;
|
|
}
|
|
|
|
void popFront() {
|
|
id += 1;
|
|
while (id != flags.length && !flags[id]) {
|
|
id += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
size_t id = 0;
|
|
while (id < flags.length && !flags[id]) {
|
|
id += 1;
|
|
}
|
|
return Range(flags.items, id);
|
|
}
|
|
|
|
auto items() {
|
|
struct Range {
|
|
T[] data;
|
|
bool[] flags;
|
|
size_t id;
|
|
|
|
bool empty() {
|
|
return id == flags.length;
|
|
}
|
|
|
|
ref T front() {
|
|
return data[id];
|
|
}
|
|
|
|
void popFront() {
|
|
id += 1;
|
|
while (id != flags.length && !flags[id]) {
|
|
id += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
size_t id = 0;
|
|
while (id < flags.length && !flags[id]) {
|
|
id += 1;
|
|
}
|
|
return Range(data.items, flags.items, id);
|
|
}
|
|
}
|
|
|
|
size_t findListCapacity(size_t length) {
|
|
enum defaultCapacity = 64;
|
|
|
|
size_t result = defaultCapacity;
|
|
while (result < length) {
|
|
result *= 2;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
struct Grid(T) {
|
|
List!T cells;
|
|
float cellWidth;
|
|
float cellHeight;
|
|
size_t rowCount;
|
|
size_t colCount;
|
|
|
|
this(size_t rowCount, size_t colCount, size_t cellWidth, size_t cellHeight) {
|
|
this.cellWidth = cellWidth;
|
|
this.cellHeight = cellHeight;
|
|
resize(rowCount, colCount);
|
|
}
|
|
|
|
this(size_t rowCount, size_t colCount) {
|
|
resize(rowCount, colCount);
|
|
}
|
|
|
|
// TODO: Learn how to slice only rows, ...
|
|
T[] opSlice(size_t dim)(size_t i, size_t j) {
|
|
return items[i .. j];
|
|
}
|
|
|
|
T[] opIndex() {
|
|
return cells[];
|
|
}
|
|
|
|
T[] opIndex(T[] slice) {
|
|
return slice;
|
|
}
|
|
|
|
ref T opIndex(size_t i) {
|
|
return cells[i];
|
|
}
|
|
|
|
ref T opIndex(size_t row, size_t col) {
|
|
return cells[colCount * row + col];
|
|
}
|
|
|
|
void opIndexAssign(TT)(TT rhs, size_t i) {
|
|
cells[i] = rhs;
|
|
}
|
|
|
|
void opIndexAssign(TT)(TT rhs, size_t row, size_t col) {
|
|
cells[colCount * row + col] = rhs;
|
|
}
|
|
|
|
void opIndexOpAssign(string op, TT)(TT rhs, size_t i) {
|
|
mixin("cells[i] " ~ op ~ "= rhs;");
|
|
}
|
|
|
|
void opIndexOpAssign(string op, TT)(TT rhs, size_t row, size_t col) {
|
|
mixin("cells[colCount * row + col] " ~ op ~ "= rhs;");
|
|
}
|
|
|
|
size_t opDollar(size_t dim)() {
|
|
static if (dim == 0) {
|
|
return rowCount;
|
|
} else {
|
|
return colCount;
|
|
}
|
|
}
|
|
|
|
size_t length() {
|
|
return cells.length;
|
|
}
|
|
|
|
float width() {
|
|
return cellWidth * colCount;
|
|
}
|
|
|
|
float height() {
|
|
return cellHeight * rowCount;
|
|
}
|
|
|
|
void resize(size_t rowCount, size_t colCount) {
|
|
this.cells.resize(rowCount * colCount);
|
|
this.rowCount = rowCount;
|
|
this.colCount = colCount;
|
|
}
|
|
|
|
void fill(TT)(TT value) {
|
|
cells.fill(value);
|
|
}
|
|
|
|
void clear() {
|
|
cells.clear();
|
|
rowCount = 0;
|
|
colCount = 0;
|
|
}
|
|
|
|
void free() {
|
|
cells.free();
|
|
rowCount = 0;
|
|
colCount = 0;
|
|
}
|
|
}
|
|
|
|
unittest {}
|