mirror of
				https://github.com/OpenMW/openmw.git
				synced 2025-10-31 14:56:38 +00:00 
			
		
		
		
	Moved monster files into the openmw repos
git-svn-id: https://openmw.svn.sourceforge.net/svnroot/openmw/trunk@12 ea6a568a-9f4f-0410-981a-c910a81bb256
This commit is contained in:
		
							parent
							
								
									16e6da7d69
								
							
						
					
					
						commit
						888c552f1b
					
				
					 5 changed files with 2405 additions and 0 deletions
				
			
		
							
								
								
									
										1016
									
								
								monster/util/aa.d
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1016
									
								
								monster/util/aa.d
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										147
									
								
								monster/util/freelist.d
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										147
									
								
								monster/util/freelist.d
									
									
									
									
									
										Normal file
									
								
							|  | @ -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); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										297
									
								
								monster/util/growarray.d
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										297
									
								
								monster/util/growarray.d
									
									
									
									
									
										Normal file
									
								
							|  | @ -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); | ||||
| } | ||||
							
								
								
									
										640
									
								
								monster/util/list.d
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										640
									
								
								monster/util/list.d
									
									
									
									
									
										Normal file
									
								
							|  | @ -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) { } | ||||
| } | ||||
							
								
								
									
										305
									
								
								monster/util/string.d
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										305
									
								
								monster/util/string.d
									
									
									
									
									
										Normal file
									
								
							|  | @ -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 a new issue