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

com.javanut.pronghorn.pipe.DataOutputBlobWriter 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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.javanut.pronghorn.util.Appendables;
import com.javanut.pronghorn.util.TrieParser;
import com.javanut.pronghorn.util.TrieParserReader;
import com.javanut.pronghorn.util.ma.RunningStdDev;

import java.io.Externalizable;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectOutputStream;

public class DataOutputBlobWriter> extends ChannelWriter {

    protected final Pipe backingPipe;
    final byte[] byteBuffer;
    private final int byteMask;
    private static final Logger logger = LoggerFactory.getLogger(DataOutputBlobWriter.class);
    
    private int startPosition;
    private int activePosition;
    private int lastPosition;
    private int backPosition;
    
 //   private int recTypeIndex = -1;
 //   public static final int REC_TYPE_CHECK = (0xD03)<<20; //value 3331, 12 bits pushed up 
   	
	private final RunningStdDev objectSizeData = new RunningStdDev();

    private boolean structuredWithIndexData = false;
    private final StructuredWriter structuredWriter;
    
    public DataOutputBlobWriter(Pipe p) {
        this.backingPipe = p;
        assert(null!=p) : "requires non null pipe";
        assert(Pipe.isInit(p)): "The pipe must be init before use.";
        this.byteBuffer = Pipe.blob(p);
        this.byteMask = Pipe.blobMask(p);  
        assert(this.byteMask!=0): "mask is "+p.blobMask+" size of blob is "+p.sizeOfBlobRing;
        this.structuredWriter = new StructuredWriter(this);
    }
    
    public void openField() {
    	
        openField(this);
        
    }
	
    @Override
	public boolean reportObjectSizes(Appendable target) {
    	if (RunningStdDev.sampleCount(objectSizeData)>=2) {
			try {		
				target.append("Pipe object writing size data for ")
				      .append(backingPipe.toString())
				      .append("\n");
				RunningStdDev.appendTo(objectSizeData, target);
				target.append("\n");
			} catch (IOException e) {
				throw new RuntimeException(e);
			}
    	}
		return true;
	}
	
	
    /**
     * Internal function only used when dependent classes want to add bounds check per method call.
     * 
     * @param  schema
     * @param that instance in use
     * @param x needed value
     */
    protected static > void checkLimit(DataOutputBlobWriter that, int x) {
    	if (that.length()+x > that.backingPipe.maxVarLen ) {
    		throw new RuntimeException("The writer is limited to a maximum length of "+that.backingPipe.maxVarLen
    				                  +". Write less data or declare a larger field. Already wrote "
    				                  +(that.activePosition-that.startPosition)+" at position "+that.activePosition+" attempting to add "+x);
    	}
    }

    public static > DataOutputBlobWriter openField(final DataOutputBlobWriter writer) {
     	return openFieldAtPosition(writer, Pipe.getWorkingBlobHeadPosition(writer.backingPipe));
    }

	public static > DataOutputBlobWriter openFieldAtPosition(final DataOutputBlobWriter writer,
																		int workingBlobHeadPosition) {
		assert(workingBlobHeadPosition>=0) : "working head position must not be negative";
		writer.backingPipe.openBlobFieldWrite();
        //NOTE: this method works with both high and low APIs.
		writer.startPosition = writer.activePosition = (writer.byteMask & workingBlobHeadPosition);
        //without any index we can write all the way up to maxVarLen;
		writer.lastPosition = writer.startPosition + writer.backingPipe.maxVarLen;
        //with an index we are limited because room is saved for type data lookup.
        writer.backPosition = writer.startPosition + Pipe.blobIndexBasePosition(writer.backingPipe);
        //this is turned on when callers begin to add index data
        writer.structuredWithIndexData = false;
        if (writer.backingPipe.sizeOfBlobRing>1) {
        	DataOutputBlobWriter.setStructType(writer, -1);//clear any previous type
        }
        return writer;
	}
    
    public int position() {
    	return activePosition - startPosition;
    }
    
    @Override
    public int remaining() {
    	int result = (lastPosition - activePosition);
    	assert(result <= this.getPipe().maxVarLen);
    	return result;
    }
    
    public void debug() {
        Appendables.appendArray(System.out, '[', backingPipe.blobRing, startPosition, backingPipe.blobMask, ']',  activePosition-startPosition);
    }
    
    public void debugAsUTF8() {
    	Appendable target = System.out;
    	debugAsUTF8(target);
        System.out.println();
    }

	public void debugAsUTF8(Appendable target) {
		Appendables.appendUTF8(target, backingPipe.blobRing, startPosition, activePosition-startPosition, backingPipe.blobMask);
	}
    
	/**
	 * Data written so far is directly copied to the destination writer.
	 * @param writer target writer
	 */
	public void replicate(DataOutputBlobWriter writer) {
		writer.write(backingPipe.blobRing, startPosition, activePosition-startPosition, backingPipe.blobMask);
	}
	
	/**
	 * Data written so far and in the offset to length segment is directly copied to the destination writer.
	 * @param writer
	 * @param offset from start of the writer, 0 is the start
	 * @param length must not be longer than bytes written.
	 */
	public void replicate(DataOutputBlobWriter writer, int offset, int length) {
		assert(length<= (activePosition-startPosition));
		writer.write(backingPipe.blobRing, startPosition+offset, length, backingPipe.blobMask);
	}
    
	public void replicate(Appendable target) {
		if (target instanceof DataOutputBlobWriter) {
			replicate((DataOutputBlobWriter)target);
		} else {
			Appendables.appendUTF8(target, backingPipe.blobRing, startPosition, activePosition-startPosition, backingPipe.blobMask);
		}
	}
	
    public static > boolean tryClearIntBackData(DataOutputBlobWriter writer, int intCount) {	
    	int bytes = (2+intCount)*4;//one for the schema index
    	
    	Pipe.validateVarLength(writer.getPipe(), bytes);
    	
    	int temp = writer.backPosition-bytes;
    	if (temp >= writer.activePosition) {
    		int p = writer.lastPosition;
    		while (--p >= temp) {//clear all
    			writer.byteBuffer[writer.byteMask & p] = (byte)0xFF;
    		}    		
    		writer.backPosition = temp;
    		return true;
    	} else {
    		return false;
    	}    	
    }
    
    public static > int lastBackPositionOfIndex(DataOutputBlobWriter writer) {
    	return writer.backPosition-writer.startPosition;
    }
    
    public static > boolean structTypeValidation(DataOutputBlobWriter writer, int value) {
    	int old = getStructType(writer); 
        
    	if (value!=old) {
    		if (old<=0) {
    			writer.structuredWithIndexData = true;    			
    			int base2 = writer.startPosition+Pipe.blobIndexBasePosition(writer.backingPipe);
    			write32(writer.byteBuffer, writer.byteMask, base2, value);
    		} else {
    			throw new UnsupportedOperationException("Type mismatch found "+old+" expected "+value);
    		}
    		
    	}
    	return true;
    }

	public static > int getStructType(DataOutputBlobWriter writer) {
		return peekInt(writer, writer.startPosition+Pipe.blobIndexBasePosition(writer.backingPipe));
	
	}

	private static > int peekInt(DataOutputBlobWriter writer, final int base) {
		return ( (       writer.byteBuffer[writer.byteMask & base]) << 24) |
                ( (0xFF & writer.byteBuffer[writer.byteMask & (base+1)]) << 16) |
                ( (0xFF & writer.byteBuffer[writer.byteMask & (base+2)]) << 8) |
                  (0xFF & writer.byteBuffer[writer.byteMask & (base+3)]);
	}
    
    public static > void setStructType(DataOutputBlobWriter writer, int value) {
    	write32(writer.byteBuffer, writer.byteMask, writer.startPosition+Pipe.blobIndexBasePosition(writer.backingPipe), value);
    }

    public static boolean isIntBackDataSet(DataOutputBlobWriter writer, int pos) {
    	return peekInt(writer, computeBackDataPos(writer, pos))!=-1;
    }
    
    public static > void setIntBackData(DataOutputBlobWriter writer, int value, int pos) {
    	
    	assert(value<=writer.position()) : "wrote "+value+" but all the data is only "+writer.position();
    	
    	assert(pos>=0) : "Can not write beyond the end. Index values must be zero or positive";
    	write32(writer.byteBuffer, writer.byteMask, computeBackDataPos(writer, pos), value);       
    }

	private static > int computeBackDataPos(DataOutputBlobWriter writer, int pos) {
		return writer.startPosition+Pipe.blobIndexBasePosition(writer.backingPipe)-(4*(pos+1));
	}

	public static > void writeToEndFrom(DataOutputBlobWriter writer, int sizeInBytes, DataInputBlobReader reader) {
	
		DataInputBlobReader.read(reader, 
				    writer.byteBuffer, 
				    writer.startPosition+Pipe.blobIndexBasePosition(writer.backingPipe)-sizeInBytes, 
				    sizeInBytes, 
				    writer.byteMask);
		
	}
    
	@Override
    public int closeHighLevelField(int targetFieldLoc) {
        return closeHighLevelField(this, targetFieldLoc);
    }

    public static > int closeHighLevelField(DataOutputBlobWriter writer, int targetFieldLoc) {
        //this method will also validate the length was in bound and throw unsupported operation if the pipe was not large enough
        //instead of fail fast as soon as one field goes over we wait to the end and only check once.
        int len = length(writer);
        
        //Appendables.appendUTF8(System.err, writer.byteBuffer, writer.startPosition, len, writer.byteMask);
                
        PipeWriter.writeSpecialBytesPosAndLen(writer.backingPipe, targetFieldLoc, len, writer.startPosition);
        writer.backingPipe.closeBlobFieldWrite();
        
        //Appendables.appendUTF8(System.out, writer.getPipe().blobRing, writer.startPosition, len, writer.getPipe().blobMask);
        
        return len;
    }
    
    @Override
    public int closeLowLevelField() {
        return closeLowLevelField(this);
    }
    
    /**
     * Close field and record its length as the number of bytes consumed by the BlobWriter
     * @param  schema
     * @param writer
     */
    public static > int closeLowLevelField(DataOutputBlobWriter writer) {
        return closeLowLeveLField(writer, length(writer));
    }
    
    /**
     * Ignore the length of this field and close it consuming all the available blob space for this field.
     * @param writer
     */
    public static > int closeLowLevelMaxVarLenField(DataOutputBlobWriter writer) {
        return closeLowLeveLField(writer, writer.getPipe().maxVarLen);
    }

    
	 public static > void commitBackData(DataOutputBlobWriter writer, int structId) {  	
	 	writer.structuredWithIndexData = true;
	 	setStructType(writer, structId);
	 }
    
	 public static > int closeLowLeveLField(DataOutputBlobWriter writer, int len) {
      
		assert(len<=writer.backingPipe.sizeOfBlobRing): "bad length "+len+" larger than pipe";
		if (writer.structuredWithIndexData) {

			//write this field as length len but move head to the end of maxvarlen
			writer.activePosition = writer.lastPosition;
			Pipe.setBlobWorkingHead(writer.backingPipe, 
					                 writer.activePosition & Pipe.BYTES_WRAP_MASK);			
		    
		} else { 

			//do not keep index just move forward by length size
			Pipe.addAndGetBlobWorkingHeadPosition(writer.backingPipe, len);
		}
		
		assert(writer.startPosition>=0) : "Error bad position of "+writer.startPosition+" length was "+len;
		
        Pipe.addBytePosAndLenSpecial(writer.backingPipe, 
        		                     writer.startPosition, len);
        Pipe.validateVarLength(writer.backingPipe, len);
        writer.backingPipe.closeBlobFieldWrite();
 
        if (writer.structuredWithIndexData) {
        	//set the flag marking this as structed.
        	Pipe.slab(writer.backingPipe)[writer.backingPipe.slabMask & (int)(Pipe.workingHeadPosition(writer.backingPipe)-2)] |= Pipe.STRUCTURED_POS_MASK;
        }
        
        return len;
	}
	 
	 @Override
    public int length() {
        return length(this);
    }

    @Override
    public int absolutePosition() {
        return activePosition;
    }

    @Override
    public void absolutePosition(int absolutePosition) {
        activePosition = absolutePosition;
    }

    public static > int length(DataOutputBlobWriter writer) {
       
    	return dif(writer, writer.startPosition, writer.activePosition);

    }

	private static > int dif(DataOutputBlobWriter writer, int pos1, int pos2) {
		return (pos2>=pos1) ? (pos2-pos1) : (writer.backingPipe.sizeOfBlobRing - (writer.byteMask & pos1))+(pos2 & writer.byteMask);
	}
	
	@Override
    public byte[] toByteArray() {
        byte[] result = new byte[length()];        
        Pipe.copyBytesFromToRing(byteBuffer, startPosition, byteMask, result, 0, Integer.MAX_VALUE, result.length);
        return result;
    }
 
	@Override
	public void write(Externalizable object) {		
		int pos = activePosition;
		try {
			object.writeExternal(this);
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
		assert(Pipe.validateVarLength(this.backingPipe, length(this)));
		assert(collectObjectSizeData(activePosition-pos));		
	}	

	private boolean collectObjectSizeData(int size) {
		//collect all these data points to make a better decision about this pipe length.	
		RunningStdDev.sample(objectSizeData, size);	
		return true;
	}
	
	@Override
    public void writeObject(Object object) {
		    int pos = activePosition;
		    try {
		    	//logger.info("creating new output stream");
	           	ObjectOutputStream oos = new ObjectOutputStream(this); //writes stream header
	           	//logger.info("write the object");
	           	oos.writeObject(object);            
	           	//logger.info("flush");
	            oos.flush();
	            //logger.info("done");
		    } catch (IOException e) {
		    	throw new RuntimeException(e);
		    }
            assert(Pipe.validateVarLength(this.backingPipe, length(this)));
            assert(collectObjectSizeData(activePosition-pos));	
            
    }
    
    @Override
    public void write(int b) {
        byteBuffer[byteMask & activePosition++] = (byte)b;
    }

    @Override
    public void write(byte[] b) {
    	DataOutputBlobWriter.write(this, b, 0, b.length);    	
    }

    @Override
    public void write(byte[] b, int off, int len) {
    	DataOutputBlobWriter.write(this, b, off, len);
    }

    @Override
    public void writeBoolean(boolean v) {
        byteBuffer[byteMask & activePosition++] = (byte) (v ? 1 : 0);
    }

    @Override
    public void writeBooleanNull() {
        byteBuffer[byteMask & activePosition++] = (byte) -1;
    }
    
    @Override
    public void writeByte(int v) {
        byteBuffer[byteMask & activePosition++] = (byte)v;
    }
    
    public static void writeByte(DataOutputBlobWriter that, int v) {
    	that.byteBuffer[that.byteMask & that.activePosition++] = (byte)v;
    }

    @Override
    public void writeShort(int v) {
        activePosition = write16(byteBuffer, byteMask, activePosition, v); 
    }

    @Override
    public void writeChar(int v) {
        activePosition = write16(byteBuffer, byteMask, activePosition, v); 
    }

    @Override
    public void writeInt(int v) {
        activePosition = write32(byteBuffer, byteMask, activePosition, v); 
    }

    @Override
    public void writeLong(long v) {
        activePosition = write64(byteBuffer, byteMask, activePosition, v);
    }

    @Override
    public void writeFloat(float v) {
        activePosition = write32(byteBuffer, byteMask, activePosition, Float.floatToIntBits(v));
    }

    @Override
    public void writeDouble(double v) {
        activePosition = write64(byteBuffer, byteMask, activePosition, Double.doubleToLongBits(v));
    }

    @Override
    public void writeBytes(String s) {
        byte[] localBuf = byteBuffer;
        int mask = byteMask;
        int pos = activePosition;
        int len = s.length();
        for (int i = 0; i < len; i ++) {
            localBuf[mask & pos++] = (byte) s.charAt(i);
        }
        activePosition = pos;
    }

    @Override
    public void writeChars(String s) {
        byte[] localBuf = byteBuffer;
        int mask = byteMask;
        int pos = activePosition;
        int len = s.length();
        for (int i = 0; i < len; i ++) {
            pos = write16(localBuf, mask, pos, (int) s.charAt(i));
        }
        activePosition = pos;
        
    }


    @Override
    public void writeUTF(String s) {
    	if (null!=s) {
    		activePosition = writeUTF(this, s, s.length(), byteMask, byteBuffer, activePosition);
    	} else {
    		writeShort(-1);
    	}
    	
    }
    
	@Override
    public void writeUTF8Text(CharSequence s) {
	    if (null!=s) {
			encodeAsUTF8(this,s);
		} else {
			writeShort(-1);
		}
    }

	@Override
    public void writeUTF8Text(CharSequence s, int pos, int len) {
    	encodeAsUTF8(this,s,pos,len);
    }
	
    private static > int writeUTF(DataOutputBlobWriter writer, CharSequence s, int len, int mask, byte[] localBuf, int pos) {
        int origPos = pos;
        pos+=2;

        pos = encodeAsUTF8(writer, s, 0, len, mask, localBuf, pos);

        write16(localBuf,mask,origPos, (pos-origPos)-2); //writes bytes count up front
        return pos;
    }
    
    public static > void encodeAsUTF8(DataOutputBlobWriter writer, CharSequence s) {
        writer.activePosition = encodeAsUTF8(writer, s, 0, s.length(), writer.byteMask, writer.byteBuffer, writer.activePosition);
    }
    
    public static > void encodeAsUTF8(DataOutputBlobWriter writer, CharSequence s, int position, int length) {
        writer.activePosition = encodeAsUTF8(writer, s, position, length, writer.byteMask, writer.byteBuffer, writer.activePosition);
    }
    
    public static > int encodeAsUTF8(DataOutputBlobWriter writer, CharSequence s, int sPos, int sLen, int mask, byte[] localBuf, int pos) {
        while (--sLen >= 0) {
            pos = Pipe.encodeSingleChar((int) s.charAt(sPos++), localBuf, mask, pos);
        }
        return pos;
    }
    
    ///////////
    //end of DataOutput methods
    ////////// 
    
    public void writeStream(DataInputBlobReader input, int length) {
    	activePosition += DataInputBlobReader.read(input, byteBuffer, activePosition, length, byteMask);
    }
    
    public static , S extends MessageSchema> void writeStream(DataOutputBlobWriter that,  DataInputBlobReader input, int length) {
    	that.activePosition += DataInputBlobReader.read(input, that.byteBuffer, that.activePosition, length, that.byteMask);
    }

	public void writeStream(InputStream inputStream, int length) {	
		assert hasExpectedData(inputStream, length);
		Pipe.copyBytesFromInputStreamToRing(inputStream, byteBuffer, activePosition, byteMask, length);
        activePosition+=length;
	}

	private boolean hasExpectedData(InputStream inputStream, int length) {
		try {
			return inputStream.available()>=length;
		} catch (IOException ioex) {
			return false;
		}
	}
	
    /////////////////////
    /////////////////////

    public static int write16(byte[] buf, int mask, int pos, int v) {
        buf[mask & pos++] = (byte)(v >>> 8);
        buf[mask & pos++] = (byte) v;
        return pos;
    }    
    
    public static int write32(byte[] buf, int mask, int pos, int v) {
        buf[mask & pos++] = (byte)(v >>> 24);
        buf[mask & pos++] = (byte)(v >>> 16);
        buf[mask & pos++] = (byte)(v >>> 8);
        buf[mask & pos++] = (byte) v;
        return pos;
    }
    
    public static int write64(byte[] buf, int mask, int pos, long v) {
        buf[mask & pos++] = (byte)(v >>> 56);
        buf[mask & pos++] = (byte)(v >>> 48);
        buf[mask & pos++] = (byte)(v >>> 40);
        buf[mask & pos++] = (byte)(v >>> 32);
        buf[mask & pos++] = (byte)(v >>> 24);
        buf[mask & pos++] = (byte)(v >>> 16);
        buf[mask & pos++] = (byte)(v >>> 8);
        buf[mask & pos++] = (byte) v;
        return pos;
    }
    
	@Override
    public void writeUTF(CharSequence s) {
		if (null!=s) {
			activePosition = writeUTF(this, s, s.length(), byteMask, byteBuffer, activePosition);
		} else {
    		writeShort(-1);
    	}		
	}    
    
	@Override
    public void writeASCII(CharSequence s) {
		
        byte[] localBuf = byteBuffer;
        int mask = byteMask;
        int pos = activePosition;
        if (null!=s) {
	        int len = s.length();        
	        for (int i = 0; i < len; i++) {
	            localBuf[mask & pos++] = (byte)s.charAt(i);
	        }
        }
        localBuf[mask & pos++] = 0;//terminator
        activePosition = pos;
    }
         
	@Override
	public void consume(byte[] backing, int pos, int len, int mask) {
		Pipe.copyBytesFromToRing(backing, pos, mask, byteBuffer, activePosition, byteMask, len);
		activePosition += len;
	}

	@Override
	public void consume(byte value) {
		byteBuffer[byteMask & activePosition++] = value;
	}
    
	@Deprecated
    public void writeByteArray(byte[] bytes) {
        activePosition = writeByteArray(this, bytes, bytes.length, byteBuffer, byteMask, activePosition);
    }
    
    private static > int writeByteArray(DataOutputBlobWriter writer, byte[] bytes, int len, byte[] bufLocal, int mask, int pos) {
    	pos = writePackedInt(bufLocal, mask, pos, len);
        Pipe.copyBytesFromArrayToRing(bytes, 0, writer.byteBuffer, pos, writer.byteMask, len); 
		return pos+len;
    }

    @Deprecated
    public void writeCharArray(char[] chars) {
        activePosition = writeCharArray(chars, chars.length, byteBuffer, byteMask, activePosition);
    }

    private int writeCharArray(char[] chars, int len, byte[] bufLocal, int mask, int pos) {
    	pos = writePackedInt(bufLocal, mask, pos, len);    	
        for(int i=0;i> void write(DataOutputBlobWriter writer, byte[] source, int sourceOff, int sourceLen, int sourceMask) {
        Pipe.copyBytesFromToRing(source, sourceOff, sourceMask, writer.byteBuffer, writer.activePosition, writer.byteMask, sourceLen); 
        writer.activePosition+=sourceLen;
    }

    public static > void write(DataOutputBlobWriter writer, byte[] source, int sourceOff, int sourceLen) {
        Pipe.copyBytesFromArrayToRing(source, sourceOff, writer.byteBuffer, writer.activePosition, writer.byteMask, sourceLen); 
        writer.activePosition+=sourceLen;
    }
    
    
    
    //////////
    //write run of byte
    //////////
    public static > void writeBytes(DataOutputBlobWriter writer, byte pattern, int runLength) {
    	int r = runLength;
    	while (--r>=0) {
    		writer.byteBuffer[writer.byteMask & writer.activePosition++] = pattern;
    	}
    }
    
    ////////
    //low level copy from reader to writer
    ///////
    
    public static > int writeBytes(DataOutputBlobWriter writer, DataInputBlobReader reader, int length) {

        int len = DataInputBlobReader.read(reader, writer.byteBuffer, writer.activePosition, length, writer.byteMask);        
        writer.activePosition+=len;
        return len;
    }
    
    
    ///////////////////////////////////////////////
    ///New idea: Packed char sequences
    ///////////////////////////////////////////////
	@Override
    public final void writePackedString(CharSequence text) {
        writePackedChars(this,text);
    }
    
    public static > void writePackedChars(DataOutputBlobWriter that, CharSequence s) {
        that.activePosition = writePackedCharsImpl(that, s, s.length(), 0, that.activePosition, that.byteBuffer, that.byteMask);
    }

    private static > int writePackedCharsImpl(DataOutputBlobWriter that, CharSequence source, int sourceLength, int sourcePos, int targetPos, byte[] target, int targetMask) {     
        targetPos = writeIntUnified(sourceLength, sourceLength, target, targetMask, targetPos, (byte)0x7F);   
        while (sourcePos < sourceLength) {// 7, 14, 21
            int value = (int) 0x7FFF & source.charAt(sourcePos++);
            targetPos = writeIntUnified(value, value, target, targetMask, targetPos, (byte)0x7F);            
        }
        return targetPos;
    } 
    
    public static > void writePackedChars(DataOutputBlobWriter that, byte[] source, int sourceMask, int sourceLength, int sourcePos) {
        that.activePosition = writePackedCharsImpl(that, source, sourceMask, sourceLength, sourcePos, that.activePosition, that.byteBuffer, that.byteMask);
    }
    
    private static > int writePackedCharsImpl(DataOutputBlobWriter that, byte[] source, int sourceMask, int sourceLength, int sourcePos, int targetPos, byte[] target, int targetMask) {
        targetPos = writeIntUnified(sourceLength, sourceLength, target, targetMask, targetPos, (byte)0x7F);        
        while (sourcePos < sourceLength) {
            int value = (int) source[sourceMask & sourcePos++];
            targetPos = writeIntUnified(value, value, target, targetMask, targetPos, (byte)0x7F);            
        }
        return targetPos;
    } 
    
    
    ///////////////////////////////////////////////////////////////////////////////////
    //Support for packed values
    //////////////////////////////////////////////////////////////////////////////////
    //Write signed using variable length encoding as defined in FAST 1.1 specification
    //////////////////////////////////////////////////////////////////////////////////
	@Override
    public final void writePackedLong(long value) {
        writePackedLong(this,value);
    }
	
	@Override
    public final void writePackedNull() {
        writePackedNull(this);
    }
    
	@Override
    public final void writePackedInt(int value) {
        writePackedInt(this,value);
    }
    
	@Override
    public final void writePackedShort(short value) {
        writePackedInt(this,value);
    }
    
	public static final > void writePackedNull(DataOutputBlobWriter that) {
		that.write(0);
		that.write(0);
		that.write((byte)0x80);  
	}
	
    public static final > void writePackedLong(DataOutputBlobWriter that, long value) {

    	that.activePosition = writePackedLong(value, that.byteBuffer, that.byteMask, that.activePosition);

    }

	public static int writePackedLong(long value, byte[] byteBuffer, int byteMask, int position) {
		final long mask = (value>>63);         // FFFFF  or 000000
        final long check = (mask^value)-mask;  //absolute value
        final int bit = (int)(check>>>63);     //is this the special value?
        
        //    Result of the special MIN_VALUE after abs logic                     //8000000000000000
        //    In order to get the right result we must pass something larger than //0x4000000000000000L;
       
		return writeLongUnified(value, (check>>>bit)+bit,
        		byteBuffer, 
        		byteMask, 
        		position, (byte)0x7F);
	}

    public static final > void writePackedInt(DataOutputBlobWriter that, int value) {

    	that.activePosition = writePackedInt(that.byteBuffer, that.byteMask, that.activePosition, value);

    }

	private static > int writePackedInt(byte[] buf, int bufMask, int pos, int value) {
		int mask = (value>>31);         // FFFFF  or 000000
        int check = (mask^value)-mask;  //absolute value
        int bit = (int)(check>>>31);     //is this the special value?        
        return writeIntUnified(value, (check>>>bit)+bit, buf, bufMask, pos, (byte)0x7F);
		
	}
    
    public static final > void writePackedShort(DataOutputBlobWriter that, short value) {
        
        int mask = (value>>31);         // FFFFF  or 000000
        int check = (mask^value)-mask;  //absolute value
        int bit = (int)(check>>>31);     //is this the special value?
        
        that.activePosition = writeIntUnified(value, (check>>>bit)+bit, that.byteBuffer, that.byteMask, that.activePosition, (byte)0x7F);

    }
    
    public static final > void writePackedULong(DataOutputBlobWriter that, long value) {
        assert(value>=0);
        //New branchless implementation we are testing
        that.activePosition = writeLongUnified(value, value, that.byteBuffer, that.byteMask, that.activePosition, (byte)0x7F);
    }
    
    public static final > void writePackedUInt(DataOutputBlobWriter that, int value) {
        assert(value>=0);
        that.activePosition = writeIntUnified(value, value, that.byteBuffer, that.byteMask, that.activePosition, (byte)0x7F);
    }
    
    public static final > void writePackedUShort(DataOutputBlobWriter that, short value) {
        assert(value>=0);
        that.activePosition = writeIntUnified(value, value, that.byteBuffer, that.byteMask, that.activePosition, (byte)0x7F);
    }
    
    private static final int writeIntUnified(final int value,final int check, final byte[] buf, final int mask, int pos, final byte low7) {
   
        if (check < 0x0000000000000040) {
        } else {
            if (check < 0x0000000000002000) {
            } else {
                if (check < 0x0000000000100000) {
                } else {
                    if (check < 0x0000000008000000) {
                    } else {                        
                        buf[mask & pos++] = (byte) (((value >>> 28) & low7));
                    }
                    buf[mask & pos++] = (byte) (((value >>> 21) & low7));
                }
                buf[mask & pos++] = (byte) (((value >>> 14) & low7));
            }
            buf[mask & pos++] = (byte) (((value >>> 7) & low7));
        }
        buf[mask & pos++] = (byte) (((value & low7) | 0x80));
        return pos;
    }
    

    private static final int writeLongUnified(final long value, final long check, final byte[] buf, final int mask, int pos, final byte low7) {
        //NOTE: do not modify this is designed to work best with Intel branch prediction which favors true
        if (check < 0x0000000000000040) {
        } else {
            if (check < 0x0000000000002000) {
            } else {
                if (check < 0x0000000000100000) {
                } else {
                    if (check < 0x0000000008000000) {
                    } else {
                        if (check < 0x0000000400000000L) {
                        } else {
                            if (check < 0x0000020000000000L) {
                            } else {
                                if (check < 0x0001000000000000L) {
                                } else {
                                    if (check < 0x0080000000000000L) {
                                    } else {
                                        if (check < 0x4000000000000000L) {
                                        } else {
                                            buf[mask & pos++] = (byte)(value >>> 63);
                                        }
                                        buf[mask & pos++] = (byte) (( ((int)(value >>> 56)) & low7));
                                    }
                                    buf[mask & pos++] = (byte) (( ((int)(value >>> 49)) & low7));
                                }
                                buf[mask & pos++] = (byte) (( ((int)(value >>> 42)) & low7));
                            }
                            buf[mask & pos++] =(byte) (( ((int)(value >>> 35)) & low7));
                        }
                        buf[mask & pos++] = (byte) (( ((int)(value >>> 28)) & low7));
                    }
                    buf[mask & pos++] = (byte) (( ((int)(value >>> 21)) & low7));
                }
                buf[mask & pos++] = (byte) (( ((int)(value >>> 14)) & low7));
            }
            buf[mask & pos++] = (byte) (( ((int)(value >>> 7)) & low7));
        }
        buf[mask & pos++] = (byte) (( ((int)(value & low7)) | 0x80));
        return pos;
    }

    @Override
    public ChannelWriter append(CharSequence s) {
	    if (null!=s) {
			encodeAsUTF8(this,s);
		} else {
			writeByte('n');
			writeByte('u');
			writeByte('l');
			writeByte('l');
		}
        return this;
    }

    @Override
    public ChannelWriter append(CharSequence csq, int start, int end) {
        this.activePosition = encodeAsUTF8(this, csq, start, end-start, this.byteMask, this.byteBuffer, this.activePosition);
        return this;
    }

    @Override
    public ChannelWriter append(char c) {
        this.activePosition = Pipe.encodeSingleChar((int)c, this.byteBuffer, this.byteMask, this.activePosition);
        return this;
    }

	public Pipe getPipe() {
		return backingPipe;
	}

	@Override
	public void writeRational(long numerator, long denominator) {
		writePackedLong(this,numerator);
		writePackedLong(this,denominator);
	}

	public static void copyBackData(DataOutputBlobWriter that, 
			                        byte[] backing, int start, 
			                        int copyLen, int byteMask2) {

		//Appendables.appendArray(System.out.append("To be copied"),  backing, start, byteMask2, copyLen).append("\n");
				
		//method must be redone to only copy what we need.
		Pipe.copyBytesFromToRing(backing, start, byteMask2, 
                that.byteBuffer, 
                that.startPosition + 
                //Pipe.blobIndexBasePosition(that.backingPipe)
                that.backingPipe.maxVarLen    -    copyLen,
                that.byteMask, 
                copyLen);

       that.structuredWithIndexData = true; //no need to set struct since we copied it over

//		Appendables.appendArray(System.out.append("  After copy"),  
//				that.byteBuffer,
//				 that.startPosition +that.backingPipe.maxVarLen-copyLen,
//				 that.byteMask,
//				 copyLen).append("\n");
		 
	   
	   
	}

	public long startsWith(TrieParserReader reader, TrieParser tp) {
		
		return TrieParserReader.query(reader, tp, byteBuffer, startPosition,
				               activePosition - startPosition, byteMask);

	}

	@Override
	public StructuredWriter structured() {
		assert(structuredWriter!=null) : 
			"this pipe was not initialized to support structures, call Pipe.typeData(pipe, registry); first";
		return structuredWriter;
	}

	@Override
	public void writeDecimal(long m, byte e) {
		writeByte(e);
		writePackedLong(m);	
	}

	@Override
	public void write(ChannelReader source) {
		source.readInto(this, source.available());	
	}
	
	public static void appendLongAsText(DataOutputBlobWriter writer, long value, boolean useNegPara) {
	        writer.activePosition = Appendables.longToChars(value, useNegPara, writer.byteBuffer, writer.byteMask, writer.activePosition);;
	}

	

}