mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-15 21:49:56 +00:00
2995f8b99f
- various gui-work - begun binding the GUI into Monster script - updated to latest Monster source (0.11 alpha) git-svn-id: https://openmw.svn.sourceforge.net/svnroot/openmw/trunk@87 ea6a568a-9f4f-0410-981a-c910a81bb256
401 lines
10 KiB
D
401 lines
10 KiB
D
/*
|
|
Monster - an advanced game scripting language
|
|
Copyright (C) 2007-2009 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;
|
|
|
|
import monster.util.list;
|
|
import monster.util.growarray;
|
|
import std.c.stdlib : malloc, free;
|
|
|
|
// A freelist for buffers of a given size. The 'size' template
|
|
// parameter gives the total requested size of the entire struct,
|
|
// including overhead.
|
|
struct BufferList(int size)
|
|
{
|
|
// Calculate the 'overhead' size of the structs
|
|
alias void* vp;
|
|
static const junk = 2*vp.sizeof + int.sizeof;
|
|
|
|
static union BuffData(int size)
|
|
{
|
|
static assert(size >= 2, "size must be at least 2");
|
|
|
|
int[size/int.sizeof] ints;
|
|
ubyte[size] bytes;
|
|
}
|
|
|
|
// Get the data sizes
|
|
static const bytes = size - junk;
|
|
static const ints = bytes / int.sizeof;
|
|
|
|
static assert(bytes > 0, "size is too small");
|
|
|
|
alias BuffData!(bytes) Value;
|
|
alias Value* ValuePtr;
|
|
static assert(Value.sizeof == bytes);
|
|
|
|
alias LinkedList!(Value, NoAlloc) List;
|
|
alias List.Node LNode;
|
|
|
|
static struct BufferNode(int size)
|
|
{
|
|
LNode data;
|
|
int index;
|
|
}
|
|
|
|
alias BufferNode!(bytes) Node;
|
|
alias Node* NodePtr;
|
|
static assert(Node.sizeof == size);
|
|
|
|
private:
|
|
|
|
// This is the array that does all the actual allocations. It is
|
|
// used for quickly looking up indices. GrowArrays are designed to
|
|
// grow dynamically without reallocation, while still being easily
|
|
// indexed like a normal array.
|
|
static GrowArray!(Node) array;
|
|
|
|
// The freelist. This is shared between all template instances of
|
|
// the same size.
|
|
static List freeList;
|
|
|
|
// The nodes belonging to THIS list instance
|
|
List nodes;
|
|
|
|
// Get a new node (move from freelist to node list)
|
|
ValuePtr getNew()
|
|
{
|
|
// Is the freelist empty?
|
|
if(freeList.length == 0)
|
|
{
|
|
// Create a bunch of nodes and shove them into the freelist.
|
|
const makeSize = 50;
|
|
|
|
// 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++)
|
|
{
|
|
NodePtr 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.
|
|
return &node.value;
|
|
}
|
|
|
|
// Move a node back to the freelist ("delete" it)
|
|
void remove(ValuePtr node)
|
|
{
|
|
nodes.removeNode(node);
|
|
freeList.insertNodeFirst(node);
|
|
}
|
|
|
|
public:
|
|
|
|
// Get the node corresponding to an index
|
|
static ValuePtr getNode(int index)
|
|
{
|
|
return &array.getPtr(index).data.value;
|
|
}
|
|
|
|
// Get the index from a node
|
|
static int getIndex(ValuePtr node)
|
|
{
|
|
return ( cast(Node*)node ).index;
|
|
}
|
|
|
|
uint length() { return nodes.length; }
|
|
static uint totLength() { return array.length; }
|
|
|
|
// Move the given node to another list
|
|
ValuePtr moveTo(ref BufferList fl, ValuePtr node)
|
|
{
|
|
nodes.removeNode(node);
|
|
fl.nodes.insertNodeFirst(node);
|
|
return node;
|
|
}
|
|
|
|
// Get the first element in the list
|
|
ValuePtr getHead() { return &nodes.getHead().value; }
|
|
|
|
// Loop through the structs in this list
|
|
int opApply(int delegate(ref Value) dg)
|
|
{
|
|
return nodes.opApply(dg);
|
|
}
|
|
|
|
int[] getInt(int isize)
|
|
{
|
|
assert(isize <= ints);
|
|
|
|
return getNew().ints[0..isize];
|
|
}
|
|
|
|
void freeInt(int[] buf)
|
|
{
|
|
assert(buf.length <= ints);
|
|
remove(cast(ValuePtr)buf.ptr);
|
|
}
|
|
|
|
void* get() { return getNew(); }
|
|
void free(void* p) { remove(cast(ValuePtr)p); }
|
|
}
|
|
|
|
struct Buffers
|
|
{
|
|
static:
|
|
BufferList!(64) b64;
|
|
BufferList!(128) b128;
|
|
BufferList!(256) b256;
|
|
BufferList!(768) b768;
|
|
|
|
/*
|
|
static this()
|
|
{
|
|
writefln("64: ints=%s bytes=%s", b64.ints, b64.bytes);
|
|
writefln("128: ints=%s bytes=%s", b128.ints, b128.bytes);
|
|
writefln("256: ints=%s bytes=%s", b256.ints, b256.bytes);
|
|
writefln("768: ints=%s bytes=%s", b768.ints, b768.bytes);
|
|
}
|
|
*/
|
|
|
|
int[] getInt(uint size)
|
|
{
|
|
if(size <= b64.ints) return b64.getInt(size);
|
|
else if(size <= b128.ints) return b128.getInt(size);
|
|
else if(size <= b256.ints) return b256.getInt(size);
|
|
else if(size <= b768.ints) return b768.getInt(size);
|
|
// Too large for our lists - just use malloc
|
|
else
|
|
{
|
|
//writefln("WARNING: using malloc for %s ints (%s bytes)", size, size*int.sizeof);
|
|
return ( cast(int*)malloc(size*int.sizeof) )[0..size];
|
|
}
|
|
}
|
|
|
|
void free(int[] buf)
|
|
{
|
|
uint size = buf.length;
|
|
if(size <= b64.ints) b64.freeInt(buf);
|
|
else if(size <= b128.ints) b128.freeInt(buf);
|
|
else if(size <= b256.ints) b256.freeInt(buf);
|
|
else if(size <= b768.ints) b768.freeInt(buf);
|
|
else .free(buf.ptr);
|
|
}
|
|
}
|
|
|
|
/* THIS DOESN'T WORK - because DMD is still stubborn with those
|
|
template forwarding issues. Instead we'll just reuse the old
|
|
freelist implementation below.
|
|
|
|
// A list that uses a freelist for allocation. It is built on top of
|
|
// BufferList.
|
|
struct FreeList(T)
|
|
{
|
|
private:
|
|
// For small sizes, pool together with existing lists.
|
|
static if(T.sizeof <= 64) static const size = 64;
|
|
else static if(T.sizeof <= 128) static const size = 128;
|
|
else static if(T.sizeof <= 256) static const size = 256;
|
|
// Just use the actual size, rounded up to the nearest 16
|
|
else static if(T.sizeof % 16 == 0)
|
|
const size = T.sizeof;
|
|
else
|
|
const size = T.sizeof + 16 - (T.sizeof%16);
|
|
|
|
alias BufferList!(size) BuffList;
|
|
BuffList buffer;
|
|
|
|
alias BuffList.Value Value;
|
|
alias BuffList.ValuePtr ValuePtr;
|
|
|
|
static assert(T.sizeof <= BuffList.bytes);
|
|
|
|
public:
|
|
|
|
// Get a new node (move from freelist to node list)
|
|
T* getNew()
|
|
{ return cast(T*) buffer.get(); }
|
|
|
|
// Move a node back to the freelist ("delete" it)
|
|
void remove(T* node)
|
|
{ buffer.free(node); }
|
|
|
|
// Get the node corresponding to an index
|
|
static T* getNode(int index)
|
|
{ return cast(T*) buffer.getNode(index); }
|
|
|
|
// Get the index from a node
|
|
static int getIndex(T *node)
|
|
{ return buffer.getIndex(cast(ValuePtr)node); }
|
|
|
|
uint length() { return buffer.length(); }
|
|
static uint totLength() { return buffer.totLength(); }
|
|
|
|
// Move the given node to another list
|
|
T* moveTo(ref FreeList fl, T* node)
|
|
{
|
|
auto vp = cast(ValuePtr) node;
|
|
return cast(T*) buffer.moveTo(fl.buffer, vp);
|
|
}
|
|
|
|
// Get the first element in the list
|
|
T* getHead() { return cast(T*) buffer.getHead(); }
|
|
|
|
// Loop through the structs in this list
|
|
int opApply(int delegate(ref T) dg)
|
|
{
|
|
auto dgc = cast(int delegate(ref Value)) dg;
|
|
return nodes.opApply(dgc);
|
|
}
|
|
}
|
|
*/
|
|
|
|
// 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:
|
|
|
|
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; }
|
|
|
|
// Get the next element in the list
|
|
T* getNext(T* nd)
|
|
{
|
|
auto node = cast(TNode*)nd;
|
|
return cast(T*) node.getNext();
|
|
}
|
|
|
|
// Loop through the structs in this list
|
|
int opApply(int delegate(ref T) dg)
|
|
{ return nodes.opApply(dg); }
|
|
}
|