/******************************************************************************* * Copyright (c) 2010, 2016 Association for Decentralized Information Management * in Industry THTH ry. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package org.simantics.databoard.util.binary; import java.io.DataInputStream; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; /** * Rancom access memory blob * * @author Toni Kalajainen */ public class BinaryMemory implements RandomAccessBinary { ByteBuffer buf; long pointer; /** the number of bytes added spare to the buffer when it is incremented */ int increment; /** * New memory blob */ public BinaryMemory() { buf = ByteBuffer.allocate(16); increment = 16; } /** * New memory blob */ public BinaryMemory(byte[] data) { this.buf = ByteBuffer.wrap(data); } /** * New memory blob */ public BinaryMemory(byte[] data, int offset, int length) { this.buf = ByteBuffer.wrap(data, offset, length); } /** * New memory blob */ public BinaryMemory(int initialSize) { buf = ByteBuffer.allocate(initialSize); increment = Math.max(16, initialSize); } /** * New memory blob */ public BinaryMemory(int initialSize, int increment) { buf = ByteBuffer.allocate(initialSize); this.increment = increment; } /** * Assume an existing byte buffer * * @param buf buffer */ public BinaryMemory(ByteBuffer buf) { this.buf = buf; } /** * Get the backend byte buffer. The result may change if BinaryMemory is * written. * * @return byte buffer */ public ByteBuffer toByteBuffer() { return buf; } @Override public void close() throws IOException { } @Override public boolean isOpen() { return true; } @Override public void flush() throws IOException { } @Override public void reset() throws IOException { } @Override public byte readByte() { assertHasReadableBytes(1); buf.position( (int) pointer ); byte result = buf.get(); pointer += 1; return result; } int _read() { if (pointer >= buf.limit()) return -1; buf.position( (int) pointer ); byte result = buf.get(); pointer += 1; return result & 0xff; } public final String readLine() throws IOException { StringBuffer input = new StringBuffer(); int c = -1; boolean eol = false; while (!eol) { switch (c = _read()) { case -1: case '\n': eol = true; break; case '\r': eol = true; long cur = position(); if ((_read()) != '\n') { position(cur); } break; default: input.append((char)c); break; } } if ((c == -1) && (input.length() == 0)) { return null; } return input.toString(); } public final String readUTF() throws IOException { return DataInputStream.readUTF(this); } @Override public int readUnsignedByte() throws IOException { return readByte() & 0x000000ff; } @Override public boolean readBoolean() throws IOException { assertHasReadableBytes(1); buf.position( (int) pointer ); byte result = buf.get(); pointer += 1; return result!=0; } @Override public void readFully(byte[] dst, int offset, int length) { assertHasReadableBytes(length); buf.position( (int) pointer ); buf.get(dst, offset, length); pointer += length; } @Override public void readFully(byte[] dst) { assertHasReadableBytes(dst.length); buf.position( (int) pointer ); buf.get(dst); pointer += dst.length; } @Override public void readFully(ByteBuffer buf) { int bytes = buf.remaining(); assertHasReadableBytes( bytes ); buf.position( (int) pointer ); if (buf.hasArray()) { this.buf.get(buf.array(), buf.arrayOffset() + buf.position(), bytes); buf.position(buf.capacity()); } else { buf.put(buf); } pointer += bytes; } @Override public void readFully(ByteBuffer buf, int length) { assertHasReadableBytes(length); buf.position( (int) pointer ); if (buf.hasArray()) { this.buf.get(buf.array(), buf.arrayOffset() + buf.position(), length); buf.position(buf.position() + length); } else { // int len = Math.min( Math.min( buf.remaining(), this.buf.remaining() ), length); int len = length; int origLimit = this.buf.limit(); try { this.buf.limit(this.buf.position()+len); buf.put(this.buf); } finally { this.buf.limit(origLimit); } } pointer += length; } @Override public double readDouble() { assertHasReadableBytes(8); buf.position( (int) pointer ); double value = buf.getDouble(); pointer += 8; return value; } @Override public float readFloat() { assertHasReadableBytes(4); buf.position( (int) pointer ); float result = buf.getFloat(); pointer += 4; return result; } @Override public int readInt() { assertHasReadableBytes(4); buf.position( (int) pointer ); int value = buf.getInt(); pointer += 4; return value; } @Override public long readLong() { assertHasReadableBytes(8); buf.position( (int) pointer ); long value = buf.getLong(); pointer += 8; return value; } @Override public short readShort() { assertHasReadableBytes(2); buf.position( (int) pointer ); short result = buf.getShort(); pointer += 2; return result; } @Override public char readChar() { assertHasReadableBytes(2); buf.position( (int) pointer ); char result = buf.getChar(); pointer += 2; return result; } @Override public int readUnsignedShort() { assertHasReadableBytes(2); buf.position( (int) pointer ); int result = buf.getShort() & 0xffff; pointer += 2; return result; } @Override public long length() { return buf.limit(); } @Override public long position() { return pointer; } @Override public void position(long newPosition) throws IOException { this.pointer = newPosition; } @Override public void write(int b) throws IOException { assertHasWritableBytes(1); buf.position( (int) pointer ); buf.put((byte) b); pointer += 1; } @Override public void writeByte(int b) throws IOException { assertHasWritableBytes(1); buf.position( (int) pointer ); buf.put((byte) b); pointer += 1; } @Override public void writeBoolean(boolean v) throws IOException { assertHasWritableBytes(1); buf.position( (int) pointer ); buf.put( (byte) (v ? 1 : 0) ); pointer += 1; } @Override public void writeFully(ByteBuffer src) throws IOException { long bytes = src.remaining(); assertHasWritableBytes( bytes ); buf.position( (int) pointer ); buf.put(src); pointer += bytes; } @Override public void writeFully(ByteBuffer src, int length) throws IOException { assertHasWritableBytes(length); buf.position( (int) pointer ); if (src.hasArray()) { byte[] array = src.array(); buf.put(array, src.arrayOffset() + src.position(), length); } else { for (int i=0; i0) { int n = is.available(); assertHasWritableBytes(n); byte buf[] = this.buf.array(); n = is.read(buf, (int) pointer, n); pointer += n; } long newLen = length(); if (newLen>oldLen & pointer>> 8) & 0xFF) ); buf.put( (byte) ((v >>> 0) & 0xFF) ); } pointer += len*2; } @Override public void writeUTF(String s) throws IOException { int len = UTF8.getModifiedUTF8EncodingByteLength(s); writeShort(len); UTF8.writeModifiedUTF(this, s); } void assertHasReadableBytes(long count) { if (pointer + count > buf.limit()) throw new IndexOutOfBoundsException(); } void assertHasWritableBytes(long count) throws IOException { if (pointer + count > buf.limit()) setLength(pointer+count); } @Override public void insertBytes(long bytes, ByteSide side) throws IOException { if (pointer<0) throw new IndexOutOfBoundsException(); // 1. Increase length long oldLength = length(); long newLength = Math.max(oldLength + bytes, pointer + bytes); // No need to allocate if (newLengtholdLength) return; // Move bytes if (buf.hasArray()) { System.arraycopy(buf.array(), (int) pointer, buf.array(), (int) (pointer+bytes), (int) (oldLength - pointer)); } else { byte[] b = new byte[(int) (oldLength - pointer)]; buf.position((int)pointer); buf.get(b); buf.position( (int) (pointer + bytes) ); buf.put(b); } } else // Need to reallocate { long inc = Math.max(increment, newLength / 4); ByteBuffer oldBuf = buf; ByteBuffer newBuf = ByteBuffer.allocate((int) (newLength + inc)); newBuf.limit((int)newLength); newBuf.position(0); oldBuf.position(0); // Copy left side oldBuf.get(newBuf.array(), 0, (int)pointer); // Copy right side if (pointer=length()) throw new IndexOutOfBoundsException(); // move some bytes if (buf.hasArray()) { System.arraycopy(buf.array(), (int)(pointer+bytes), buf.array(), (int) pointer, (int) (newLength-pointer) ); } else { byte[] b = new byte[(int) (oldLength-pointer)]; buf.position((int) (pointer+bytes)); buf.get(b); buf.position((int) (pointer)); buf.put(b); } buf.limit( (int) newLength ); } @Override public void setLength(long newLength) throws IOException { int oldLength = (int) length(); if (oldLength==newLength) return; if (newLength<=oldLength) { buf.limit((int)newLength); } else if (newLength<=buf.capacity()) { buf.limit((int)newLength); } else { ByteBuffer oldBuf = buf; long inc = Math.max(increment, newLength / 4); ByteBuffer newBuf = ByteBuffer.allocate((int) (newLength + inc)); oldBuf.position(0); oldBuf.get(newBuf.array(), 0, oldLength); newBuf.limit((int)newLength); buf = newBuf; } } @Override public long skipBytes(long bytes) throws IOException { pointer += bytes; return bytes; } @Override public int skipBytes(int bytes) throws IOException { pointer += bytes; return bytes; } @Override public String toString() { return "Mem("+length()+")"; } }