/* * Copyright 2014 Ruediger Moeller. * * 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 org.nustaq.serialization.coders; import java.io.*; import org.nustaq.offheap.bytez.*; import org.nustaq.offheap.bytez.onheap.*; import org.nustaq.serialization.*; import org.nustaq.serialization.util.*; /** * Created by ruedi on 09.11.2014. * * uses unsafe to read values directly from memory instead an inputstream. no value compression is applied. * */ public class FSTBytezDecoder implements FSTDecoder { BasicBytez input; HeapBytez ascStringCache; FSTConfiguration conf; public FSTClazzNameRegistry clnames; long pos; InputStream inputStream; long readUntil = 0; public FSTBytezDecoder(FSTConfiguration conf, BasicBytez input) { this(conf); if ( FSTConfiguration.isAndroid ) throw new RuntimeException("not supported on android"); this.input = input; } @Override public void setConf(FSTConfiguration conf) { this.conf = conf; } public FSTBytezDecoder(FSTConfiguration conf) { this.conf = conf; clnames = (FSTClazzNameRegistry) conf.getCachedObject(FSTClazzNameRegistry.class); if (clnames == null) { clnames = new FSTClazzNameRegistry(conf.getClassRegistry()); } else { clnames.clear(); } } byte tmp[]; public int ensureReadAhead(int bytes) { if ( inputStream != null ) { if ( pos+bytes > readUntil ) { return readNextInputChunk(bytes); } } else if ( pos+bytes > input.length() ) { return -1; } return 0; } protected int readNextInputChunk(int bytes) { try { int toRead = Math.min(Integer.MAX_VALUE - 5, bytes); if ( inputStream instanceof ByteArrayInputStream ) { toRead = Math.min(((ByteArrayInputStream) inputStream).available(),toRead); } if ( tmp == null || tmp.length < toRead ) { tmp = new byte[toRead]; } int read = inputStream.read(tmp, 0, toRead); if ( read > 0 ) { if ( input.length() < pos+read ) { BasicBytez bytez = input.newInstance(2*(pos + read)); input.copyTo(bytez,0,0,pos); input = (HeapBytez) bytez; } input.set(pos,tmp,0,read); readUntil = pos+read; return read; } else if ( read == -1 ) return -1; // fixme: should loop in case read == 0 } catch (IOException e) { FSTUtil.<RuntimeException>rethrow(e); } return 0; } char chBufS[]; char[] getCharBuf(int siz) { char chars[] = chBufS; if (chars == null || chars.length < siz) { chars = new char[Math.max(siz, 15)]; chBufS = chars; } return chars; } public String readStringUTF() throws IOException { int len = readFInt(); char[] charBuf = getCharBuf(len * 2); ensureReadAhead(len*2); input.getCharArr(pos,charBuf,0,len); pos += len*2; return new String(charBuf, 0, len); } public byte readObjectHeaderTag() throws IOException { return readFByte(); } /** * len < 127 !!!!! * * @return * @throws java.io.IOException */ @Override public String readStringAsc() throws IOException { int len = readFInt(); if (ascStringCache == null || ascStringCache.length() < len) ascStringCache = new HeapBytez(new byte[len]); ensureReadAhead(len); // System.arraycopy(input.buf, input.pos, ascStringCache, 0, len); input.copyTo(ascStringCache, 0, pos, len); pos += len; return new String(ascStringCache.getBase(), 0, 0, len); } public BasicBytez getInput() { return input; } public void setInput(BasicBytez input) { this.input = input; } /** * assumes class header+len already read * * @param componentType * @param len * @return */ @Override public Object readFPrimitiveArray(Object array, Class componentType, int len) { // FIXME: if else chaining could be avoided if (componentType == byte.class) { ensureReadAhead(len); byte arr[] = (byte[]) array; input.getArr(pos, arr, 0, len); pos += len; return arr; } else if (componentType == char.class) { ensureReadAhead(len*2); char[] arr = (char[]) array; input.getCharArr(pos, arr, 0, len); pos += len * 2; return arr; } else if (componentType == short.class) { ensureReadAhead(len*2); short[] arr = (short[]) array; input.getShortArr(pos, arr, 0, len); pos += len * 2; return arr; } else if (componentType == int.class) { ensureReadAhead(len*4); int[] arr = (int[]) array; input.getIntArr(pos, arr, 0, len); pos += len * 4; return arr; } else if (componentType == float.class) { ensureReadAhead(len*4); float[] arr = (float[]) array; input.getFloatArr(pos, arr, 0, len); pos += len * 4; return arr; } else if (componentType == double.class) { ensureReadAhead(len*8); double[] arr = (double[]) array; input.getDoubleArr(pos, arr, 0, len); pos += len * 8; return arr; } else if (componentType == long.class) { ensureReadAhead(len*8); long[] arr = (long[]) array; input.getLongArr(pos, arr, 0, len); pos += len * 8; return arr; } else if (componentType == boolean.class) { ensureReadAhead(len); boolean[] arr = (boolean[]) array; input.getBooleanArr(pos, arr, 0, len); pos += len; return arr; } else { throw new RuntimeException("unexpected primitive type " + componentType.getName()); } } @Override public void readFIntArr(int len, int[] arr) throws IOException { input.getIntArr(pos, arr, 0, len); pos += len * 4; } @Override public int readFInt() throws IOException { return readPlainInt(); } @Override public double readFDouble() throws IOException { return Double.longBitsToDouble(readPlainLong()); } /** * Reads a 4 byte float. */ @Override public float readFFloat() throws IOException { return Float.intBitsToFloat(readPlainInt()); } @Override public final byte readFByte() throws IOException { ensureReadAhead(1); return input.get(pos++); } @Override public int readIntByte() throws IOException { final int res = ensureReadAhead(1); if ( res == -1 ) return -1; return input.get(pos++) & 0xff; } @Override public long readFLong() throws IOException { return readPlainLong(); } @Override public char readFChar() throws IOException { return readPlainChar(); } @Override public short readFShort() throws IOException { return readPlainShort(); } private char readPlainChar() throws IOException { ensureReadAhead(2); char res = input.getChar(pos); pos += 2; return res; } private short readPlainShort() throws IOException { ensureReadAhead(2); short res = input.getShort(pos); pos += 2; return res; } @Override public int readPlainInt() throws IOException { ensureReadAhead(4); int res = input.getInt(pos); pos += 4; return res; } private long readPlainLong() throws IOException { ensureReadAhead(8); long res = input.getLong(pos); pos += 8; return res; } @Override public byte[] getBuffer() { if ( input instanceof HeapBytez && ((HeapBytez) input).getOffsetIndex() == 0 ) { return ((HeapBytez) input).asByteArray(); } byte res[] = new byte[(int) pos]; input.getArr(0,res,0, (int) pos); return res; } @Override public int getInputPos() { return (int) pos; } @Override public void moveTo(int position) { pos = position; } @Override public void reset() { pos = 0; clnames.clear(); inputStream = null; } @Override public void setInputStream(InputStream in) { if ( in == FSTObjectInput.emptyStream ) { inputStream = null; readUntil = 0; return; } this.inputStream = in; clnames.clear(); pos = 0; if ( input == null ) input = new HeapBytez(new byte[4096]); readUntil = 0; } @Override public void resetToCopyOf(byte[] bytes, int off, int len) { inputStream = null; if ( input == null ) { byte[] base = new byte[len]; input = new HeapBytez(base,0,len); } if ( input.length() < len ) { input = (HeapBytez) input.newInstance(len); } input.set(0, bytes, off, len); pos = 0; clnames.clear(); } @Override public void resetWith(byte[] bytes, int len) { inputStream = null; if ( input == null ) { input = new HeapBytez(bytes,0,len); return; } // suboptimal method for non heap backing if ( input.getClass() == HeapBytez.class ) { ((HeapBytez)input).setBase(bytes,0,len); } else { BasicBytez newBytez = input.newInstance(len); newBytez.set(0,bytes,0,len); } pos = 0; clnames.clear(); } @Override public FSTClazzInfo readClass() throws IOException, ClassNotFoundException { return clnames.decodeClass(this,conf); } @Override public Class classForName(String name) throws ClassNotFoundException { return clnames.classForName(name,conf); } @Override public void registerClass(Class possible) { clnames.registerClass(possible,conf); } @Override public void close() { conf.returnObject(clnames); } @Override public void skip(int n) { pos += n; } @Override public void readPlainBytes(byte[] b, int off, int len) { ensureReadAhead(len); // System.arraycopy(input.buf,input.pos,b,off,len); input.getArr(pos, b, off, len); pos += len; } @Override public boolean isMapBased() { return false; } @Override public Object getDirectObject() // in case class already resolves to read object (e.g. mix input) { return null; } @Override public int getObjectHeaderLen() { return -1; } @Override public void consumeEndMarker() { } @Override public Class readArrayHeader() throws Exception { return readClass().getClazz(); } @Override public void readExternalEnd() { // do nothing for direct encoding } @Override public boolean isEndMarker(String s) { return false; } @Override public int readVersionTag() throws IOException { return readFByte(); } @Override public void pushBack(int bytes) { pos -= bytes; } @Override public void readArrayEnd(FSTClazzInfo clzSerInfo) {} @Override public void readObjectEnd() {} @Override public Object coerceElement(Class arrType, Object value) { return value; } @Override public int available() { return (int) (input.length()-pos); } @Override public boolean inArray() { return false; } @Override public void startFieldReading(Object newObj) { } }