/* * Copyright 2015 Odnoklassniki Ltd, Mail.Ru Group * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package one.nio.serial; import one.nio.util.Utf8; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import static one.nio.util.JavaInternals.*; public class DataStream implements ObjectInput, ObjectOutput { protected static final byte REF_NULL = -1; protected static final byte REF_RECURSIVE = -2; protected static final byte REF_RECURSIVE2 = -3; protected static final byte REF_EMBEDDED = -4; protected byte[] array; protected long address; protected long limit; protected long offset; public DataStream(int capacity) { this(new byte[capacity], byteArrayOffset, capacity); } public DataStream(byte[] array) { this(array, byteArrayOffset, array.length); } public DataStream(long address, int capacity) { this(null, address, capacity); } protected DataStream(byte[] array, long address, long length) { this.array = array; this.address = address; this.limit = address + length; this.offset = address; } public byte[] array() { return array; } public long address() { return address; } public int count() { return (int) (offset - address); } public void write(int b) { long offset = alloc(1); unsafe.putByte(array, offset, (byte) b); } public void write(byte[] b) { long offset = alloc(b.length); unsafe.copyMemory(b, byteArrayOffset, array, offset, b.length); } public void write(byte[] b, int off, int len) { long offset = alloc(len); unsafe.copyMemory(b, byteArrayOffset + off, array, offset, len); } public void writeBoolean(boolean v) { long offset = alloc(1); unsafe.putBoolean(array, offset, v); } public void writeByte(int v) { long offset = alloc(1); unsafe.putByte(array, offset, (byte) v); } public void writeShort(int v) { long offset = alloc(2); unsafe.putShort(array, offset, Short.reverseBytes((short) v)); } public void writeChar(int v) { long offset = alloc(2); unsafe.putChar(array, offset, Character.reverseBytes((char) v)); } public void writeInt(int v) { long offset = alloc(4); unsafe.putInt(array, offset, Integer.reverseBytes(v)); } public void writeLong(long v) { long offset = alloc(8); unsafe.putLong(array, offset, Long.reverseBytes(v)); } public void writeFloat(float v) { writeInt(Float.floatToRawIntBits(v)); } public void writeDouble(double v) { writeLong(Double.doubleToRawLongBits(v)); } public void writeBytes(String s) { int length = s.length(); long offset = alloc(length); for (int i = 0; i < length; i++) { unsafe.putByte(array, offset++, (byte) s.charAt(i)); } } public void writeChars(String s) { int length = s.length(); long offset = alloc(length * 2); for (int i = 0; i < length; i++) { unsafe.putChar(array, offset, Character.reverseBytes(s.charAt(i))); offset += 2; } } public void writeUTF(String s) { int utfLength = Utf8.length(s); if (utfLength <= 0x7fff) { writeShort(utfLength); } else { writeInt(utfLength | 0x80000000); } long offset = alloc(utfLength); Utf8.write(s, array, offset); } @SuppressWarnings("unchecked") public void writeObject(Object obj) throws IOException { if (obj == null) { writeByte(REF_NULL); } else { Serializer serializer = Repository.get(obj.getClass()); if (serializer.uid < 0) { writeByte((byte) serializer.uid); } else { writeLong(serializer.uid); } serializer.write(obj, this); } } public void writeFrom(long address, int len) { long offset = alloc(len); unsafe.copyMemory(null, address, array, offset, len); } public int read() { return unsafe.getByte(array, alloc(1)); } public int read(byte[] b) { readFully(b); return b.length; } public int read(byte[] b, int off, int len) { readFully(b, off, len); return len; } public void readFully(byte[] b) { unsafe.copyMemory(array, alloc(b.length), b, byteArrayOffset, b.length); } public void readFully(byte[] b, int off, int len) { unsafe.copyMemory(array, alloc(len), b, byteArrayOffset + off, len); } public long skip(long n) throws IOException { alloc((int) n); return n; } public int skipBytes(int n) { alloc(n); return n; } public boolean readBoolean() { return unsafe.getBoolean(array, alloc(1)); } public byte readByte() { return unsafe.getByte(array, alloc(1)); } public int readUnsignedByte() { return unsafe.getByte(array, alloc(1)) & 0xff; } public short readShort() { return Short.reverseBytes(unsafe.getShort(array, alloc(2))); } public int readUnsignedShort() { return Short.reverseBytes(unsafe.getShort(array, alloc(2))) & 0xffff; } public char readChar() { return Character.reverseBytes(unsafe.getChar(array, alloc(2))); } public int readInt() { return Integer.reverseBytes(unsafe.getInt(array, alloc(4))); } public long readLong() { return Long.reverseBytes(unsafe.getLong(array, alloc(8))); } public float readFloat() { return Float.intBitsToFloat(readInt()); } public double readDouble() { return Double.longBitsToDouble(readLong()); } public String readLine() { throw new UnsupportedOperationException(); } public String readUTF() { int length = readUnsignedShort(); if (length == 0) { return ""; } if (length > 0x7fff) { length = (length & 0x7fff) << 16 | readUnsignedShort(); } return Utf8.read(array, alloc(length), length); } public Object readObject() throws IOException, ClassNotFoundException { Serializer serializer; byte b = readByte(); if (b >= 0) { offset--; serializer = Repository.requestSerializer(readLong()); } else if (b == REF_NULL) { return null; } else { serializer = Repository.requestBootstrapSerializer(b); } return serializer.read(this); } public void readTo(long address, int len) { unsafe.copyMemory(array, alloc(len), null, address, len); } public int available() { return (int) (limit - offset); } public void flush() { // Nothing to do } public void close() { // Nothing to do } public void register(Object obj) { // Nothing to do } protected long alloc(int size) { long currentOffset = offset; if ((offset = currentOffset + size) > limit) { throw new IndexOutOfBoundsException(); } return currentOffset; } }