Moved monster files into the openmw repos

git-svn-id: https://openmw.svn.sourceforge.net/svnroot/openmw/trunk@12 ea6a568a-9f4f-0410-981a-c910a81bb256
actorid
nkorslund 17 years ago
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…
Cancel
Save