/* * Copyright (C) 2013 Omry Yadan <omry@yadan.net> * All rights reserved. * * See https://github.com/omry/banana/blob/master/BSD-LICENSE for licensing information */ package net.yadan.banana.stack; import net.yadan.banana.DebugLevel; import net.yadan.banana.DefaultFormatter; import net.yadan.banana.Formatter; import net.yadan.banana.ICollection; import net.yadan.banana.memory.IBuffer; import net.yadan.banana.memory.IMemAllocator; import net.yadan.banana.memory.OffsetPrimitiveAccess; import net.yadan.banana.memory.malloc.ChainedAllocator; /** * A stack backed by a single int[] array. Stack supports variable size frames * * @author omry * @created Apr 22, 2013 */ public class Stack implements ICollection { private final static int NEXT_OFFSET = 0; private final static int DATA_OFFSET = 1; private IMemAllocator m_memory; private int m_head; private int m_size; private DebugLevel m_debugLevel = DebugLevel.NONE; private Formatter m_formatter; public Stack(int numElements, int recordSize) { if (recordSize <= 0) throw new IllegalArgumentException("Non positive record size " + recordSize); m_memory = new ChainedAllocator(numElements, recordSize + DATA_OFFSET); m_formatter = new DefaultFormatter(); m_head = -1; m_size = 0; } public void push(int size) { int n = m_memory.malloc(size + DATA_OFFSET); m_memory.setInt(n, NEXT_OFFSET, m_head); m_head = n; m_size++; } public void push(IBuffer data) { push(data.size()); m_memory.setInts(m_head, DATA_OFFSET, data.array(), 0, data.size()); } public void pop() { ensure_not_empty(); int next = m_memory.getInt(m_head, NEXT_OFFSET); m_memory.free(m_head); m_head = next; m_size--; } public void setInt(int data, int offset_in_data) { ensure_not_empty(); m_memory.setInt(m_head, DATA_OFFSET + offset_in_data, data); } public void setLong(long data, int offset_in_data) { ensure_not_empty(); m_memory.setLong(m_head, DATA_OFFSET + offset_in_data, data); } public void set_ints(int src_data[], int src_pos, int length, int dst_offset_in_record) { ensure_not_empty(); m_memory.setInts(m_head, DATA_OFFSET + dst_offset_in_record, src_data, src_pos, length); } public int getInt(int offset_in_data) { ensure_not_empty(); return m_memory.getInt(m_head, DATA_OFFSET + offset_in_data); } public long getLong(int offset_in_data) { ensure_not_empty(); return m_memory.getLong(m_head, DATA_OFFSET + offset_in_data); } public void getInts(int src_offset_in_record, int dst_data[], int dst_pos, int length) { ensure_not_empty(); m_memory.getInts(m_head, DATA_OFFSET + src_offset_in_record, dst_data, dst_pos, length); } @Override public boolean isEmpty() { return m_head == -1; } public int availableBlocks() { return m_memory.freeBlocks(); } public int usedBlocks() { return m_memory.usedBlocks(); } public int maxBlocks() { return m_memory.maxBlocks(); } public int blockSize() { return m_memory.blockSize() - DATA_OFFSET; } private void ensure_not_empty() { if (isEmpty()) { throw new IllegalStateException("Stack empty"); } } @Override public void clear() { while (!isEmpty()) { pop(); } } @Override public int size() { return m_size; } @Override public long computeMemoryUsage() { return m_memory.computeMemoryUsage(); } @Override public void setDebug(DebugLevel level) { m_debugLevel = level; } @Override public DebugLevel getDebug() { return m_debugLevel; } @Override public void setFormatter(Formatter formatter) { m_formatter = formatter; } @Override public Formatter getFormatter() { return m_formatter; } @Override public String toString() { try { OffsetPrimitiveAccess prim = new OffsetPrimitiveAccess(m_memory, DATA_OFFSET); StringBuilder sb = new StringBuilder("Stack (" + size() + " records)"); switch (m_debugLevel) { case DEBUG_CONTENT: { sb.append("\n"); int n = m_head; while (n != -1) { int next = m_memory.getInt(n, NEXT_OFFSET); String st; try { st = m_formatter.format(prim, n); } catch (RuntimeException e) { st = "\t" + e.getClass().getSimpleName() + " : " + e.getMessage(); } sb.append("\t").append(st); if (next != -1) { sb.append("\n"); } n = next; } } break; case DEBUG_STRUCTURE: { sb.append("\n"); int n = m_head; while (n != -1) { int next = m_memory.getInt(n, NEXT_OFFSET); sb.append("#").append(n).append(" : "); String st; try { st = "N #" + next + " : " + m_formatter.format(prim, n); } catch (RuntimeException e) { st = e.getClass().getSimpleName() + " : " + e.getMessage(); } sb.append("\t").append(st); if (next != -1) { sb.append("\n"); } n = next; } } break; case NONE: break; } return sb.toString(); } catch (RuntimeException e) { return "Exception in toString() : " + e.getClass().getSimpleName() + " : " + e.getMessage(); } } }