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

com.javanut.pronghorn.pipe.stream.RingStreams 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.stream;

import static com.javanut.pronghorn.pipe.Pipe.byteBackingArray;
import static com.javanut.pronghorn.pipe.Pipe.bytePosition;
import static com.javanut.pronghorn.pipe.Pipe.headPosition;
import static com.javanut.pronghorn.pipe.Pipe.tailPosition;
import static com.javanut.pronghorn.pipe.Pipe.takeRingByteLen;
import static com.javanut.pronghorn.pipe.Pipe.takeRingByteMetaData;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import com.javanut.pronghorn.pipe.Pipe;
import com.javanut.pronghorn.pipe.RawDataSchema;

@Deprecated //Consider using the newer DataInputBlobReader or DataOutputBlobWriter or the fieldWrite methods in PipeReader Pipe or PipeWriter
public class RingStreams {
	
	/**
	 * Copies all bytes from the inputRing to the outputStream.  Will continue to do this until the inputRing
	 * provides a byteArray reference with negative length.
	 * 
	 * Upon exit the RingBuffer and OutputStream are NOT closed so this method can be called again if needed.
	 * 
	 * For example the same connection can be left open for sending multiple files in sequence.
	 * 
	 * 
	 * @param inputRing
	 * @param outputStream
	 * @throws IOException
	 */
	public static void writeToOutputStream(Pipe inputRing, OutputStream outputStream) throws IOException {
				
		long step =  RawDataSchema.FROM.fragDataSize[0];
		
		 //this blind byte copy only works for this simple message type, it is not appropriate for other complex types
		if (Pipe.from(inputRing) != RawDataSchema.FROM) {
			throw new UnsupportedOperationException("This method can only be used with the very simple RAW_BYTES catalog of messages.");
		}
		
		//target is always 1 ahead of where we are then we step by step size, this lets us pick up the 
		//EOF message which is only length 2
		long target = 2+tailPosition(inputRing);
				
		//write to outputStream only when we have data on inputRing.
        long headPosCache = headPosition(inputRing);

        //NOTE: This can be made faster by looping and summing all the lengths to do one single copy to the output stream
        //      That change may however increase latency.
        
        int byteMask = inputRing.blobMask;
        int byteSize = byteMask+1;
        
        while (true) {
        	        	
        	//block until one more byteVector is ready.
        	
        	long lastCheckedValue = headPosCache;
			while ( lastCheckedValue < target) {
				Pipe.spinWork(inputRing);//TODO: WARNING this may hang when using a single thread scheduler
			    lastCheckedValue = Pipe.headPosition(inputRing);
			}
			headPosCache = lastCheckedValue;	                        	    	                        		           
        	
        	int msgId = Pipe.takeMsgIdx(inputRing);

        				
        	if (msgId<0) { //exit logic
        		Pipe.releaseReadLock(inputRing);
          		break;
        	} else {          
            	int meta = Pipe.takeByteArrayMetaData((Pipe) inputRing);//side effect, this moves the pointer.
            	int len = Pipe.takeByteArrayLength((Pipe) inputRing);
            	if (len>0) {            	
					byte[] data = byteBackingArray(meta, inputRing);
					int off = bytePosition(meta,inputRing,len)&byteMask;
					int len1 = byteSize-off;
					if (len1>=len) {
						//simple add bytes
						outputStream.write(data, off, len); 
					} else {						
						//rolled over the end of the buffer
						outputStream.write(data, off, len1);
						outputStream.write(data, 0, len-len1);
					}
					outputStream.flush();
            	}
            	Pipe.releaseReadLock(inputRing);
        	}
        	
        	target += step;
            
        }   
		
	}
		
	/**
	 * Copies all bytes from the inputRing to each of the outputStreams.  Will continue to do this until the inputRing
	 * provides a byteArray reference with negative length.
	 * 
	 * Upon exit the RingBuffer and OutputStream are NOT closed so this method can be called again if needed.
	 * 
	 * For example the same connection can be left open for sending multiple files in sequence.
	 * 
	 * 
	 * @param inputRing
	 * @param outputStreams the streams we want to write the data to.
	 * @throws IOException
	 */
	public static void writeToOutputStreams(Pipe inputRing, OutputStream... outputStreams) throws IOException {
						
		long step =  RawDataSchema.FROM.fragDataSize[0];
		
		 //this blind byte copy only works for this simple message type, it is not appropriate for other complex types
		if (Pipe.from(inputRing) != RawDataSchema.FROM) {
			throw new UnsupportedOperationException("This method can only be used with the very simple RAW_BYTES catalog of messages.");
		}
		
		//only need to look for 2 value then step forward by steps this lets us pick up the EOM message without hanging.
		long target = 2+tailPosition(inputRing);
				
		//write to outputStream only when we have data on inputRing.
        long headPosCache = headPosition(inputRing);

        //NOTE: This can be made faster by looping and summing all the lengths to do one single copy to the output stream
        //      That change may however increase latency.
        
        while (true) {
        	        	
        	//block until one more byteVector is ready.
        	
        	long lastCheckedValue = headPosCache;
			while ( lastCheckedValue < target) {
				Pipe.spinWork(inputRing);//TODO: WARNING this may hang when using a single thread scheduler
			    lastCheckedValue = Pipe.headPosition(inputRing);
			}
			headPosCache = lastCheckedValue;
        	int msgId = Pipe.takeMsgIdx(inputRing);
        				
        	if (msgId<0) { //exit logic
        		int bytesCount = Pipe.takeInt(inputRing);
        		assert(0==bytesCount);
            	
        		Pipe.releaseReadLock(inputRing);
          		return;
        	} else {                    	
            	int meta = Pipe.takeByteArrayMetaData((Pipe) inputRing);//side effect, this moves the pointer.
            	int len = Pipe.takeByteArrayLength((Pipe) inputRing);
            	
        		int byteMask = inputRing.blobMask;
				byte[] data = byteBackingArray(meta, inputRing);
				
				int offset = bytePosition(meta,inputRing,len);        					
	
				int adjustedOffset = offset & byteMask;
				int adjustedEnd = (offset + len) & byteMask;
				int adjustedLength = 1 + byteMask - adjustedOffset;

				for(OutputStream os : outputStreams) {
					if ( adjustedOffset > adjustedEnd) {
						//rolled over the end of the buffer
					 	os.write(data, adjustedOffset, adjustedLength);
						os.write(data, 0, len - adjustedLength);
					} else {						
					 	//simple add bytes
						 os.write(data, adjustedOffset, len); 
					}
					os.flush();
				}
				
				Pipe.releaseReadLock(inputRing);
        	}
        	
        	target += step;
            
        }   
		
	}
	
	/**
	 * Copies all bytes from the inputStream to the outputRing.
	 * 
	 * Blocks as needed for the outputRing.
	 * Writes until the inputStream reaches EOF, this is signaled by a negative length from the call to read.
	 * 
	 * @param inputStream
	 * @param outputRing
	 * @throws IOException
	 */
	@Deprecated
	public static void readFromInputStream(InputStream inputStream, Pipe outputRing) throws IOException {
		assert (Pipe.from(outputRing) == RawDataSchema.FROM);
		int step = RawDataSchema.FROM.fragDataSize[0];
		int fill =  1 + outputRing.slabMask - step;
		int maxBlockSize = outputRing.maxVarLen;
		
		long targetTailValue = headPosition(outputRing)-fill;
		long tailPosCache = tailPosition(outputRing);
		
		byte[] buffer = Pipe.blob((Pipe) outputRing);
		int byteMask = outputRing.blobMask;
		
		int position = Pipe.getWorkingBlobHeadPosition((Pipe) outputRing);

		int size = 0;	
		try{
		    new Exception("this does not support wrapping of blob data and any usages should be changed over to the new stream apis int PipeReader, Pipe and PipeWriter").printStackTrace();
			while ( (size=inputStream.read(buffer,position&byteMask,((position&byteMask) > ((position+maxBlockSize-1) & byteMask)) ? 1+byteMask-(position&byteMask) : maxBlockSize))>=0 ) {	
				if (size>0) {
					//block until there is a slot to write into
					long lastCheckedValue = tailPosCache;
					while (null==Pipe.slab(outputRing) || lastCheckedValue < targetTailValue) {
						Pipe.spinWork(outputRing);
					    lastCheckedValue = Pipe.tailPosition(outputRing);
					}
					tailPosCache = lastCheckedValue;///TODO:M Rewrite using RingBuffer.roomToLowLevelWrite(output, size)
					targetTailValue += step;
					
					Pipe.addMsgIdx(outputRing, 0);
					Pipe.validateVarLength(outputRing, size);
					Pipe.addBytePosAndLen(outputRing, position, size);
			        Pipe.addAndGetBlobWorkingHeadPosition(outputRing, size);
			        
			        Pipe.confirmLowLevelWrite(outputRing, RawDataSchema.FROM.fragDataSize[0]);
					Pipe.publishWrites(outputRing);
					position += size;
				} else {
					Thread.yield();
				}
			}
		} catch (IOException ioex) {
			System.err.println("FAILURE detected at position: "+position+" last known sizes: "+size+" byteMask: "+outputRing.blobMask+
					" rolloever "+((position&byteMask) >= ((position+maxBlockSize-1) & byteMask))+"  "+(position&byteMask)+" > "+((position+maxBlockSize-1) & byteMask));
			throw ioex;
		}
	}
	
	
	/**
	 * copied data array into ring buffer.  It blocks if needed and will split the array on ring buffer if needed.
	 * 
	 * @param data
	 * @param output
	 * @param blockSize
	 */
	public static void writeBytesToRing(byte[] data, int dataOffset, int dataLength,  Pipe output, int blockSize) {
		assert (Pipe.from(output) == RawDataSchema.FROM);
		
	 	int fill = 1 + output.slabMask - RawDataSchema.FROM.fragDataSize[0];
		   
		long tailPosCache = tailPosition(output);    
		 
		int position = dataOffset; //position within the data array
		int stop = dataOffset+dataLength;
		while (position) inputRing);//side effect, this moves the pointer.
		    	int len = Pipe.takeByteArrayLength((Pipe) inputRing);
		    	
	    		byte[] data = byteBackingArray(meta, inputRing);

				int offset = bytePosition(meta,inputRing,len);        					
				
				if ((offset&byteMask) > ((offset+len-1) & byteMask)) {
					//rolled over the end of the buffer
					 int len1 = 1+byteMask-(offset&byteMask);
					 visitor.visit(data, offset&byteMask, len1, 0, len-len1);
				} else {						
					 //simple add bytes
					 visitor.visit(data, offset&byteMask, len); 
				}
				Pipe.confirmLowLevelRead(inputRing, RawDataSchema.FROM.fragDataSize[0]);
				Pipe.releaseReadLock(inputRing);
	    	}
	    	
	    	target += step;
	        
	    }   
		
	}

	
	
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy