/* * Copyright (c) 2003-onwards Shaven Puppy Ltd * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of 'Shaven Puppy' nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package com.shavenpuppy.jglib; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.HashMap; import java.util.Map; /** * Memory is a crude memory allocator that attempts to manage memory allocation. * Somewhat crudely memory is allocated in chunks to the nearest power of two; * the smallest allocation is 256 bytes. */ public class Memory extends Resource { private static final long serialVersionUID = 1L; private static final boolean DEBUG = false; /** * A class to keep track of allocations in an allocator */ private static class AllocationHandler { /** The source buffer */ final ByteBuffer allocator; /** Entries */ final Entry entry; /** * Entries in the lists */ class Entry { int position; int length; boolean allocated; Entry next, prev; /** * Merge this free entry with the next entry. All contiguous entries * are merged. */ void merge() { assert allocated; allocated = false; // Now merge all contiguous blocks together: // 1. Scan back to first allocated block Entry current = this; while (current.prev != null && !current.prev.allocated) { current = current.prev; } // 2. Now scan forward to first allocated block while (current.next != null && !current.next.allocated) { current.length += current.next.length; current.next = current.next.next; } if (current.next != null) { current.next.prev = current; } } /** * Split this entry into an allocated part and unallocated part. */ void split(int size) { assert !allocated; assert size <= length; allocated = true; if (size == length) { // Allocated the whole thing return; } else { Entry newEntry = new Entry(); newEntry.prev = this; newEntry.next = next; newEntry.position = position + size; newEntry.length = length - size; length = size; next = newEntry; } } /** * Create us a buffer out of this tree branch */ ByteBuffer createBuffer() { ByteBuffer dup = allocator.duplicate(); dup.clear().limit(position + length); dup.position(position); return dup.slice().order(ByteOrder.nativeOrder()); // For fuck's sake, why doesn't it take the byteorder from the original buffer??? } } /** * Constructor */ AllocationHandler(ByteBuffer allocator) { this.allocator = allocator; // Create a single freelist entry entry = new Entry(); entry.length = allocator.capacity(); } /** * Allocate some memory */ Entry allocate(int size) { // Simple and inefficient algorithm: just scan through the linked list until we find an Entry // big enough Entry e = entry; while (e != null) { if (e.allocated || e.length < size) { e = e.next; } else { e.split(size); return e; } } return null; } } /** A map of names to AllocationHandlers */ private static final Map<String, AllocationHandler> allocatorMap = new HashMap<String, AllocationHandler>(); /** The source buffer used */ private String allocatorName; /** The length of the memory we want to allocate */ private int length; /** The branch of the allocation tree in which this memory is allocated */ private AllocationHandler.Entry entry; /** And a nice safe buffer we can use to access the memory */ private ByteBuffer buffer; /** * Constructor for Memory. * * @param allocator The name of the ByteBuffer in which you wish to allocate memory * @param length The number of bytes you wish to allocate */ public Memory(String allocatorName, int length) { super("Memory allocation ("+length+" bytes)"); this.allocatorName = allocatorName; this.length = length; } /** * Initialize with an allocator * @param name The name of the allocator * @param buf The memory itself */ public static synchronized void init(String name, ByteBuffer allocator) { AllocationHandler ah = new AllocationHandler(allocator); allocatorMap.put(name, ah); } /* (non-Javadoc) * @see com.powersolve.opengl.ALResource#doCreate() */ @Override protected final synchronized void doCreate() { // Find a tree for the allocator; if none exists then create one. AllocationHandler handler = allocatorMap.get(allocatorName); // Minimum length is 32 bytes length = Math.max(32, length); entry = handler.allocate(length); if (entry == null) { throw new RuntimeException("Couldn't allocate "+length+" bytes from "+allocatorName); } buffer = entry.createBuffer(); //System.out.println("Allocated memory "+Sys.getDirectBufferAddress(buffer)+" Pos:"+entry.position+"->"+(entry.position+entry.length)+" to "+Sys.getDirectBufferAddress(allocator)+"/"+allocator.hashCode()); // Thread.dumpStack(); } /* (non-Javadoc) * @see com.powersolve.opengl.ALResource#doDestroy() */ @Override protected void doDestroy() { // System.out.println("Deallocated mem/ory @ "+entry.position+"->"+(entry.position+entry.length)+" to "+allocator.hashCode()); entry.merge(); entry = null; buffer = null; } /** * Returns the buffer */ public final ByteBuffer getBuffer() { assert isCreated(); return buffer; } /** * Cleanup. All memory from a particular allocator is removed. */ public static synchronized void cleanup(String name) { allocatorMap.remove(name); } /** * Slice and dice memory. * @param src The source ByteBuffer * @param byteOffset The offset, in bytes, from which to take a sub-buffer * @param length The length of the sub-buffer to chop out * @return the new ByteBuffer */ public static ByteBuffer chop(ByteBuffer src, int byteOffset, int length) { ByteBuffer ret = src.duplicate(); ret.clear().position(byteOffset).limit(byteOffset + length); ret = ret.slice(); ret.order(ByteOrder.nativeOrder()); return ret; } /** * Slice and dice memory. * @param src The source ByteBuffer * @param byteOffset The offset, in bytes, from which to take a sub-buffer * @return the new ByteBuffer */ public static ByteBuffer chop(ByteBuffer src, int byteOffset) { ByteBuffer ret = src.duplicate(); ret.clear().position(byteOffset); ret = ret.slice(); ret.order(ByteOrder.nativeOrder()); return ret; } }