Moved monster files into the openmw repos
git-svn-id: https://openmw.svn.sourceforge.net/svnroot/openmw/trunk@12 ea6a568a-9f4f-0410-981a-c910a81bb256actorid
parent
16e6da7d69
commit
888c552f1b
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,147 @@
|
||||
/*
|
||||
Monster - an advanced game scripting language
|
||||
Copyright (C) 2007, 2008 Nicolay Korslund
|
||||
Email: <korslund@gmail.com>
|
||||
WWW: http://monster.snaptoad.com/
|
||||
|
||||
This file (freelist.d) is part of the Monster script language
|
||||
package.
|
||||
|
||||
Monster is distributed as free software: you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License
|
||||
version 3, as published by the Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
version 3 along with this program. If not, see
|
||||
http://www.gnu.org/licenses/ .
|
||||
|
||||
*/
|
||||
|
||||
module monster.util.freelist;
|
||||
|
||||
// TODO: Create unittests
|
||||
|
||||
import monster.util.list;
|
||||
import monster.util.growarray;
|
||||
|
||||
// This had to be moved outside FreeList to work around some
|
||||
// irritating DMD template problems. (Can you say Aaargh!)
|
||||
struct __FreeNode(T)
|
||||
{
|
||||
_lstNode!(T) data;
|
||||
int index;
|
||||
}
|
||||
|
||||
// A list that uses a freelist for allocation. Based on
|
||||
// LinkedList. Very basic, only functions that are actually in use in
|
||||
// my own code are implemented.
|
||||
struct FreeList(T)
|
||||
{
|
||||
alias LinkedList!(T, NoAlloc) TList;
|
||||
alias TList.Node TNode;
|
||||
|
||||
private:
|
||||
|
||||
/*
|
||||
static struct _FreeNode
|
||||
{
|
||||
TNode data;
|
||||
int index;
|
||||
}
|
||||
*/
|
||||
alias __FreeNode!(T) _FreeNode;
|
||||
|
||||
// This is the array that does all the actual allocations. It is
|
||||
// used for quickly looking up indices.
|
||||
static GrowArray!(_FreeNode) array;
|
||||
|
||||
// The freelist. This is shared between all template instances of
|
||||
// the same type, as far as I know. DMD might have some strange
|
||||
// behavior that I am not aware of, but the worst case is that we
|
||||
// end up with multiple freelists, which is not the end of the world
|
||||
// (although slightly inefficient.)
|
||||
static TList freeList;
|
||||
|
||||
// The nodes belonging to THIS list
|
||||
TList nodes;
|
||||
|
||||
public:
|
||||
// Get a new node (move from freelist to node list)
|
||||
T* getNew()
|
||||
{
|
||||
// Is the freelist empty?
|
||||
if(freeList.length == 0)
|
||||
{
|
||||
// Create a bunch of nodes and shove them into the freelist.
|
||||
const makeSize = 100;
|
||||
|
||||
// Grow the growarray
|
||||
uint len = array.length;
|
||||
array.length = len + makeSize;
|
||||
|
||||
// Loop through the new nodes, number them, and insert them
|
||||
// into freeList
|
||||
for(int i=0; i < makeSize; i++)
|
||||
{
|
||||
_FreeNode *fn = array.getPtr(i+len);
|
||||
fn.index = i + len;
|
||||
freeList.insertNode(&fn.data);
|
||||
}
|
||||
}
|
||||
|
||||
// Move the first element from the freelist into the node list.
|
||||
auto node = freeList.getHead;
|
||||
freeList.removeNode(node);
|
||||
nodes.insertNodeFirst(node);
|
||||
|
||||
// Return the value pointer. Since the value is always at the
|
||||
// begining of the Node struct, this is the same
|
||||
// pointer. LinkedList lets us choose if we want to use T* or
|
||||
// Node*.
|
||||
return &node.value;
|
||||
}
|
||||
|
||||
// Get the node corresponding to an index
|
||||
static T* getNode(int index)
|
||||
{
|
||||
return &array.getPtr(index).data.value;
|
||||
}
|
||||
|
||||
// Get the index from a node
|
||||
static int getIndex(T *node)
|
||||
{
|
||||
return ( cast(_FreeNode*)node ).index;
|
||||
}
|
||||
|
||||
// Move a node back to the freelist ("delete" it)
|
||||
void remove(T* node)
|
||||
{
|
||||
nodes.removeNode(node);
|
||||
freeList.insertNodeFirst(node);
|
||||
}
|
||||
|
||||
uint length() { return nodes.length; }
|
||||
static uint totLength() { return array.length; }
|
||||
|
||||
// Move the given node to another list
|
||||
T* moveTo(ref FreeList fl, T* node)
|
||||
{
|
||||
nodes.removeNode(node);
|
||||
fl.nodes.insertNodeFirst(node);
|
||||
return node;
|
||||
}
|
||||
|
||||
// Get the first element in the list
|
||||
T* getHead() { return &nodes.getHead().value; }
|
||||
|
||||
// Loop through the structs in this list
|
||||
int opApply(int delegate(ref T) dg)
|
||||
{
|
||||
return nodes.opApply(dg);
|
||||
}
|
||||
}
|
@ -0,0 +1,297 @@
|
||||
/*
|
||||
Monster - an advanced game scripting language
|
||||
Copyright (C) 2007, 2008 Nicolay Korslund
|
||||
Email: <korslund@gmail.com>
|
||||
WWW: http://monster.snaptoad.com/
|
||||
|
||||
This file (growarray.d) is part of the Monster script language
|
||||
package.
|
||||
|
||||
Monster is distributed as free software: you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License
|
||||
version 3, as published by the Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
version 3 along with this program. If not, see
|
||||
http://www.gnu.org/licenses/ .
|
||||
|
||||
*/
|
||||
|
||||
module monster.util.growarray;
|
||||
|
||||
// Array that grows without reallocations.
|
||||
struct GrowArray(T)
|
||||
{
|
||||
const defSize = 128;
|
||||
|
||||
private:
|
||||
uint listSize = defSize; // Size of new lists
|
||||
uint elements; // Current number of elements
|
||||
uint elemsAlloc; // Elements allocated
|
||||
|
||||
uint begin; // At what element to start counting. Used for
|
||||
// slices.
|
||||
|
||||
T[][] listList;
|
||||
|
||||
// Make sure there is room for at least 'size' elements in total.
|
||||
void alloc(uint size)
|
||||
{
|
||||
// Do nothing if the list is large enough
|
||||
if(size <= elemsAlloc) return;
|
||||
|
||||
// If this is a slice, we must always reallocate when
|
||||
// growing. Implement that later.
|
||||
if(begin) assert(0, "Cannot grow a slice");
|
||||
|
||||
// Number of lists we need
|
||||
uint lists = ((size-1) / listSize) + 1;
|
||||
|
||||
// The number of needed elements should never decrease
|
||||
assert((listSize*lists) >= elemsAlloc);
|
||||
|
||||
// Number of elements we need allocated
|
||||
elemsAlloc = listSize * lists;
|
||||
|
||||
// Make sure the list of lists is large enough
|
||||
if(listList.length < lists)
|
||||
listList.length = lists+30;
|
||||
|
||||
// Allocate the lists we need
|
||||
for(int i=0; i<lists; i++)
|
||||
listList[i].length = listSize;
|
||||
}
|
||||
|
||||
public:
|
||||
uint length() { return elements - begin; }
|
||||
void length(uint newLen)
|
||||
{
|
||||
newLen += begin;
|
||||
|
||||
alloc(newLen);
|
||||
|
||||
elements = newLen;
|
||||
}
|
||||
|
||||
// Initialize - set the current size
|
||||
void initialize(uint size = 0, uint listSize = defSize)
|
||||
{
|
||||
assert(listList.length == 0);
|
||||
this.listSize = listSize;
|
||||
length(size);
|
||||
}
|
||||
|
||||
static GrowArray opCall(uint size = 0, uint listSize = defSize)
|
||||
{
|
||||
GrowArray a;
|
||||
a.initialize(size, listSize);
|
||||
return a;
|
||||
}
|
||||
|
||||
void opCatAssign(T t)
|
||||
{
|
||||
length = length + 1;
|
||||
opIndexAssign(t, length-1);
|
||||
}
|
||||
|
||||
void opCatAssign(T[] list)
|
||||
{
|
||||
uint len = length;
|
||||
length = len + list.length;
|
||||
foreach(int i, ref T t; list)
|
||||
opIndexAssign(t, len+i);
|
||||
}
|
||||
|
||||
T opIndex(int index)
|
||||
{
|
||||
index += begin;
|
||||
assert(index >= begin && index < elements,
|
||||
"GrowArray index out of bounds");
|
||||
|
||||
return listList[index/listSize][index%listSize];
|
||||
}
|
||||
|
||||
T opIndexAssign(T value, int index)
|
||||
{
|
||||
index += begin;
|
||||
assert(index >= begin && index < elements,
|
||||
"GrowArray index out of bounds");
|
||||
|
||||
return (listList[index/listSize][index%listSize] = value);
|
||||
}
|
||||
|
||||
T* getPtr(int index)
|
||||
{
|
||||
index += begin;
|
||||
assert(index >= begin && index < elements,
|
||||
"GrowArray index out of bounds");
|
||||
|
||||
return &listList[index/listSize][index%listSize];
|
||||
}
|
||||
|
||||
GrowArray opSlice(int start, int stop)
|
||||
{
|
||||
assert(start<=stop, "Illegal GrowArray slice");
|
||||
GrowArray ga = *this;
|
||||
ga.begin = begin+start;
|
||||
ga.length = stop-start;
|
||||
return ga;
|
||||
}
|
||||
|
||||
GrowArray opSlice()
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
int opApply(int delegate(ref int, ref T) dg)
|
||||
{
|
||||
int res;
|
||||
int len = length;
|
||||
int pos = begin%listSize;
|
||||
int list = begin/listSize;
|
||||
for(int i; i<len; i++)
|
||||
{
|
||||
res = dg(i, listList[list][pos++]);
|
||||
if(res) break;
|
||||
|
||||
if(pos == listSize)
|
||||
{
|
||||
list++;
|
||||
pos = 0;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
int opApply(int delegate(ref T) dg)
|
||||
{
|
||||
int res;
|
||||
int len = length;
|
||||
int pos = begin%listSize;
|
||||
int list = begin/listSize;
|
||||
for(int i; i<len; i++)
|
||||
{
|
||||
res = dg(listList[list][pos++]);
|
||||
if(res) break;
|
||||
|
||||
if(pos == listSize)
|
||||
{
|
||||
list++;
|
||||
pos = 0;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
unittest
|
||||
{
|
||||
// Test default initialization
|
||||
GrowArray!(int) arr;
|
||||
assert(arr.begin == 0);
|
||||
assert(arr.length == 0);
|
||||
assert(arr.elements == 0);
|
||||
assert(arr.elemsAlloc == 0);
|
||||
assert(arr.listList.length == 0);
|
||||
assert(arr.listSize == 128);
|
||||
|
||||
// Setting length
|
||||
arr.length = 3;
|
||||
assert(arr.length == 3);
|
||||
assert(arr.elements == 3);
|
||||
assert(arr.elemsAlloc == arr.listSize);
|
||||
assert(arr.listList.length >= 1);
|
||||
|
||||
// Setting and reading elements
|
||||
arr[0] = 1;
|
||||
arr[1] = 2;
|
||||
arr[2] = 3;
|
||||
assert(arr[0] == 1);
|
||||
assert(arr[1] == 2);
|
||||
assert(arr[2] == 3);
|
||||
assert(arr.listList[0][0] == 1);
|
||||
assert(arr.listList[0][1] == 2);
|
||||
assert(arr.listList[0][2] == 3);
|
||||
|
||||
// Test opCatAssign
|
||||
arr ~= 4;
|
||||
assert(arr.length == 4);
|
||||
assert(arr[3] == 4);
|
||||
|
||||
// Foreach
|
||||
int tmp = 0;
|
||||
foreach(int i, int v; arr)
|
||||
{
|
||||
assert(v==i+1);
|
||||
tmp++;
|
||||
}
|
||||
assert(tmp == 4);
|
||||
|
||||
tmp = 1;
|
||||
foreach(int v; arr)
|
||||
assert(v == tmp++);
|
||||
assert(tmp == 5);
|
||||
|
||||
// Slicing the entire array
|
||||
arr = arr[0..4];
|
||||
assert(arr.length == 4);
|
||||
assert(arr[3] == 4);
|
||||
|
||||
// Slicing part of the array
|
||||
auto arrS = arr[1..3];
|
||||
assert(arrS.length == 2);
|
||||
assert(arrS[0] == 2);
|
||||
assert(arrS[1] == 3);
|
||||
arrS[0] = 10;
|
||||
assert(arr[1] == 10);
|
||||
|
||||
// Slicing the slice
|
||||
arrS = arrS[1..2];
|
||||
assert(arrS.length == 1);
|
||||
assert(arrS[0] == 3);
|
||||
|
||||
// Empty slice
|
||||
arrS = arr[3..3];
|
||||
assert(arrS.length == 0);
|
||||
|
||||
// Custom list size, and more than one list
|
||||
auto arr2 = GrowArray!(byte)(3,2);
|
||||
assert(arr2.length == 3);
|
||||
assert(arr2.elements == 3);
|
||||
assert(arr2.listSize == 2);
|
||||
assert(arr2.elemsAlloc == 4);
|
||||
assert(arr2.listList.length >= 2);
|
||||
assert(arr2.listList[0].length == 2);
|
||||
|
||||
assert(arr2[0] == 0);
|
||||
assert(arr2[1] == 0);
|
||||
assert(arr2[2] == 0);
|
||||
|
||||
arr2[1]=2;
|
||||
arr2[2]=4;
|
||||
|
||||
foreach(int i, byte v; arr2)
|
||||
assert(v == 2*i);
|
||||
|
||||
// Check that boundry checking works (in non-release mode.)
|
||||
bool err = false;
|
||||
try{arr2[3];}
|
||||
catch
|
||||
{
|
||||
err = true;
|
||||
}
|
||||
assert(err == true);
|
||||
|
||||
err = false;
|
||||
try{arr2[3] = 0;}
|
||||
catch
|
||||
{
|
||||
err = true;
|
||||
}
|
||||
assert(err == true);
|
||||
}
|
@ -0,0 +1,640 @@
|
||||
/*
|
||||
Monster - an advanced game scripting language
|
||||
Copyright (C) 2007, 2008 Nicolay Korslund
|
||||
Email: <korslund@gmail.com>
|
||||
WWW: http://monster.snaptoad.com/
|
||||
|
||||
This file (list.d) is part of the Monster script language
|
||||
package.
|
||||
|
||||
Monster is distributed as free software: you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License
|
||||
version 3, as published by the Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
version 3 along with this program. If not, see
|
||||
http://www.gnu.org/licenses/ .
|
||||
|
||||
*/
|
||||
|
||||
module monster.util.list;
|
||||
|
||||
// Set this to enable some more extensive list checks. These will loop
|
||||
// through the entire list on every insert and remove, so they are
|
||||
// very slow for large lists. But they are very handy bug catchers
|
||||
// when doing a little dirty list hacking, and they have saved me in
|
||||
// the past.
|
||||
|
||||
// debug=slowcheck;
|
||||
|
||||
private import std.c.stdlib;
|
||||
private import std.string;
|
||||
|
||||
typedef void GCAlloc;
|
||||
|
||||
alias malloc cmalloc;
|
||||
alias free cfree;
|
||||
|
||||
class LinkedListException : Exception
|
||||
{
|
||||
this(char[] msg)
|
||||
{
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Internal structure used by List
|
||||
*/
|
||||
|
||||
align(1)
|
||||
struct _lstNode(Value)
|
||||
{
|
||||
// It is essential that the value is first in the struct. This
|
||||
// allows us to interchange pointers to the value with pointer to
|
||||
// the Node. This is done for convenience - allowing us to use the
|
||||
// value directly instead of using somePtr.value, This also
|
||||
// sidesteps the fact that DMD isn't very good with template forward
|
||||
// references, something that creates a lot of problems if we use
|
||||
// LinkedList.Iterator for everything (trust me on this.)
|
||||
Value value;
|
||||
|
||||
_lstNode* getNext() { return next; }
|
||||
_lstNode* getPrev() { return prev; }
|
||||
|
||||
private:
|
||||
_lstNode* next; // Next node
|
||||
_lstNode* prev; // Previous node
|
||||
}
|
||||
|
||||
/*
|
||||
* This is a doubly linked list. It's not terribly advanced at the
|
||||
* moment, but I don't need any more functionality right now.
|
||||
*
|
||||
* Alloc must have the following members:
|
||||
* void* alloc(uint size)
|
||||
* void free(void*)
|
||||
* bool autoinit; // True if alloc automatically sets memory to zero.
|
||||
*/
|
||||
|
||||
// Example allocator using malloc and free:
|
||||
struct Malloc
|
||||
{
|
||||
static const bool autoinit = false; // malloc does not initialize memory
|
||||
static const bool usefree = true; // We must call free() to release memory
|
||||
static void* alloc(uint size) { return cmalloc(size); }
|
||||
static void free(void* p) { cfree(p); }
|
||||
}
|
||||
|
||||
// A null allocator. Use if you only intend to move nodes into and out
|
||||
// of the list, not to allocate them. Useful for a freelist, for
|
||||
// example.
|
||||
struct NoAlloc
|
||||
{
|
||||
static const bool autoinit = false;
|
||||
static const bool usefree = true;
|
||||
static void *alloc(uint size) { assert(0, "NoAlloc.alloc not allowed"); }
|
||||
static void free(void *p) { assert(0, "NoAlloc.free not allowed"); }
|
||||
}
|
||||
|
||||
struct LinkedList(Value, alias Alloc = GCAlloc)
|
||||
{
|
||||
alias _lstNode!(Value) Node;
|
||||
|
||||
private:
|
||||
|
||||
Node *head; // This is the head of the linked list (first element)
|
||||
Node *tail; // New nodes are inserted here
|
||||
uint totalNum; // Number of elements
|
||||
|
||||
// Determine if the allocator automatically initializes memory
|
||||
static if(is(Alloc == GCAlloc))
|
||||
static const bool autoinit = true;
|
||||
else static if(Alloc.autoinit)
|
||||
static const bool autoinit = true;
|
||||
else
|
||||
static const bool autoinit = false;
|
||||
|
||||
// Determine if we have to manually free memory
|
||||
static if(is(Alloc == GCAlloc))
|
||||
static const bool usefree = false;
|
||||
else static if(Alloc.usefree)
|
||||
static const bool usefree = true;
|
||||
else
|
||||
static const bool usefree = false;
|
||||
|
||||
// Throw an exception
|
||||
void fail(char[] msg)
|
||||
{
|
||||
msg = format("LinkedList!(%s) exception: %s", typeid(Value).toString, msg);
|
||||
throw new LinkedListException(msg);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
// Whenever you find a bug that creates an invalid state, put it in
|
||||
// here so we can safeguard against regressions
|
||||
|
||||
invariant()
|
||||
{
|
||||
if(head != null || tail != null || totalNum != 0)
|
||||
{
|
||||
assert(head != null);
|
||||
assert(tail != null);
|
||||
assert(totalNum != 0);
|
||||
|
||||
assert(head.prev == null);
|
||||
assert(tail.next == null);
|
||||
}
|
||||
}
|
||||
|
||||
alias Node* Iterator;
|
||||
|
||||
// Simply reset all pointers and variables, losing any nodes
|
||||
// present.
|
||||
void reset()
|
||||
{
|
||||
head = tail = null;
|
||||
totalNum = 0;
|
||||
}
|
||||
|
||||
// Go through the list and delete all nodes
|
||||
void deleteAll()
|
||||
{
|
||||
// If there is no need to free objects, then deleteAll() is
|
||||
// equivalent to reset().
|
||||
static if(usefree)
|
||||
{
|
||||
// Loop through the list and delete everything
|
||||
Node *p = head;
|
||||
while(p != null)
|
||||
{
|
||||
Node *next = p.next;
|
||||
Alloc.free(p);
|
||||
p = next;
|
||||
}
|
||||
}
|
||||
reset();
|
||||
}
|
||||
|
||||
Iterator getHead() { return head; }
|
||||
Iterator getTail() { return tail; }
|
||||
|
||||
// Check if the given iterator is part of the list
|
||||
bool hasIterator(Node *v)
|
||||
{
|
||||
Node* p = head;
|
||||
|
||||
while(p != null)
|
||||
{
|
||||
if(p == v)
|
||||
{
|
||||
assert(length >= 1);
|
||||
return true;
|
||||
}
|
||||
p = p.next;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Insert a value at the end of the list.
|
||||
alias insert insertLast;
|
||||
Iterator insert(Value v)
|
||||
{
|
||||
Node *p = createNode();
|
||||
p.value = v;
|
||||
return insertNode(p);
|
||||
}
|
||||
// Also allow ~= syntax for this
|
||||
Iterator opCatAssign(Value v) { return insert(v); }
|
||||
|
||||
// Insert an existing node at the end of the list. The insertNode*()
|
||||
// variants along with removeNode() are useful for removing and
|
||||
// reinserting nodes without allocating more memory. This can for
|
||||
// example be used for free lists and similar constructions. In
|
||||
// other words, you can use these to move elements from one list to
|
||||
// another.
|
||||
alias insertNode insertNodeLast;
|
||||
Iterator insertNode(Node *p)
|
||||
in
|
||||
{
|
||||
//debug(slowcheck)
|
||||
assert(!hasIterator(p), "inserNode: Node is already in the list");
|
||||
}
|
||||
body
|
||||
{
|
||||
if(tail)
|
||||
{
|
||||
// Insert node at the end of the list
|
||||
assert(head != null);
|
||||
tail.next = p;
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is the first element to be inserted
|
||||
assert(head == null);
|
||||
head = p;
|
||||
}
|
||||
p.prev = tail;
|
||||
tail = p;
|
||||
p.next = null;
|
||||
|
||||
totalNum++;
|
||||
|
||||
return p;
|
||||
}
|
||||
// The Value* variants of the node functions work the same way as
|
||||
// their Iterator (Node*) versions. The pointers are the same, they
|
||||
// just need to be recast.
|
||||
Value* insertNode(Value *p)
|
||||
{ return &insertNode( cast(Node*)p ).value; }
|
||||
|
||||
// Beginning of the list
|
||||
Iterator insertFirst(Value v)
|
||||
{
|
||||
Node *p = createNode();
|
||||
p.value = v;
|
||||
return insertNodeFirst(p);
|
||||
}
|
||||
|
||||
Iterator insertNodeFirst(Node *p)
|
||||
in
|
||||
{
|
||||
debug(slowcheck)
|
||||
assert(!hasIterator(p), "inserNodeFirst: Node is already in the list");
|
||||
}
|
||||
body
|
||||
{
|
||||
if(head)
|
||||
{
|
||||
// Insert node at the beginning of the list
|
||||
assert(tail != null);
|
||||
head.prev = p;
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is the first element to be inserted
|
||||
assert(tail == null);
|
||||
tail = p;
|
||||
}
|
||||
p.next = head;
|
||||
head = p;
|
||||
p.prev = null;
|
||||
|
||||
totalNum++;
|
||||
|
||||
return p;
|
||||
}
|
||||
Value* insertNodeFirst(Value *p)
|
||||
{ return &insertNodeFirst( cast(Node*)p ).value; }
|
||||
|
||||
// Insert after a given element
|
||||
Iterator insertAfter(Iterator i, Value v)
|
||||
{
|
||||
Node *p = createNode();
|
||||
p.value = v;
|
||||
return insertNodeAfter(i, p);
|
||||
}
|
||||
|
||||
// Insert p after i
|
||||
Iterator insertNodeAfter(Iterator i, Node *p)
|
||||
in
|
||||
{
|
||||
//debug(slowcheck)
|
||||
{
|
||||
assert(!hasIterator(p), "inserNodeAfter: Node is already in the list");
|
||||
assert(hasIterator(i), "insertNodeAfter(): element i not part of the list");
|
||||
}
|
||||
}
|
||||
body
|
||||
{
|
||||
// If i is the last element, then insertNodeLast already does a
|
||||
// stellar job of inserting
|
||||
if(i == tail)
|
||||
return insertNodeLast(p);
|
||||
|
||||
// Make p point to the right elements
|
||||
p.next = i.next;
|
||||
p.prev = i;
|
||||
|
||||
// Random consistency check
|
||||
assert(i == i.next.prev);
|
||||
|
||||
// Make the right elements point to p
|
||||
i.next = p;
|
||||
assert(p.next != null);
|
||||
p.next.prev = p;
|
||||
|
||||
totalNum++;
|
||||
|
||||
return p;
|
||||
}
|
||||
// Insert p after i
|
||||
Value* insertNodeAfter(Value* p, Value* i)
|
||||
{ return &insertNodeAfter( cast(Node*)p, cast(Node*)i ).value; }
|
||||
|
||||
|
||||
// Insert value v before i
|
||||
Iterator insertBefore(Iterator i, Value v)
|
||||
{
|
||||
Node *p = createNode();
|
||||
p.value = v;
|
||||
return insertNodeBefore(i, p);
|
||||
}
|
||||
|
||||
// Insert p before i
|
||||
Iterator insertNodeBefore(Iterator i, Node *p)
|
||||
in
|
||||
{
|
||||
//debug(slowcheck)
|
||||
{
|
||||
assert(!hasIterator(p), "inserNodeBefore: Node is already in the list");
|
||||
assert(hasIterator(i), "insertBefore(): element not part of the list");
|
||||
}
|
||||
}
|
||||
body
|
||||
{
|
||||
// If i is the first, just insert at the beginning
|
||||
if(i==head) return insertNodeFirst(p);
|
||||
|
||||
// If I mess it up, an assertion failure is easier to debug than
|
||||
// a segfault.
|
||||
assert(i.prev != null);
|
||||
|
||||
// Reuse insertAfter instead of reinventing the wheel
|
||||
return insertNodeAfter(i.prev, p);
|
||||
}
|
||||
// Insert p before i
|
||||
Value* insertNodeBefore(Value* p, Value* i)
|
||||
{ return &insertNodeBefore( cast(Node*)p, cast(Node*)i ).value; }
|
||||
|
||||
|
||||
// Swap position of element a and b
|
||||
void swap(Iterator a, Iterator b)
|
||||
in
|
||||
{
|
||||
//debug(slowcheck)
|
||||
assert(hasIterator(a) && hasIterator(b),
|
||||
"swap(a,b): both elements must be in the list");
|
||||
}
|
||||
body
|
||||
{
|
||||
Iterator tmp;
|
||||
|
||||
// Handle special cases first
|
||||
|
||||
// The same element? Do nothing.
|
||||
if(a==b) return;
|
||||
|
||||
// Are they next to each other?
|
||||
if(b.next == a)
|
||||
{
|
||||
// Swap it so we have a before b, then handle it below.
|
||||
assert(a.prev == b);
|
||||
tmp = a;
|
||||
a = b;
|
||||
b = tmp;
|
||||
}
|
||||
|
||||
// Point a.prev to b
|
||||
if(a.prev) a.prev.next = b;
|
||||
else
|
||||
{
|
||||
assert(head == a);
|
||||
head = b;
|
||||
}
|
||||
// Point to b.next a
|
||||
if(b.next) b.next.prev = a;
|
||||
else
|
||||
{
|
||||
assert(tail == b);
|
||||
tail = a;
|
||||
}
|
||||
|
||||
// From this point on, if a is next to b it must be handled as a
|
||||
// special case. We have already swapped them above so that a is
|
||||
// before b.
|
||||
if(a.next == b)
|
||||
{
|
||||
assert(b.prev == a);
|
||||
|
||||
// Assign outer pointers
|
||||
b.prev = a.prev;
|
||||
a.next = b.next;
|
||||
|
||||
// Assign inner pointers
|
||||
a.prev = b;
|
||||
b.next = a;
|
||||
return;
|
||||
}
|
||||
|
||||
// If a is NOT next to b, continue the pointer orgy.
|
||||
|
||||
// Point a.next to b
|
||||
if(a.next) a.next.prev = b;
|
||||
else
|
||||
{
|
||||
assert(tail == a);
|
||||
tail = b;
|
||||
}
|
||||
|
||||
if(b.prev) b.prev.next = a;
|
||||
else
|
||||
{
|
||||
assert(head == b);
|
||||
head = a;
|
||||
}
|
||||
|
||||
// Finally, swap a and b's internal pointers
|
||||
tmp = a.next;
|
||||
a.next = b.next;
|
||||
b.next = tmp;
|
||||
|
||||
tmp = a.prev;
|
||||
a.prev = b.prev;
|
||||
b.prev = tmp;
|
||||
}
|
||||
void swap(Value* a, Value* b)
|
||||
{ swap( cast(Node*)a, cast(Node*)b ); }
|
||||
|
||||
// Remove a node from the list and delete it
|
||||
void remove(Iterator p)
|
||||
{
|
||||
removeNode(p);
|
||||
deleteNode(p);
|
||||
}
|
||||
|
||||
// Just remove the node from the list, do not delete it.
|
||||
void removeNode(Iterator p)
|
||||
in
|
||||
{
|
||||
//debug(slowcheck)
|
||||
assert(hasIterator(p), "remove(): element not part of the list");
|
||||
}
|
||||
body
|
||||
{
|
||||
// Remove from the list
|
||||
if(p.next)
|
||||
{
|
||||
p.next.prev = p.prev;
|
||||
|
||||
// Make sure we are NOT tail
|
||||
assert(tail != p);
|
||||
}
|
||||
else // We're the tail
|
||||
{
|
||||
assert(tail == p);
|
||||
tail = p.prev;
|
||||
}
|
||||
|
||||
if(p.prev)
|
||||
{
|
||||
p.prev.next = p.next;
|
||||
|
||||
// We are NOT the head, since we have a previous element
|
||||
assert(head != p);
|
||||
}
|
||||
else // We're head
|
||||
{
|
||||
assert(head == p);
|
||||
head = p.next;
|
||||
}
|
||||
|
||||
totalNum--;
|
||||
}
|
||||
void removeNode(Value *v)
|
||||
{ removeNode( cast(Iterator)v ); }
|
||||
|
||||
// Free a node
|
||||
static private void deleteNode(Node *p)
|
||||
{
|
||||
// For the GC, just release the
|
||||
// pointer into the wild.
|
||||
static if(usefree) Alloc.free(p);
|
||||
}
|
||||
|
||||
// Create a new node and return it's pointer. TODO: Make this
|
||||
// static, and increase totalNum in the insert methods instead.
|
||||
static private Node* createNode()
|
||||
{
|
||||
static if(is(Alloc == GCAlloc))
|
||||
Node *p = new Node;
|
||||
else
|
||||
Node *p = cast(Node*)Alloc.alloc(Node.sizeof);
|
||||
|
||||
// Initialize next pointers
|
||||
static if(!autoinit)
|
||||
{
|
||||
p.next = null;
|
||||
p.prev = null;
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
// Loop through the nodes in the order they were inserted
|
||||
int opApply(int delegate(ref Value v) del)
|
||||
{
|
||||
Node *p = head;
|
||||
uint safeGuard = 0;
|
||||
while(p != null)
|
||||
{
|
||||
assert(safeGuard++ < totalNum);
|
||||
int i = del(p.value);
|
||||
if(i) return i;
|
||||
p = p.next;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Loop through the nodes in the order they were inserted
|
||||
int opApply(int delegate(ref int ind, ref Value v) del)
|
||||
{
|
||||
Node *p = head;
|
||||
int ind = 0;
|
||||
while(p != null)
|
||||
{
|
||||
assert(ind < totalNum);
|
||||
int i = del(ind, p.value);
|
||||
ind++;
|
||||
if(i) return i;
|
||||
p = p.next;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Number of elements
|
||||
uint length() { return totalNum; }
|
||||
|
||||
char[] toString()
|
||||
{
|
||||
char[] res = "[";
|
||||
foreach(int i, Value v; *this)
|
||||
{
|
||||
if(i < totalNum-1) res ~= format(" %s,", v);
|
||||
else res ~= format(" %s ]", v);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
/* This test is NOT very complete */
|
||||
unittest
|
||||
{
|
||||
LinkedList!(float) ll;
|
||||
|
||||
assert(ll.length == 0);
|
||||
ll.Iterator it = ll.insert(10.4);
|
||||
assert(ll.length == 1);
|
||||
ll.insert(23);
|
||||
it = ll.insert(6.3);
|
||||
ll.insert(-1000);
|
||||
|
||||
assert(ll.length == 4);
|
||||
|
||||
//foreach(float f; ll) writefln(f);
|
||||
|
||||
ll.remove(it);
|
||||
|
||||
assert(ll.length == 3);
|
||||
|
||||
ll.reset();
|
||||
|
||||
assert(ll.length == 0);
|
||||
|
||||
//foreach(int i, float f; ll) writefln(i, " ", f);
|
||||
}
|
||||
//import std.stdio;
|
||||
|
||||
// Array allocator. TODO: Put this and Malloc in their own place,
|
||||
// extend list to be the same quality as aa.d and make a system out of
|
||||
// it. Make some better unit tests.
|
||||
struct ArrAlloc
|
||||
{
|
||||
ubyte[] data;
|
||||
uint pos;
|
||||
|
||||
void reset() { pos = 0; }
|
||||
|
||||
const bool autoinit = false;
|
||||
const bool usefree = false;
|
||||
|
||||
void* alloc(uint size)
|
||||
{
|
||||
if(pos+size > data.length)
|
||||
data.length = pos+size+30;
|
||||
|
||||
void * ptr = &data[pos];
|
||||
|
||||
pos += size;
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void free(void* p) { }
|
||||
}
|
@ -0,0 +1,305 @@
|
||||
/*
|
||||
Monster - an advanced game scripting language
|
||||
Copyright (C) 2004, 2007, 2008 Nicolay Korslund
|
||||
Email: <korslund@gmail.com>
|
||||
WWW: http://monster.snaptoad.com/
|
||||
|
||||
This file (string.d) is part of the Monster script language
|
||||
package.
|
||||
|
||||
Monster is distributed as free software: you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License
|
||||
version 3, as published by the Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
version 3 along with this program. If not, see
|
||||
http://www.gnu.org/licenses/ .
|
||||
|
||||
*/
|
||||
|
||||
module monster.util.string;
|
||||
|
||||
import std.utf;
|
||||
import std.string;
|
||||
|
||||
bool begins(char[] str, char[] start)
|
||||
{
|
||||
if(str.length < start.length ||
|
||||
str[0..start.length] != start) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
unittest
|
||||
{
|
||||
assert("heia".begins(""));
|
||||
assert("heia".begins("h"));
|
||||
assert("heia".begins("he"));
|
||||
assert(!("heia".begins("H")));
|
||||
assert(!("heia".begins("hE")));
|
||||
assert("heia".begins("hei"));
|
||||
assert("heia".begins("heia"));
|
||||
assert(!("heia".begins("heia ")));
|
||||
assert(!("heia".begins(" heia")));
|
||||
assert(!("heia".begins("eia")));
|
||||
|
||||
assert(!("h".begins("ha")));
|
||||
assert(!("h".begins("ah")));
|
||||
assert(!("".begins("ah")));
|
||||
assert("".begins(""));
|
||||
}
|
||||
|
||||
bool ends(char[] str, char[] end)
|
||||
{
|
||||
if(str.length < end.length ||
|
||||
str[$-end.length..$] != end) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
unittest
|
||||
{
|
||||
assert("heia".ends(""));
|
||||
assert(!("heia".ends("h")));
|
||||
assert("heia".ends("a"));
|
||||
assert("heia".ends("ia"));
|
||||
assert(!("heia".ends("A")));
|
||||
assert(!("heia".ends("Ia")));
|
||||
assert("heia".ends("eia"));
|
||||
assert("heia".ends("heia"));
|
||||
assert(!("heia".ends("heia ")));
|
||||
assert(!("heia".ends(" heia")));
|
||||
assert(!("heia".ends("hei")));
|
||||
|
||||
assert(!("h".ends("ha")));
|
||||
assert(!("h".ends("ah")));
|
||||
assert(!("".ends("ah")));
|
||||
assert("".ends(""));
|
||||
}
|
||||
|
||||
// Case insensitive version of begins()
|
||||
bool iBegins(char[] str, char[] start)
|
||||
{
|
||||
if(str.length < start.length ||
|
||||
icmp(str[0..start.length], start) != 0) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
unittest
|
||||
{
|
||||
assert("heia".iBegins(""));
|
||||
assert("heia".iBegins("H"));
|
||||
assert("heia".iBegins("hE"));
|
||||
assert("heia".iBegins("hei"));
|
||||
assert("HeIa".iBegins("hei"));
|
||||
assert("heia".iBegins("heia"));
|
||||
assert("hEia".iBegins("heiA"));
|
||||
assert(!("heia".iBegins("heia ")));
|
||||
assert(!("heIa".iBegins("heia ")));
|
||||
assert(!("heia".iBegins("eia")));
|
||||
|
||||
assert(!("h".iBegins("ha")));
|
||||
assert(!("h".iBegins("ah")));
|
||||
assert(!("".iBegins("ah")));
|
||||
assert("".iBegins(""));
|
||||
}
|
||||
|
||||
// Case insensitive version of begins()
|
||||
bool iEnds(char[] str, char[] end)
|
||||
{
|
||||
if(str.length < end.length ||
|
||||
icmp(str[$-end.length..$], end) != 0) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
unittest
|
||||
{
|
||||
assert("heia".iEnds(""));
|
||||
assert(!("heia".iEnds("h")));
|
||||
assert("heia".iEnds("a"));
|
||||
assert("heia".iEnds("ia"));
|
||||
assert("heia".iEnds("A"));
|
||||
assert("heia".iEnds("Ia"));
|
||||
assert("heia".iEnds("EiA"));
|
||||
assert("he ia".iEnds("HE IA"));
|
||||
assert("heia".iEnds("eia"));
|
||||
assert("heia".iEnds("heia"));
|
||||
assert(!("heia".iEnds("heia ")));
|
||||
assert(!("heia".iEnds(" heia")));
|
||||
assert(!("heia".iEnds("hei")));
|
||||
|
||||
assert(!("h".iEnds("ha")));
|
||||
assert(!("h".iEnds("ah")));
|
||||
assert(!("".iEnds("ah")));
|
||||
assert("".iEnds(""));
|
||||
}
|
||||
|
||||
// Converts any string to valid UTF8 so it can be safely printed. It
|
||||
// does not translate from other encodings but simply replaces invalid
|
||||
// characters with 'replace'. Does everything in place.
|
||||
char[] makeUTF8(char[] str, char replace = '?')
|
||||
{
|
||||
size_t idx = 0;
|
||||
while(idx < str.length)
|
||||
{
|
||||
try decode(str, idx);
|
||||
catch(UtfException ue)
|
||||
str[idx++] = replace;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
char[] nextWord(ref char[] str, char delim = ' ')
|
||||
{
|
||||
int i = find(str, delim);
|
||||
char[] result;
|
||||
|
||||
// No 'delim' found, return the entire string and set remainder to
|
||||
// null.
|
||||
if(i == -1)
|
||||
{
|
||||
result = str;
|
||||
str = null;
|
||||
return result;
|
||||
}
|
||||
|
||||
// A separator was found. Return everything upto 'delim' (index i),
|
||||
// put the remainder of the string (not including the char at [i])
|
||||
// in str.
|
||||
result = str[0..i];
|
||||
str = str[i+1..$];
|
||||
return result;
|
||||
}
|
||||
|
||||
unittest
|
||||
{
|
||||
char[] test = "bjarne betjent er betent";
|
||||
assert(nextWord(test) == "bjarne");
|
||||
assert(test == "betjent er betent");
|
||||
assert(nextWord(test) == "betjent");
|
||||
assert(nextWord(test) == "er");
|
||||
assert(test == "betent");
|
||||
assert(nextWord(test) == "betent");
|
||||
assert(test == "");
|
||||
assert(nextWord(test) == "");
|
||||
|
||||
test = ";;foo;bar;";
|
||||
assert(nextWord(test,';') == "");
|
||||
assert(nextWord(test,';') == "");
|
||||
assert(nextWord(test,';') == "foo");
|
||||
assert(nextWord(test,';') == "bar");
|
||||
assert(nextWord(test,';') == "");
|
||||
assert(nextWord(test,';') == "");
|
||||
}
|
||||
|
||||
// An 'object oriented' interface to nextWord
|
||||
class NextWord
|
||||
{
|
||||
char delim;
|
||||
char[] str;
|
||||
|
||||
this(char[] str, char delim = ' ')
|
||||
{
|
||||
this.delim = delim;
|
||||
this.str = str;
|
||||
}
|
||||
|
||||
this(char delim = ' ')
|
||||
{ this.delim = delim; }
|
||||
|
||||
char[] next()
|
||||
{ return nextWord(str, delim); }
|
||||
}
|
||||
|
||||
unittest
|
||||
{
|
||||
auto n = new NextWord(";;foo;bar;",';');
|
||||
assert(n.next == "");
|
||||
assert(n.next == "");
|
||||
assert(n.next == "foo");
|
||||
assert(n.next == "bar");
|
||||
assert(n.next == "");
|
||||
assert(n.next == "");
|
||||
n.str = "a;bc";
|
||||
assert(n.next == "a");
|
||||
assert(n.next == "bc");
|
||||
}
|
||||
|
||||
// Strip trailing zeros
|
||||
char[] stripz(char [] s)
|
||||
{
|
||||
foreach(int i, char c; s)
|
||||
if( c == 0 )
|
||||
return s[0..i];
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
unittest
|
||||
{
|
||||
assert(stripz(" a b c ") == " a b c ");
|
||||
char[8] str;
|
||||
str[] = 0;
|
||||
assert(stripz(str) == "");
|
||||
str[2] = 'o';
|
||||
assert(stripz(str) == "");
|
||||
str[0] = 'f';
|
||||
str[3] = 'd';
|
||||
assert(stripz(str) == "f");
|
||||
str[1] = 'o';
|
||||
assert(stripz(str) == "food");
|
||||
}
|
||||
|
||||
// Convert a long integer into a string using nice comma
|
||||
// formatting. delim is the delimiter character, size is the number of
|
||||
// digits in each group. See the unittest for examples.
|
||||
char[] comma(long i, char delim=',', int size = 3)
|
||||
{
|
||||
char[] str = toString(i);
|
||||
char[] res;
|
||||
|
||||
if(i<0) str=str[1..$];
|
||||
|
||||
str.reverse;
|
||||
foreach(int j, char c; str)
|
||||
{
|
||||
if(j!=0 && j%size == 0)
|
||||
res = delim ~ res;
|
||||
res = c ~ res;
|
||||
}
|
||||
|
||||
if(i<0) res = "-" ~ res;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
unittest
|
||||
{
|
||||
//_________________
|
||||
// Inkas ___ \_
|
||||
// were here / \ \
|
||||
assert(comma(1) == "1");
|
||||
assert(comma(12) == "12");
|
||||
assert(comma(123) == "123");
|
||||
assert(comma(1234) == "1,234");
|
||||
assert(comma(12345) == "12,345");
|
||||
assert(comma(123456) == "123,456");
|
||||
assert(comma(1234567) == "1,234,567");
|
||||
assert(comma(12345678) == "12,345,678");
|
||||
|
||||
// Negative values
|
||||
assert(comma(-1) == "-1");
|
||||
assert(comma(-12) == "-12");
|
||||
assert(comma(-123) == "-123");
|
||||
assert(comma(-1234) == "-1,234");
|
||||
assert(comma(-12345) == "-12,345");
|
||||
|
||||
// Different delimiter
|
||||
assert(comma(-888888888888,'-') == "-888-888-888-888");
|
||||
|
||||
// Different size
|
||||
assert(comma(1111111111,'.',4) == "11.1111.1111");
|
||||
}
|
Loading…
Reference in New Issue