--- /dev/null
+/*******************************************************************************\r
+ * Copyright (c) 2010, 2016 Association for Decentralized Information Management\r
+ * in Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ * VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.databoard.util.binary;
+
+import java.io.DataInputStream;\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.nio.ByteBuffer;\r
+
+/**
+ * Rancom access memory blob
+ *
+ * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
+ */
+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;
+ }
+\r
+ /**\r
+ * New memory blob\r
+ */\r
+ public BinaryMemory(byte[] data) {\r
+ this.buf = ByteBuffer.wrap(data);\r
+ }\r
+ \r
+ /**\r
+ * New memory blob\r
+ */\r
+ public BinaryMemory(byte[] data, int offset, int length) {\r
+ this.buf = ByteBuffer.wrap(data, offset, length);\r
+ }\r
+
+ /**
+ * 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 {
+ }
+\r
+ @Override\r
+ public boolean isOpen() {\r
+ return true;\r
+ }\r
+
+ @Override
+ public void flush() throws IOException {
+ }\r
+ \r
+ @Override\r
+ public void reset() throws IOException {\r
+ }
+
+ @Override
+ public byte readByte() {
+ assertHasReadableBytes(1);
+ buf.position( (int) pointer );
+ byte result = buf.get();
+ pointer += 1;
+ return result;
+ }\r
+ \r
+ int _read() {\r
+ if (pointer >= buf.limit()) return -1;\r
+ buf.position( (int) pointer );\r
+ byte result = buf.get();\r
+ pointer += 1;\r
+ return result & 0xff;\r
+ } \r
+ \r
+ public final String readLine() throws IOException {\r
+ StringBuffer input = new StringBuffer();\r
+ int c = -1;\r
+ boolean eol = false;\r
+\r
+ while (!eol) {\r
+ switch (c = _read()) {\r
+ case -1:\r
+ case '\n':\r
+ eol = true;\r
+ break;\r
+ case '\r':\r
+ eol = true;\r
+ long cur = position();\r
+ if ((_read()) != '\n') {\r
+ position(cur);\r
+ }\r
+ break;\r
+ default:\r
+ input.append((char)c);\r
+ break;\r
+ }\r
+ }\r
+\r
+ if ((c == -1) && (input.length() == 0)) {\r
+ return null;\r
+ }\r
+ return input.toString();\r
+ } \r
+ \r
+ public final String readUTF() throws IOException {\r
+ return DataInputStream.readUTF(this);\r
+ } \r
+ \r
+ @Override\r
+ public int readUnsignedByte() throws IOException {\r
+ return readByte() & 0x000000ff;\r
+ }
+\r
+ @Override\r
+ public boolean readBoolean() throws IOException {\r
+ assertHasReadableBytes(1); \r
+ buf.position( (int) pointer );\r
+ byte result = buf.get();\r
+ pointer += 1;\r
+ return result!=0;\r
+ }\r
+
+ @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;
+ }\r
+ \r
+ @Override\r
+ public char readChar() {\r
+ assertHasReadableBytes(2);\r
+ buf.position( (int) pointer );\r
+ char result = buf.getChar();\r
+ pointer += 2;\r
+ return result;\r
+ } \r
+ \r
+ @Override\r
+ public int readUnsignedShort() {\r
+ assertHasReadableBytes(2);\r
+ buf.position( (int) pointer );\r
+ int result = buf.getShort() & 0xffff;\r
+ pointer += 2;\r
+ return result;\r
+ }
+
+ @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;
+ }\r
+ \r
+ @Override\r
+ public void writeByte(int b) throws IOException {\r
+ assertHasWritableBytes(1);\r
+ buf.position( (int) pointer );\r
+ buf.put((byte) b);\r
+ pointer += 1;\r
+ } \r
+ \r
+ @Override\r
+ public void writeBoolean(boolean v) throws IOException {\r
+ assertHasWritableBytes(1);\r
+ buf.position( (int) pointer );\r
+ buf.put( (byte) (v ? 1 : 0) );\r
+ pointer += 1;\r
+ }
+
+ @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; i<length; i++)
+ buf.put(src.get());
+ }
+ pointer += length;
+ }
+
+
+ public void put(InputStream is) throws IOException {
+ long oldLen = length();
+ while (is.available()>0) {
+ 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<newLen) setLength(pointer);
+ }
+
+ @Override
+ public void write(byte[] src, int offset, int length) throws IOException {
+ assertHasWritableBytes(length);
+ buf.position( (int) pointer );
+ buf.put(src, offset, length);
+ pointer += length;
+ }
+
+ @Override
+ public void write(byte[] src) throws IOException {
+ assertHasWritableBytes(src.length);
+ buf.position( (int) pointer );
+ buf.put(src);
+ pointer += src.length;
+ }
+
+ @Override
+ public void writeDouble(double value) throws IOException {
+ assertHasWritableBytes(8);
+ buf.position( (int) pointer );
+ buf.putDouble(value);
+ pointer += 8;
+ }
+
+ @Override
+ public void writeFloat(float value) throws IOException {
+ assertHasWritableBytes(4);
+ buf.position( (int) pointer );
+ buf.putFloat(value);
+ pointer += 4;
+ }
+
+ @Override
+ public void writeInt(int value) throws IOException {
+ assertHasWritableBytes(4);
+ buf.position( (int) pointer );
+ buf.putInt(value);
+ pointer += 4;
+ }
+
+ @Override
+ public void writeLong(long value) throws IOException {
+ assertHasWritableBytes(8);
+ buf.position( (int) pointer );
+ buf.putLong(value);
+ pointer += 8;
+ }
+
+ @Override
+ public void writeShort(int value) throws IOException {
+ assertHasWritableBytes(2);
+ buf.position( (int) pointer );
+ buf.putShort( (short) value);
+ pointer += 2;
+ }\r
+ \r
+ @Override\r
+ public void writeChar(int value) throws IOException {\r
+ assertHasWritableBytes(2);\r
+ buf.position( (int) pointer );\r
+ buf.putShort( (short) value);\r
+ pointer += 2;\r
+ } \r
+ \r
+ @Override\r
+ public void writeBytes(String s) throws IOException {\r
+ int len = s.length();\r
+ assertHasWritableBytes(len);\r
+ buf.position( (int) pointer );\r
+ for (int i = 0 ; i < len ; i++) {\r
+ buf.put((byte)s.charAt(i));\r
+ }\r
+ pointer += len;\r
+ } \r
+ \r
+ @Override\r
+ public void writeChars(String s) throws IOException {\r
+ int len = s.length();\r
+ assertHasWritableBytes(len*2);\r
+ for (int i = 0 ; i < len ; i++) {\r
+ int v = s.charAt(i);\r
+ buf.put( (byte) ((v >>> 8) & 0xFF) ); \r
+ buf.put( (byte) ((v >>> 0) & 0xFF) ); \r
+ }\r
+ pointer += len*2;\r
+ }\r
+ \r
+ @Override\r
+ public void writeUTF(String s) throws IOException {\r
+ int len = UTF8.getModifiedUTF8EncodingByteLength(s);\r
+ writeShort(len);\r
+ UTF8.writeModifiedUTF(this, s);\r
+ }
+
+ 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 (newLength<buf.capacity()) {
+ // Update limit
+ buf.limit((int)newLength);
+
+ // No need to move bytes
+ if (pointer>oldLength) 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<oldLength)
+ oldBuf.get(newBuf.array(), (int)(pointer+bytes), (int)(oldLength-pointer));
+
+ buf = newBuf;
+ }
+
+ }
+
+ @Override
+ public void removeBytes(long bytes, ByteSide side) throws IOException {
+ long oldLength = length();
+ long newLength = oldLength - bytes;
+ if (pointer<0 || 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;\r
+ return bytes;
+ }
+\r
+ @Override\r
+ public int skipBytes(int bytes) throws IOException {\r
+ pointer += bytes;\r
+ return bytes;\r
+ }\r
+
+ @Override
+ public String toString() {
+ return "Mem("+length()+")";
+ }
+
+}
+