All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.javanut.pronghorn.pipe.DataInputBlobReader Maven / Gradle / Ivy

Go to download

Ring buffer based queuing utility for applications that require high performance and/or a small footprint. Well suited for embedded and stream based processing.

There is a newer version: 1.1.27
Show newest version
package com.javanut.pronghorn.pipe;

import java.io.DataInput;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInputStream;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.javanut.pronghorn.pipe.util.IntArrayPool;
import com.javanut.pronghorn.pipe.util.IntArrayPoolLocal;
import com.javanut.pronghorn.struct.StructRegistry;
import com.javanut.pronghorn.util.Appendables;
import com.javanut.pronghorn.util.TrieParser;
import com.javanut.pronghorn.util.TrieParserReader;
import com.javanut.pronghorn.util.math.Decimal;
import com.javanut.pronghorn.util.math.DecimalResult;

public class DataInputBlobReader> extends ChannelReader {

    private final StringBuilder workspace;
    private final Pipe pipe;
    private byte[] backing;
    private final int byteMask;
    private static final Logger logger = LoggerFactory.getLogger(DataInputBlobReader.class);
    
    protected int length;
    private int bytesHighBound;
    protected int bytesLowBound;
    protected int position;
    protected boolean isStructured;
    
    private final StructuredReader structuredReader;
    
    /////////////////////////
    //package protected DimArray methods
    /////////////////////////    
    public static int reserveDimArray(DataInputBlobReader reader, int size, int maxSize) {   	   	
    	return IntArrayPool.lockInstance(IntArrayPoolLocal.get(), size);  	
    }
    public static int[] lookupDimArray(DataInputBlobReader reader, int size, int instance) {
      	return IntArrayPool.getArray(IntArrayPoolLocal.get(), size, instance);  	
    }
    public static void releaseDimArray(DataInputBlobReader reader, int size, int instance) {
    	IntArrayPool.releaseLock(IntArrayPoolLocal.get(), size, instance);
    }
    ////////////////////////////////
    ////////////////////////////////
    
    
    private static TrieParser textToNumberParser;
    protected TrieParserReader reader;
    
    private long mostRecentPacked=-1; //used for asserts;
    
    private int EOF_MARKER = -1;
    
    public DataInputBlobReader(Pipe pipe) {
    	super();
        this.pipe = pipe;
        this.backing = Pipe.blob(pipe);
        this.byteMask = Pipe.blobMask(pipe); 
        this.workspace = new StringBuilder(64);
        assert(this.backing!=null) : "The pipe must be init before use.";
        if (this.backing==null) {
        	throw new UnsupportedOperationException("Not Init...");
        }
        
        structuredReader = new StructuredReader(this);
    }
        
	public void debug() {
	    
    	Appendables.appendArray(Appendables.appendValue(System.out,  "read at ", bytesLowBound), '[', backing, bytesLowBound, byteMask, ']',  length);

	}
	
	public void debugUTF8() {
	    Appendables.appendUTF8(System.out, backing, bytesLowBound, length, byteMask);
		
	}
	
    public int openHighLevelAPIField(int loc) {
        
    	this.isStructured = PipeReader.isStructured(pipe, loc);    	
        this.length         = Math.max(0, PipeReader.readBytesLength(pipe, loc));
        this.bytesLowBound  = this.position       = PipeReader.readBytesPosition(pipe, loc);
        this.backing        = PipeReader.readBytesBackingArray(pipe, loc); 
        assert(this.backing!=null) : "The pipe must be init before use.";
        if (null==backing) {
        	throw new UnsupportedOperationException("bad loc value of: "+loc);
        }
        this.bytesHighBound = pipe.blobMask & (position + length);
        
        assert(Pipe.validatePipeBlobHasDataToRead(pipe, position, length));
        
        return this.length;
    }
    
    public static int peekHighLevelAPIField(DataInputBlobReader reader, int loc) {
        
    	reader.isStructured   = 0!=(Pipe.STRUCTURED_POS_MASK&PipeReader.peekDataMeta(reader.pipe, loc));
    	reader.length         = PipeReader.peekDataLength(reader.pipe, loc);
    	reader.bytesLowBound  = reader.position = PipeReader.peekDataPosition(reader.pipe, loc);
    	reader.backing        = PipeReader.peekDataBackingArray(reader.pipe, loc); 
    	assert(reader.backing!=null) : "The pipe must be init before use.";
    	 if (null==reader.backing) {
         	throw new UnsupportedOperationException("bad loc value of: "+loc);
         }
    	reader.bytesHighBound = reader.pipe.blobMask & (reader.position + reader.length);
     
        return reader.length;
    }
    
    
    public int readFromEndLastInt(int negativeIntOffset) {
    	assert(readFromLastInt(this, negativeIntOffset)<=this.length) :
    		  "index position is out of bounds in pipe "+this.getBackingPipe(this).id
    		 +" at idx "+negativeIntOffset
    		 +" bad pos of "+readFromLastInt(this, negativeIntOffset)
    		 +" full length "+this.length;
    	
    	
    	return readFromLastInt(this, negativeIntOffset);
    }

    
    public static boolean structTypeValidation(DataInputBlobReader reader, int structId) {
    	if (getStructType(reader)!=structId) {
    		throw new UnsupportedOperationException("Type mismatch");
    	}
    	
    	return true;
    }
    
    public boolean isStructured() {
    	return isStructured(this);
    }
       
    
    public static boolean isStructured(DataInputBlobReader reader) {
    	return reader.isStructured;
    }
    
    //not as useful as you might think
	public static int getStructType(DataInputBlobReader reader) {
		//must return -1 when structures are not used, in that case we may have 
		//valid data beyond the body of this payload. so we can not fetch the value.
		return (!reader.isStructured) 
				? -1 
			    : StructRegistry.IS_STRUCT_BIT | bigEndianInt((reader.bytesLowBound + Pipe.blobIndexBasePosition(reader.pipe)), reader.byteMask, reader.backing);
		
	}
	
	private static int bigEndianInt(int position, int mask, byte[] back) {
		return ( ( back[mask & position++]) << 24) |
				 ( (0xFF & back[mask & position++]) << 16) |
				 ( (0xFF & back[mask & position++]) << 8) |
				   (0xFF & back[mask & position]);
	}
    
	public static int readFromLastInt(DataInputBlobReader reader, int pos) {
		assert(pos>=0) : "there is no data found at the end";
    	//logger.info("readFromEndLastInt pos {} ",position);    	
    	return bigEndianInt((reader.bytesLowBound + Pipe.blobIndexBasePosition(reader.pipe))-(4*(pos+1)), reader.byteMask, reader.backing);
	}
        
	public boolean readFromEndInto(DataOutputBlobWriter outputStream) {
		assert(isStructured) : "method can only be called on structured readers";
		//WARNING: this method will carry the same exact struct forward to the destination
		
		final int type = getStructType(this);
		//only copy back data if it is found.
		if (type!=-1) {
			//warning this must copy all the way to the very end with maxVarLen
			final int end = (bytesLowBound + pipe.maxVarLen);
			
			final int copyLen = ((Pipe.structRegistry(getBackingPipe(this)).totalSizeOfIndexes(type))*4)+4;//plus the type
			int start = end-copyLen;
			
			DataOutputBlobWriter.copyBackData(outputStream, backing, start, copyLen, byteMask);
			return true;
		}
		return false;
	}
	
    
    public int openLowLevelAPIField() {
        int meta = Pipe.takeByteArrayMetaData(this.pipe);
        
        this.isStructured = (0!=(Pipe.STRUCTURED_POS_MASK&meta));
                
		this.length    = Math.max(0, Pipe.takeByteArrayLength(this.pipe));
		assert(this.length<=this.pipe.sizeOfBlobRing) : "bad length "+this.length;
		
		this.bytesLowBound = this.position = Pipe.bytePosition(meta, this.pipe, this.length);
		this.backing   = Pipe.byteBackingArray(meta, this.pipe); 
		assert(this.backing!=null) : 
			"The pipe "+(1==(meta>>31)?" constant array ": " blob ")+"must be defined before use.\n "+this.pipe;
			
		if (null==this.backing) {
	        	throw new UnsupportedOperationException("bad loc value of: "+meta);
	    }
		 
		this.bytesHighBound = this.pipe.blobMask & (this.position + this.length);
		
		assert(Pipe.validatePipeBlobHasDataToRead(this.pipe, this.position, this.length));
		
		return this.length;
    }
    
    public int peekLowLevelAPIField(int offset) {
    	int meta = Pipe.peekInt(this.pipe, offset);     
    	
    	this.isStructured = (0!=(Pipe.STRUCTURED_POS_MASK&meta));
    	
		this.length    = Math.max(0, Pipe.peekInt(this.pipe, offset+1));
		this.bytesLowBound = this.position = Pipe.convertToPosition(meta, this.pipe);
		this.backing   = Pipe.byteBackingArray(meta, this.pipe);
		 if (null==this.backing) {
	        	throw new UnsupportedOperationException("bad loc value of: "+meta);
	        }
		 
		assert(this.backing!=null) : 
			"The pipe "+(1==(meta>>31)?" constant array ": " blob ")+"must be defined before use.\n "+this.pipe;
			
		this.bytesHighBound = this.pipe.blobMask & (this.position + this.length);
		
		assert(Pipe.validatePipeBlobHasDataToRead(this.pipe, this.position, this.length));
		
		return this.length;
    }
    
    
    @Deprecated
    public static > int openLowLevelAPIField(DataInputBlobReader that) {
        return that.openLowLevelAPIField();
    }

    public int accumLowLevelAPIField() {
    	return accumLowLevelAPIField(this);
    }
    
    public static int accumLowLevelAPIField(DataInputBlobReader that) {
        if (0==that.length) {
            int meta = Pipe.takeByteArrayMetaData(that.pipe);
			int localLen = Pipe.takeByteArrayLength(that.pipe);
			
			that.length    = Math.max(0, localLen);
			that.bytesLowBound = that.position = Pipe.bytePosition(meta, that.pipe, that.length);
			that.backing   = Pipe.byteBackingArray(meta, that.pipe); 
			assert(that.backing!=null) : 
				"The pipe "+(1==(meta>>31)?" constant array ": " blob ")+"must be defined before use.\n "+that.pipe;
				
			 if (null==that.backing) {
		        	throw new UnsupportedOperationException("bad loc value of: "+meta);
		        }
			that.bytesHighBound = that.pipe.blobMask & (that.position + that.length);
			
			assert(Pipe.validatePipeBlobHasDataToRead(that.pipe, that.position, that.length));
			
			return localLen;
        } else {        
        
            Pipe.takeByteArrayMetaData(that.pipe);
            int len = Pipe.takeByteArrayLength(that.pipe);
            if (len>0) {//may be -1 for null values
            	that.length += len;
            	that.bytesHighBound = that.pipe.blobMask & (that.bytesHighBound + len);
            }
            return len;
        }
    }
    
    
    public int accumHighLevelAPIField(int loc) {
        if (0>=this.length) {
        	
            int localLen = PipeReader.readBytesLength(pipe, loc);
			this.length         = Math.max(0, localLen);
			this.bytesLowBound  = this.position       = PipeReader.readBytesPosition(pipe, loc);
			this.backing        = PipeReader.readBytesBackingArray(pipe, loc); 
			 if (null==this.backing) {
		        	throw new UnsupportedOperationException("bad loc value of: "+loc);
		        }
			assert(this.backing!=null) : "The pipe must be init before use.";
			this.bytesHighBound = pipe.blobMask & (position + length);
			
			assert(Pipe.validatePipeBlobHasDataToRead(pipe, position, length));
			
			return localLen;
        } else {        
        
        	int len = PipeReader.readBytesLength(pipe, loc);
        	if (len>0) {            
        		this.length += len;
            	this.bytesHighBound = pipe.blobMask & (bytesHighBound + len);
        	}
            return len;
        }        
    }
    
    @Override    
    public boolean hasRemainingBytes() {
        return (byteMask & position) != bytesHighBound;
    }

    @Override
    public int available() {        
        return bytesRemaining(this);
    }

    public static int bytesRemaining(DataInputBlobReader that) {
    	return that.length - that.position();
    }

    public DataInput nullable() {
        return length<0 ? null : this;
    }
   
    @Override
    public int absolutePosition() {
    	return absolutePosition(this);
    }
    
    @Override
    public void absolutePosition(int position) {
    	absolutePosition(this, position);
    }
    
    public static int absolutePosition(DataInputBlobReader reader) {
    	return reader.position;
    }
    
    public static void absolutePosition(DataInputBlobReader reader, int position) {
    	reader.position = position;
    }
    
    public static void position(DataInputBlobReader reader, int byteIndexFromStart) {
    	assert(byteIndexFromStart>=0);
    	//can read up to the end so this position is the same as length yet it is excluded.
    	assert(byteIndexFromStart<=reader.length) : "index of "+byteIndexFromStart+" is out of limit "+reader.length;
    	//logger.trace("set to position from start "+byteIndexFromStart);
    	reader.position = reader.bytesLowBound+byteIndexFromStart;
    }

    @Override
    public int position() {
    	return position - bytesLowBound;
    }    
    
	@Override
	public void position(int position) {
		position(this, position);
	}
    
    @Deprecated
    public void setPositionBytesFromStart(int byteIndexFromStart) {
        position(this, byteIndexFromStart);
    }

    
    
    
//    @Override
//	public synchronized void mark(int readlimit) {
//		super.mark(readlimit);
//	}
//
//	@Override
//	public synchronized void reset() throws IOException {
//		super.reset();
//	}
//
//	@Override
//	public boolean markSupported() {
//		return super.markSupported();
//	}

	@Override
    public int read(byte[] b) {
        if ((byteMask & position) == bytesHighBound) {
            return EOF_MARKER;
        }       
                
        int max = bytesRemaining(this);
        int len = b.length > max? max : b.length;      

        Pipe.copyBytesFromToRing(backing, position, byteMask, b, 0, Integer.MAX_VALUE, len);
        position += len;
        return len;
    }
    
    @Override
    public int read(byte[] b, int off, int len) {
        if ((byteMask & position) == bytesHighBound) {
            return EOF_MARKER;
        }
        
        int max = bytesRemaining(this);
        if (len > max) {
            len = max;
        }
        Pipe.copyBytesFromToRing(backing, position, byteMask, b, off, Integer.MAX_VALUE, len);
        position += len;
        return len;
    }
    
    @Override
    public void readFully(byte[] b) {
                
        Pipe.copyBytesFromToRing(backing, position, byteMask, b, 0, Integer.MAX_VALUE, b.length);
        position += b.length;
       
    }

    @Override
    public void readFully(byte[] b, int off, int len) {
        
        Pipe.copyBytesFromToRing(backing, position, byteMask, b, off, Integer.MAX_VALUE, len);
        position += len;
        
    }

    @Override
    public long skip(long n) {
    	long count = Math.min(n, bytesRemaining(this));
    	assert(count+position < ((long)Integer.MAX_VALUE));
        position += count; 
        return count;
    }
    
    @Override
    public int skipBytes(int n) {
    	
    	int count = Math.min(n, bytesRemaining(this));
        position += count;
        return count;
    }

    @Override
    public boolean readBoolean() {
        return backing[byteMask & position++]>0; //positive is true, zero is false, neg is null
    }
    
    @Override
    public boolean wasBooleanNull() {
    	return backing[byteMask & (position-1)]<0;
    }

    @Override
    public byte readByte() {
        return backing[byteMask & position++];
    }

    @Override
    public int readUnsignedByte() {
        return 0xFF & backing[byteMask & position++];
    }
    
    private static > short read16(byte[] buf, int mask, DataInputBlobReader that) {
        return (short)((       buf[mask & that.position++] << 8) |
                       (0xFF & buf[mask & that.position++])); 
    }    
    
    private static > int read32(byte[] buf, int mask, DataInputBlobReader that) {        
        return ( ( (       buf[mask & that.position++]) << 24) |
                 ( (0xFF & buf[mask & that.position++]) << 16) |
                 ( (0xFF & buf[mask & that.position++]) << 8) |
                   (0xFF & buf[mask & that.position++]) ); 
    }
    
    private static > long read64(byte[] buf, int mask, DataInputBlobReader that) {        
        return ( ( (  (long)buf[mask & that.position++]) << 56) |              
                 ( (0xFFl & buf[mask & that.position++]) << 48) |
                 ( (0xFFl & buf[mask & that.position++]) << 40) |
                 ( (0xFFl & buf[mask & that.position++]) << 32) |
                 ( (0xFFl & buf[mask & that.position++]) << 24) |
                 ( (0xFFl & buf[mask & that.position++]) << 16) |
                 ( (0xFFl & buf[mask & that.position++]) << 8) |
                   (0xFFl & buf[mask & that.position++]) ); 
    }

    @Override
    public short readShort() {
        return read16(backing,byteMask,this);
    }

    @Override
    public int readUnsignedShort() {
        return 0xFFFF & read16(backing,byteMask,this);
    }

    @Override
    public char readChar() {
       return (char)read16(backing,byteMask,this);
    }

    @Override
    public int readInt() {
        return read32(backing,byteMask,this);
    }

    @Override
    public long readLong() {
        return read64(backing,byteMask,this);
    }

    @Override
    public float readFloat() {        
        return Float.intBitsToFloat(read32(backing,byteMask,this));
    }

    @Override
    public double readDouble() {
        return Double.longBitsToDouble(read64(backing,byteMask,this));
    }

    @Override
    public int read() {
        return (byteMask & position) != bytesHighBound ? backing[byteMask & position++] : EOF_MARKER;//isOpen?0:-1;
    }

    @Override
    public String readLine() {
        
        workspace.setLength(0);        
        if ((byteMask & position) != bytesHighBound) {
            char c = (char)read16(backing,byteMask,this);
            while (
                    ((byteMask & position) != bytesHighBound) &&  //hard stop for EOF but this is really end of field.
                    c != '\n'
                  ) {
                if (c!='\r') {
                    workspace.append(c);            
                    c = (char)read16(backing,byteMask,this);
                }
            }
        }
        return new String(workspace);
    }
    
    @Override
    public String readUTF() { //use this two line implementation
        int length = readShort(); //read first 2 byte for length in bytes to convert.
        return length>=0?readUTFOfLength(length):null;
    }
    
    @Override
    public  A readUTF(A target) {
        int length = readShort(); //read first 2 byte for length in bytes to convert.    
        return length>=0?readUTFOfLength(length, target):target;
    }

    @Override
    public String readUTFFully() {
        workspace.setLength(0);
        try {
        	return readUTF(this, available(), workspace).toString();
        } catch (Exception e) {
        	throw new RuntimeException(e);
        }
    }
    
    @Override
    public String readUTFOfLength(int length) {
    	if (length >= 0) {
	        workspace.setLength(0);
	        
	        try {
	        	return readUTF(this, length, workspace).toString();
	        } catch (Exception e) {
	        	throw new RuntimeException(e);
	        }
    	} else {
    		return null;
    	}
    }
    
    @Override
    public  A readUTFOfLength(int length, A target) {      
        try {
        	return readUTF(this, length, target);
        } catch (Exception e) {
        	throw new RuntimeException(e);
        }
    }
    
    @Override
    public long parse(TrieParserReader reader, TrieParser trie, int length) {
		long result = reader.query(reader, trie, backing, position, length, byteMask);
		
		if (result!=-1) {			
			position = reader.sourcePos;
		}
		
		return result;
	}
    
    @Override
	public boolean equalUTF(byte[] equalText) {
		int len = readShort();
		if (len!=equalText.length) {
			return false;
		}
		int p = 0;
		int pp = position;
		while (--len>=0) {
			if (equalText[p++]!=backing[byteMask & pp++]) {
				return false;
			}
		}
		//only moves forward when equal.
		position = pp;
		return true;
	}
	
    @Override
	public boolean equalBytes(byte[] bytes) {
		return equalBytes(bytes, 0, bytes.length);
	}
	
    @Override
	public boolean equalBytes(byte[] bytes, int bytesPos, int bytesLen) {
		int len = available();
		if (len=0) {
			if (bytes[bytesPos++]!=backing[byteMask & pp++]) {
				return false;
			}
		}
		//only moves forward when equal.
		position = pp;
		return true;
	}
	
	
    public static > A readUTF(DataInputBlobReader reader, int length, A target) throws IOException {
    	if (length>=0) {
	    	assert(reader.storeMostRecentPacked(-1));
	    	long charAndPos = ((long)reader.position)<<32;
	        long limit = ((long)reader.position+length)<<32;
	
	        while (charAndPos> int read(DataInputBlobReader reader, byte[] b, int off, int len, int mask) {
    	assert(null!=reader.backing);
    	assert(null!=b);
    	
    	if (null==reader.backing) {
    		throw new NullPointerException("hack test");
    	}
    	if (null==b) {
    		throw new NullPointerException("hack test");
    	}
    	
        int max = bytesRemaining(reader);
        if (len > max) {
            len = max;
        }
        Pipe.copyBytesFromToRing(reader.backing, reader.position, reader.byteMask, b, off, mask, len);
        reader.position += len;
        return len;
    }
    
    @Override
    public int readInto(byte[] b, int off, int len, int mask) {
    	return read(this,b,off,len,mask);
    }
    
    
    @Override
    public void readInto(ChannelWriter writer, int length) {
    	
    	DataOutputBlobWriter.write((DataOutputBlobWriter)writer, backing, position, length, byteMask);
    	position += length;
    	
    }    
    
    public > void readInto(DataOutputBlobWriter writer, int length) {
    	
    	DataOutputBlobWriter.write(writer, backing, position, length, byteMask);
    	position += length;
    	
    }   
    
    
    ////////
    //parsing methods
    ///////
    public static TrieParser textToNumberTrieParser() {
    	if (textToNumberParser == null) {    		
    		TrieParser p = new TrieParser(8,false); //supports streaming, so false
    		p.setUTF8Value("%i%.", 1); 
    		
    		
    		
    		//p.setUTF8Value("%i%.%/%.", 1);
    		//the above pattern can parse  234 and 234.34 and 23/45 and 23.4/56.7
    		//it also supports the normal capture methods for ints and decimals with no change
    		
    		//TrieParserReader.capturedLongField(parserReader, 0)
    		
    		
    		textToNumberParser = p;
    	}
    	return textToNumberParser;
    }
    
    private TrieParserReader parserReader() {
    	if (null == reader) {
    		reader = new TrieParserReader(true);
    	}
    	return reader;
    }
    
    public static long readUTFAsLong(DataInputBlobReader reader) {    	
    	return convertTextToLong(reader.parserReader(), reader.backing, reader.position, reader.byteMask, reader.available());
    }

	public static long convertTextToLong(TrieParserReader parserReader,
			                             byte[] backing, 
			                             int position, 
			                             int mask,
			                             int available) {
		
		return (TrieParserReader.query(parserReader, 
				                   textToNumberTrieParser(), 
    			                   backing, position, 
    			                   available, mask)>=0)
				? TrieParserReader.capturedLongField(parserReader, 0) : -1;
	}
    
    public static double readUTFAsDecimal(DataInputBlobReader reader) {
    	return convertTextToDouble(reader.parserReader(), reader.backing, reader.position, reader.available(), reader.byteMask);	
    }

	public static double convertTextToDouble(TrieParserReader parserReader, 
											 byte[] backing, int position,
											 int available, int mask) {
		
		return (TrieParserReader.query(parserReader, textToNumberTrieParser(), 
		    			               backing, position,
		    			               available, mask)>=0) ?
		    			            		   
    		Decimal.asDouble(
    				TrieParserReader.capturedDecimalMField(parserReader, 0), 
    				TrieParserReader.capturedDecimalEField(parserReader, 0)) : -1;
    				
	}
	
	public static boolean convertTextToDecimal(TrieParserReader parserReader, 
			 byte[] backing, int position,
			 int available, int mask, DecimalResult result) {

		if (TrieParserReader.query(parserReader, textToNumberTrieParser(), 
		      backing, position,
		      available, mask)>=0) {
			
			result.result(TrieParserReader.capturedDecimalMField(parserReader, 0), 
				    	  TrieParserReader.capturedDecimalEField(parserReader, 0));
			
			return true;
		} else {
			return false;
		}
		
	}
    
    ///////
    //Packed Chars
    //////
    @Override
    public  A readPackedChars(A target) {
        readPackedChars(this,target);
        return target;
    }
    
    public static > void readPackedChars(DataInputBlobReader that, Appendable target) {
        int length = readPackedInt(that);
        int i = length;
        try {
	        while (--i>=0) {
	            target.append((char) readPackedInt(that));
	        }
        } catch (IOException e) {
        	throw new RuntimeException(e);
        }
    }
    
    ///////////////////////////////////////////////////////////////////////////////////
    //Support for packed values
    //////////////////////////////////////////////////////////////////////////////////
    //Read signed using variable length encoding as defined in FAST 1.1 specification
    //////////////////////////////////////////////////////////////////////////////////
    
    /**
     * Parse a 64 bit signed value 
     */
    @Override
    public long readPackedLong() {   
            long result = readPackedLong(this);
            assert(storeMostRecentPacked(result));
            return result;
    }

    boolean storeMostRecentPacked(long result) {
    	mostRecentPacked=result;
    	return true;
	}

	@Override
    public final boolean wasPackedNull() {
    	assert checkRecentPacked(this) : "Must not check for null unless value was read as zero first";
        return wasPackedNull(this);
    }
    
    @Override
    public final boolean wasDecimalNull() {
    	assert checkRecentPacked(this) : "Must not check for null unless value was read as zero first";
    	return wasDecimalNull(this);
    }
    
    @Override
    public int readPackedInt() {   
        int result = readPackedInt(this);
        assert(storeMostRecentPacked(result));
        return result;
    }
    
    @Override
    public double readDecimalAsDouble() {
    	long m = readPackedLong();
    	assert(storeMostRecentPacked(m));
    	if (0!=m) {
    		return Decimal.asDouble(m, readByte());
    	} else {
    		position++;//must consume last byte (not needed);
    		return wasPackedNull() ? Double.NaN: 0;
    	}
    }
    
    @Override
	public  A readDecimalAsText(A target) {
		long m = readPackedLong();
		assert(storeMostRecentPacked(m));
		
		return Appendables.appendDecimalValue(target, m, readByte());
	}


    
    @Override
    public double readRationalAsDouble() {
    	return (double)readPackedLong()/(double)readPackedLong();
    }
    
    @Override
    public  A readRationalAsText(A target) {
    
    	try {
			return (A) Appendables.appendValue(
					Appendables.appendValue(target, readPackedLong())
			           .append("/"), readPackedLong());
		} catch (IOException e) {
			throw new RuntimeException(e);
		}      
    	
    }
    
    @Override
    public long readDecimalAsLong() {
    	//packed nulls will appear as zero in this case.
    	long m = readPackedLong();
    	assert(storeMostRecentPacked(m));
		return Decimal.asLong(m, readByte());
    }
    
    @Override
    public short readPackedShort() {
        short result = (short)readPackedInt(this);
        assert(storeMostRecentPacked(result));
        return result;
    }

    public static > Pipe getBackingPipe(DataInputBlobReader that) {
    	return that.pipe;
    }
    
    public static > long readPackedLong(DataInputBlobReader that) {
        byte v = that.backing[that.byteMask & that.position++];
        long accumulator = (~((long)(((v>>6)&1)-1)))&0xFFFFFFFFFFFFFF80l;
        
        IntArrayPool temp = IntArrayPoolLocal.get();
        int id = IntArrayPool.lockInstance(temp, 1);
        int[] pos = IntArrayPool.getArray(IntArrayPoolLocal.get(), 1, id); //thread local wrapper
        pos[0] = that.position;        
        long result = (v >= 0) ? readPackedLong((accumulator | v) << 7,that.backing,that.byteMask,pos, 0) : (accumulator) |(v & 0x7F);
        that.position = pos[0];
        IntArrayPool.releaseLock(temp, 1, id);
        
        return result;
    }
    
    public static > long readPackedLong(byte[] backing, int byteMask, int[] position) {
        byte v = backing[byteMask & position[0]++];
        long accumulator = (~((long)(((v>>6)&1)-1)))&0xFFFFFFFFFFFFFF80l;
         
        return (v >= 0) ? readPackedLong((accumulator | v) << 7,backing,byteMask,position, 0) : (accumulator) |(v & 0x7F);

    }
    
    public static > int readPackedInt(DataInputBlobReader that) {
        byte v = that.backing[that.byteMask & that.position++];
        int accumulator = (~((int)(((v>>6)&1)-1)))&0xFFFFFF80; 
        return (v >= 0) ? readPackedInt((accumulator | v) << 7,that.backing,that.byteMask,that, 0) : accumulator |(v & 0x7F);
    }
    
    //recursive use of the stack turns out to be a good way to unroll this loop.
    private static > long readPackedLong(long a, byte[] buf, int mask, int[] pos, int depth) {
        return readPackedLongB(a, buf, mask, pos, buf[mask & pos[0]++], depth+1);
    }

    private static > long readPackedLongB(long a, byte[] buf, int mask, int[] pos, byte v, int depth) {
    	assert(depth<11) : "Error malformed data";
    	if (depth<11) {
    		//Not checking for this assert because we use NaN for business logic, assert(a!=0 || v!=0) : "malformed data";
    		return (v >= 0) ? readPackedLong((a | v) << 7, buf, mask, pos, depth) : a | (v & 0x7Fl);
    	} else {
    		logger.warn("malformed data");
    		return 0;
    	}
    }

    public static > boolean wasPackedNull(DataInputBlobReader that) {
     	return wasPackedNull(that.backing, that.byteMask, that.position);
    }
    
    public static > boolean wasDecimalNull(DataInputBlobReader that) {
       	return wasPackedNull(that.backing, that.byteMask, that.position-1); //skip over trailing e
    }

	private static > boolean checkRecentPacked(DataInputBlobReader that) {
		boolean result = 0 == that.mostRecentPacked;
		that.mostRecentPacked = -1;//can only be checked once.
		return result;
	}

	private static boolean wasPackedNull(byte[] localBacking, int localMask, int localPos) {
		assert( 0 != (0x80&localBacking[localMask & (localPos-1)]) ) : "Must only call wasPackedNull AFTER a packed number read";
    	
    	return (((byte)0x80)==localBacking[localMask & (localPos-1)])
    		   && 0==localBacking[localMask & (localPos-2)]
    	       && 0==localBacking[localMask & (localPos-3)];
	}
       
    private static > int readPackedInt(int a, byte[] buf, int mask, DataInputBlobReader that, int depth) {
    	return readPackedIntB(a, buf, mask, that, buf[mask & that.position++], depth+1);
    }

    private static > int readPackedIntB(int a, byte[] buf, int mask, DataInputBlobReader that, byte v, int depth) {
    	assert(depth<7) : "Error malformed data";
        return (v >= 0) ? readPackedInt((a | v) << 7, buf, mask, that, depth) : a | (v & 0x7F);
    }

    
	public static void setupParser(DataInputBlobReader input, TrieParserReader reader) {
		
		//System.out.println("input data to be parsed: ");
		//Appendables.appendUTF8(System.out, input.backing, input.position, bytesRemaining(input), input.byteMask);
		
		TrieParserReader.parseSetup(reader, input.backing, input.position, bytesRemaining(input), input.byteMask); 
	}

	public static void setupParser(DataInputBlobReader input, TrieParserReader reader, int length) {
		TrieParserReader.parseSetup(reader, input.backing, input.position, Math.min(bytesRemaining(input), length), input.byteMask); 
	}
	@Override
	public StructuredReader structured() {
		assert(isStructured()) : "This data is not structured";
		return structuredReader;
	}

	public static short peekShort(DataInputBlobReader channelReader) {
        return (short)((       channelReader.backing[channelReader.byteMask & channelReader.position] << 8) |
        		       (0xFF & channelReader.backing[channelReader.byteMask & channelReader.position])); 
	}
	
	public int length() {
		return length;
	}

    
}