mirror of https://github.com/deavmi/niknaks
Merge f71286e85c
into b6106883da
This commit is contained in:
commit
9003c59473
|
@ -11,6 +11,8 @@ import std.datetime.stopwatch : StopWatch, AutoStart;
|
||||||
import core.thread : Thread;
|
import core.thread : Thread;
|
||||||
import core.sync.condition : Condition;
|
import core.sync.condition : Condition;
|
||||||
import std.functional : toDelegate;
|
import std.functional : toDelegate;
|
||||||
|
import core.exception : ArrayIndexError;
|
||||||
|
import core.exception : RangeError;
|
||||||
|
|
||||||
version(unittest)
|
version(unittest)
|
||||||
{
|
{
|
||||||
|
@ -597,4 +599,434 @@ unittest
|
||||||
|
|
||||||
// Destroy the map (such that it ends the sweeper
|
// Destroy the map (such that it ends the sweeper
|
||||||
destroy(map);
|
destroy(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct Sector(T)
|
||||||
|
{
|
||||||
|
private T[] data;
|
||||||
|
|
||||||
|
this(T[] data)
|
||||||
|
{
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T opIndex(size_t idx)
|
||||||
|
{
|
||||||
|
return this.data[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void opIndexAssign(T value, size_t index)
|
||||||
|
{
|
||||||
|
this.data[index] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Contract: Obtaining the length must be present
|
||||||
|
public size_t opDollar()
|
||||||
|
{
|
||||||
|
return this.data.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Contract: Obtaining the length must be present
|
||||||
|
@property
|
||||||
|
public size_t length()
|
||||||
|
{
|
||||||
|
return opDollar();
|
||||||
|
}
|
||||||
|
|
||||||
|
public T[] opSlice(size_t start, size_t end)
|
||||||
|
{
|
||||||
|
return this.data[start..end];
|
||||||
|
}
|
||||||
|
|
||||||
|
public T[] opSlice()
|
||||||
|
{
|
||||||
|
return opSlice(0, opDollar);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Contract: Rezising must be implemented
|
||||||
|
// TODO: This would then be the very reason for
|
||||||
|
// using ref actually, as resizing may only
|
||||||
|
// change a local copy when extding on
|
||||||
|
// the tail-end "extent" (SectorType)
|
||||||
|
|
||||||
|
// Actually should resizing even be done here?
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Make a bit better
|
||||||
|
private bool isSector(S)()
|
||||||
|
{
|
||||||
|
return __traits(hasMember, S, "opIndex");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public struct View(T, SectorType = Sector!(T))
|
||||||
|
if(isSector!(SectorType)())
|
||||||
|
{
|
||||||
|
private SectorType[] sectors;
|
||||||
|
// private
|
||||||
|
|
||||||
|
// Maybe current size should be here as we
|
||||||
|
// are a view, we should allow modofication
|
||||||
|
// but not make any NEW arrays
|
||||||
|
private size_t curSize;
|
||||||
|
|
||||||
|
private size_t computeTotalLen()
|
||||||
|
{
|
||||||
|
size_t l;
|
||||||
|
foreach(SectorType sector; this.sectors)
|
||||||
|
{
|
||||||
|
l += sector.opDollar();
|
||||||
|
}
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
|
public size_t opDollar()
|
||||||
|
{
|
||||||
|
return this.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T opIndex(size_t idx)
|
||||||
|
{
|
||||||
|
// Within range of "fake" size
|
||||||
|
if(!(idx < this.length))
|
||||||
|
{
|
||||||
|
throw new ArrayIndexError(idx, this.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t thunk;
|
||||||
|
foreach(SectorType sector; this.sectors)
|
||||||
|
{
|
||||||
|
if(idx-thunk < sector.opDollar())
|
||||||
|
{
|
||||||
|
return sector[idx-thunk];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
thunk += sector.opDollar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: This should be unreachable but
|
||||||
|
// compiler moans and groans
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void opIndexAssign(T value, size_t idx)
|
||||||
|
{
|
||||||
|
// Within range of "fake" size
|
||||||
|
if(!(idx < this.length))
|
||||||
|
{
|
||||||
|
throw new ArrayIndexError(idx, this.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t thunk;
|
||||||
|
// TODO: Should be ref, else it is just a local struct copy
|
||||||
|
// could cheat if sector is never replaced, hence why it works
|
||||||
|
foreach(SectorType sector; this.sectors)
|
||||||
|
{
|
||||||
|
version(unittest)
|
||||||
|
{
|
||||||
|
writeln(sector);
|
||||||
|
writeln("idx: ", idx);
|
||||||
|
writeln("thunk: ", thunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(idx-thunk < sector.opDollar())
|
||||||
|
{
|
||||||
|
sector[idx-thunk] = value;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
thunk += sector.opDollar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public T[] opSlice()
|
||||||
|
{
|
||||||
|
return this[0..this.length];
|
||||||
|
}
|
||||||
|
|
||||||
|
public T[] opSlice(size_t start, size_t end)
|
||||||
|
{
|
||||||
|
// Invariant of start <= end
|
||||||
|
if(!(start <= end))
|
||||||
|
{
|
||||||
|
throw new RangeError("Starting index must be smaller than or equal to ending index");
|
||||||
|
}
|
||||||
|
// If the indices are equal, then it is empty
|
||||||
|
else if(start == end)
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
// Within range of "fake" size
|
||||||
|
else if(!((start < this.length) && (end <= this.length)))
|
||||||
|
{
|
||||||
|
throw new RangeError("start index or end index not under range");
|
||||||
|
}
|
||||||
|
|
||||||
|
T[] collected;
|
||||||
|
|
||||||
|
size_t thunk;
|
||||||
|
foreach(SectorType sector; this.sectors)
|
||||||
|
{
|
||||||
|
// If the current sector contains
|
||||||
|
// both the starting AND ending
|
||||||
|
// indices
|
||||||
|
if(start-thunk < sector.opDollar() && end-thunk <= sector.opDollar())
|
||||||
|
{
|
||||||
|
return sector[start-thunk..end-thunk];
|
||||||
|
}
|
||||||
|
// If the current sector's starting
|
||||||
|
// index (only) is included
|
||||||
|
else if(start-thunk < sector.opDollar() && !(end-thunk <= sector.opDollar()))
|
||||||
|
{
|
||||||
|
collected ~= sector[start-thunk..$];
|
||||||
|
}
|
||||||
|
// If the current sector's ending
|
||||||
|
// index (only) is included
|
||||||
|
else if(!(start-thunk < sector.opDollar()) && end-thunk <= sector.opDollar())
|
||||||
|
{
|
||||||
|
collected ~= sector[0..end-thunk];
|
||||||
|
}
|
||||||
|
// If the current sector's entirety
|
||||||
|
// is to be included
|
||||||
|
else
|
||||||
|
{
|
||||||
|
collected ~= sector[];
|
||||||
|
}
|
||||||
|
|
||||||
|
thunk += sector.opDollar();
|
||||||
|
}
|
||||||
|
|
||||||
|
return collected;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool isArrayAppend(P)()
|
||||||
|
{
|
||||||
|
return __traits(isSame, P, T[]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool isElementAppend(P)()
|
||||||
|
{
|
||||||
|
return __traits(isSame, P, T);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append
|
||||||
|
public void opOpAssign(string op, E)(E value)
|
||||||
|
if(op == "~" && (isArrayAppend!(E) || isElementAppend!(E)))
|
||||||
|
{
|
||||||
|
static if(isArrayAppend!(E))
|
||||||
|
{
|
||||||
|
add(value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
add([value]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Takes the data, constructs a kind-of SectorType
|
||||||
|
// and adds it
|
||||||
|
private void add(T[] data)
|
||||||
|
{
|
||||||
|
// Create a new sector
|
||||||
|
SectorType sec = SectorType(data);
|
||||||
|
|
||||||
|
// Update the tracking size
|
||||||
|
this.curSize += sec.length;
|
||||||
|
|
||||||
|
// Concatenate it to the view
|
||||||
|
this.sectors ~= SectorType(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@property
|
||||||
|
public size_t length()
|
||||||
|
{
|
||||||
|
return this.curSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
@property
|
||||||
|
public void length(size_t size)
|
||||||
|
{
|
||||||
|
// TODO: Need we continuously compute this?
|
||||||
|
// ... we should have a tracking field for
|
||||||
|
// ... this
|
||||||
|
size_t actualSize = computeTotalLen();
|
||||||
|
|
||||||
|
// On successful exit, update the "fake" size
|
||||||
|
scope(success)
|
||||||
|
{
|
||||||
|
this.curSize = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Don't allow sizing up (doesn't make sense for a view)
|
||||||
|
if(size > actualSize)
|
||||||
|
{
|
||||||
|
auto r = new RangeError();
|
||||||
|
r.msg = "Cannot extend the size of a view past its total size (of all attached sectors)";
|
||||||
|
throw r;
|
||||||
|
}
|
||||||
|
// If nothing changes
|
||||||
|
else if(size == actualSize)
|
||||||
|
{
|
||||||
|
// Nothing
|
||||||
|
}
|
||||||
|
// If shrinking to zero
|
||||||
|
else if(size == 0)
|
||||||
|
{
|
||||||
|
// Just drop everything
|
||||||
|
this.sectors.length = 0;
|
||||||
|
}
|
||||||
|
// If shrinking (arbitrary)
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Sectors from left-to-right to keep
|
||||||
|
size_t sectorCnt;
|
||||||
|
|
||||||
|
// Accumulator
|
||||||
|
size_t accumulator;
|
||||||
|
|
||||||
|
foreach(SectorType sector; this.sectors)
|
||||||
|
{
|
||||||
|
accumulator += sector.length;
|
||||||
|
sectorCnt++;
|
||||||
|
if(size <= accumulator)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.sectors.length = sectorCnt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unittest
|
||||||
|
{
|
||||||
|
View!(int) view;
|
||||||
|
assert(view.opDollar() == 0);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
view[1];
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
catch(ArrayIndexError e)
|
||||||
|
{
|
||||||
|
assert(e.index == 1);
|
||||||
|
assert(e.length == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
view ~= [1,3,45];
|
||||||
|
assert(view.opDollar() == 3);
|
||||||
|
assert(view.length == 3);
|
||||||
|
|
||||||
|
view ~= 2;
|
||||||
|
assert(view.opDollar() == 4);
|
||||||
|
assert(view.length == 4);
|
||||||
|
|
||||||
|
assert(view[0] == 1);
|
||||||
|
assert(view[1] == 3);
|
||||||
|
assert(view[2] == 45);
|
||||||
|
assert(view[3] == 2);
|
||||||
|
assert(view[0..2] == [1,3]);
|
||||||
|
assert(view[0..4] == [1,3,45,2]);
|
||||||
|
|
||||||
|
// Update elements
|
||||||
|
view[0] = 71;
|
||||||
|
view[3] = 50;
|
||||||
|
|
||||||
|
// Set size to same size
|
||||||
|
view.length = view.length;
|
||||||
|
|
||||||
|
// Check that update is present
|
||||||
|
// and size unchanged
|
||||||
|
int[] all = view[];
|
||||||
|
assert(all == [71,3,45,50]);
|
||||||
|
|
||||||
|
// Truncate by 1 element
|
||||||
|
view.length = view.length-1;
|
||||||
|
all = view[];
|
||||||
|
assert(all == [71,3,45]);
|
||||||
|
|
||||||
|
// This should fail
|
||||||
|
try
|
||||||
|
{
|
||||||
|
view[3] = 3;
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
catch(RangeError e)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// This should fail
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int j = view[3];
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
catch(RangeError e)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// Up-sizing past real size should not be allowed
|
||||||
|
try
|
||||||
|
{
|
||||||
|
view.length = view.length+1;
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
catch(RangeError e)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// Size to zero
|
||||||
|
view.length = 0;
|
||||||
|
assert(view.length == 0);
|
||||||
|
assert(view[] == []);
|
||||||
|
}
|
||||||
|
|
||||||
|
unittest
|
||||||
|
{
|
||||||
|
View!(int) view;
|
||||||
|
view ~= 1;
|
||||||
|
view ~= [2,3,4];
|
||||||
|
view ~= 5;
|
||||||
|
|
||||||
|
assert(view[0..5] == [1,2,3,4,5]);
|
||||||
|
|
||||||
|
// test: start <= end invariant broken
|
||||||
|
try
|
||||||
|
{
|
||||||
|
auto j = view[1..0];
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
catch(RangeError e)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// test: end out of bounds
|
||||||
|
try
|
||||||
|
{
|
||||||
|
auto j = view[1..view.length+1];
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
catch(RangeError e)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int[] d = [1,2,3];
|
||||||
|
writeln("according to dlang: ", d[1..2]);
|
||||||
|
|
||||||
|
writeln("test lekker: ", view[1..2]);
|
||||||
|
assert(view[1..2] == [2]);
|
||||||
|
|
||||||
|
writeln("test lekker: ", view[1..1]);
|
||||||
|
assert(view[1..1] == []);
|
||||||
}
|
}
|
Loading…
Reference in New Issue