view src/impl/hoofbaby/util/memory.d @ 0:3425707ddbf6

Initial import (hopefully this mercurial stuff works...)
author fraserofthenight
date Mon, 06 Jul 2009 08:06:28 -0700
parents
children
line wrap: on
line source

/**
 * Hoofbaby -- http://www.dsource.org/projects/hoofbaby
 * Copyright (C) 2009 Robert Fraser
 * 
 * This program is free software; you can redistribute it andor
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * 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.
 */

module hoofbaby.util.memory;

import tango.core.Memory : GC;
import candy.util.array;

/**
 * Provides a pool of GCed memory to allocate things from a block.
 * This maintains cache coherency for related types (i.e. tree nodes).
 * It doesn't garuntee any ordering, though, the array struct should be
 * used for that. Also, everything has to be freed at once, freeing one
 * portion of this has no effect.
 * 
 * Based on a similar concept posted by bearophile at: 
 * http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=88227
 */
public struct MemPool(size_t BLOCK_SIZE = 1 << 14)
{
    private void* next; // Next available block
    private void* end;  // End of the current block
    private void*[] blocks;
    
    public void* alloc(size_t sz)
    {
    	sz = ((sz + 7) & ~7); // Next multiple of 8 if this isn't a multiple of 8
    	if (this.next + sz >= this.end)
    	{
    		void* blk = GC.calloc(BLOCK_SIZE);
    		this.blocks.length = this.blocks.length + 1;
    		this.blocks[$ - 1] = blk;
    		this.next = blk;
    		this.end = blk + BLOCK_SIZE;
        }
        
        void* ret = this.next;
        this.next += sz;
        return ret;
    }
    
    public void free()
    {
    	foreach(blk; this.blocks)
    		GC.free(blk);
    	this.blocks = null;
    	this.blocks.length = 0;
    	this.next = null;
    	this.end = null;
    }
}

/**
 * Wrapper for MemPool that allocates the given struct
 */
public struct StructPool(T)
{
	private MemPool!() pool;
	public T* alloc()  { return cast(T*) pool.alloc(T.sizeof); }
}

public struct MemStack(size_t BLOCK_SIZE = 1 << 14)
{
	private Stack!(MemPool!(BLOCK_SIZE)*, 16, true, true) stack;
	public static const size_t MAX_ALLOC = BLOCK_SIZE;
	
	public void* alloc(size_t sz) { return stack.peek().alloc(sz);          }
	public void  push()           { stack.push(new MemPool!(BLOCK_SIZE));   }
	public void  pop()            { stack.pop().free();                     }
}

/**
 * Placement new mixin for allocating from a memory pool. Benchmarks show this
 * as faster than the D new in real usage (i.e. the parser runs about 1.2x
 * faster using this).
 */
public template MemPoolNew(alias Pool)
{
	version(NoMemPool) { } else
	{
		public final new(uint sz)    { return Pool.alloc(sz); }
		public final delete(void *p) {                        }
	}
}