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

org.xsocket.connection.WriteQueue Maven / Gradle / Ivy

There is a newer version: 2.8.15
Show newest version
/*
 * Copyright (c) xlightweb.org, 2006 - 2010. All rights reserved.
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * Please refer to the LGPL license at: http://www.gnu.org/copyleft/lesser.txt
 * The latest copy of this software may be found on http://www.xsocket.org/
 */
package org.xsocket.connection;


import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.util.ArrayList;

import org.xsocket.DataConverter;
import org.xsocket.connection.AbstractNonBlockingStream.ISink;



 
/**
 * the WriteQueue
 * 
 * @author grro
 */
final class WriteQueue implements Cloneable {

	// queue
	private final Queue queue = new Queue(); 
	
	
	// mark support
	private RewriteableBuffer writeMarkBuffer = null;
	private boolean isWriteMarked = false;
	
	
	public void reset() {
		queue.reset();
		
		writeMarkBuffer = null;
		isWriteMarked = false;
	}
	
	
	
	/**
	 * returns true, if empty
	 *
	 * @return true, if empty
	 */
	public boolean isEmpty() {
		return queue.isEmpty() && (writeMarkBuffer == null);
	}
	

	/**
	 * return the current size
	 *
	 * @return  the current size
	 */
	public int getSize() {

		int size = queue.getSize();
				
		if (writeMarkBuffer != null) {
			size += writeMarkBuffer.size();
		}
					
		return size;
	}
	
	

	/**
	 * drain the queue
	 *
	 * @return the queue content
	 */
	public ByteBuffer[] drain() {
		return queue.drain();
	}

		
	
	/**
	 * append a byte buffer to this queue.
	 *
	 * @param data the ByteBuffer to append
	 */
	public void append(ByteBuffer data) {
		
		if (data == null) {
			return;
		}
				
		if (isWriteMarked) {
			writeMarkBuffer.append(data);

		} else {
			queue.append(data);
		}
	}
	
	
	
	
	/**
	 * append a list of byte buffer to this queue. By adding a list,
	 * the list becomes part of to the buffer, and should not be modified outside the buffer
	 * to avoid side effects
	 *
	 * @param bufs  the list of ByteBuffer
	 */
	public void append(ByteBuffer[] bufs) {
		
		if (bufs == null) {
			return;
		}
		
		if (bufs.length < 1) {
			return;
		}
		

		if (isWriteMarked) {
			for (ByteBuffer buffer : bufs) {
				writeMarkBuffer.append(buffer);	
			}
			
		} else {
			queue.append(bufs);
		}
	}
	
	


	
	/**
	 * mark the current write position  
	 */
	public void markWritePosition() {
		removeWriteMark();

		isWriteMarked = true;
		writeMarkBuffer = new RewriteableBuffer();
	}



	/**
	 * remove write mark 
	 */
	public void removeWriteMark() {
		if (isWriteMarked) {
			isWriteMarked = false;
			
			append(writeMarkBuffer.drain());
			writeMarkBuffer = null;
		}
	}
	
	
	/**
	 * reset the write position the the saved mark
	 * 
	 * @return true, if the write position has been marked  
	 */
	public boolean resetToWriteMark() {
		if (isWriteMarked) {
			writeMarkBuffer.resetWritePosition();
			return true;

		} else {
			return false;
		}
	}

	
	
	@Override
	protected Object clone() throws CloneNotSupportedException {
		WriteQueue copy = new WriteQueue();
		copy.queue.append(this.queue.copy());
	
		if (this.writeMarkBuffer != null) {
			copy.writeMarkBuffer = (RewriteableBuffer) this.writeMarkBuffer.clone();
		}
		
		return copy;
	}

	
	/**
	 * {@inheritDoc}
	 */
	public String toString(String encoding) {
		return queue.toString(encoding);
	}
	
	
	
	
	
	/**
	 * {@inheritDoc}
	 */
	@Override
	public String toString() {
		return queue.toString();
	}
	
	
	

	
	private static final class RewriteableBuffer implements Cloneable {
		private ArrayList bufs = new ArrayList();
		private int writePosition = 0;
		
		
		public void append(ByteBuffer buffer) {
			
			if (buffer.remaining() < 1) {
				return;
			}
			
			if (writePosition == bufs.size()) {
				bufs.add(buffer);
				writePosition++;
				
			} else {
				ByteBuffer currentBuffer = bufs.remove(writePosition);
				
				if (currentBuffer.remaining() == buffer.remaining()) {
					bufs.add(writePosition, buffer);
					writePosition++;
					
				} else if (currentBuffer.remaining() > buffer.remaining()) {
					currentBuffer.position(currentBuffer.position() + buffer.remaining());
					bufs.add(writePosition, currentBuffer);
					bufs.add(writePosition, buffer);
					writePosition++;
					
				} else { // currentBuffer.remaining() < buffer.remaining()
					bufs.add(writePosition, buffer);
					writePosition++;
					
					int bytesToRemove = buffer.remaining() - currentBuffer.remaining();
					while (bytesToRemove > 0) {
						// does tailing buffers exits?
						if (writePosition < bufs.size()) {
							
							ByteBuffer buf = bufs.remove(writePosition);
							if (buf.remaining() > bytesToRemove) {
								buf.position(buf.position() + bytesToRemove);
								bufs.add(writePosition, buf);
							} else {
								bytesToRemove -= buf.remaining();
							}
							
						// ...no
						} else {
							bytesToRemove = 0;
						}
					}
				}
				
			}	
		}
	
		public void resetWritePosition() {
			writePosition = 0;
		}
		
	
		public ByteBuffer[] drain() {
			ByteBuffer[] result = bufs.toArray(new ByteBuffer[bufs.size()]);
			bufs.clear();
			writePosition = 0;
			
			return result;
		}
		
		
		public int size() {
			int size = 0;
			for (ByteBuffer buffer : bufs) {
				size += buffer.remaining();
			}
			
			return size;
		}
		
		@Override
		protected Object clone() throws CloneNotSupportedException {
			RewriteableBuffer copy = (RewriteableBuffer) super.clone();
			
			copy.bufs = new ArrayList();
			for (ByteBuffer buffer : this.bufs) {
				copy.bufs.add(buffer.duplicate());
			}

			return copy;
		}
	}
	
	
	
	private static final class Queue implements ISink {

		private ByteBuffer[] buffers;

		
		/**
		 * clean the queue
		 */
		public synchronized void reset() {
			buffers = null;
		}
	
		

		/**
		 * returns true, if empty
		 *
		 * @return true, if empty
		 */
		public synchronized boolean isEmpty() {
			return empty();
		}
		

		private boolean empty() {
			return (buffers == null);
		}
			

		/**
		 * return the current size
		 *
		 * @return  the current size
		 */
		public synchronized int getSize() {
			if (empty()) {
				return 0;
				
			} else {
				int size = 0;
				if (buffers != null) {
					for (int i = 0; i < buffers.length; i++) {
						if (buffers[i] != null) {
							size += buffers[i].remaining();
						}
					}
				}
				
				return size;
			}
		}
		
		
		public synchronized void append(ByteBuffer data) {			
			if (buffers == null) {
				buffers = new ByteBuffer[1];
				buffers[0] = data;
									
			} else {
				ByteBuffer[] newBuffers = new ByteBuffer[buffers.length + 1];
				System.arraycopy(buffers, 0, newBuffers, 0, buffers.length);
				newBuffers[buffers.length] = data;
				buffers = newBuffers;
			}
		}
		
		
		/**
		 * append a list of byte buffer to this queue. By adding a list,
		 * the list becomes part of to the buffer, and should not be modified outside the buffer
		 * to avoid side effects
		 *
		 * @param bufs  the list of ByteBuffer
		 */
		public synchronized void append(ByteBuffer[] bufs) {
			if (buffers == null) {
				buffers = bufs;
										
			}  else {
				ByteBuffer[] newBuffers = new ByteBuffer[buffers.length + bufs.length];
				System.arraycopy(buffers, 0, newBuffers, 0, buffers.length);
				System.arraycopy(bufs, 0, newBuffers, buffers.length, bufs.length);
				buffers = newBuffers;
			}
		}
		


		/**
		 * drain the queue
		 *
		 * @return the queue content
		 */
		public synchronized ByteBuffer[] drain() {
			ByteBuffer[] result = buffers;
			buffers = null;
						
			return result;
		}
		
		
		
		public synchronized ByteBuffer[] copy()  {
			return ConnectionUtils.copy(buffers);
		}
		
		
		
		/**
		 * {@inheritDoc}
		 */
		@Override
		public String toString() {
			return toString("US-ASCII");
		}
		
		
		/**
		 * {@inheritDoc}
		 */
		public synchronized String toString(String encoding) {
			StringBuilder sb = new StringBuilder();
			if (buffers != null) {
				ByteBuffer[] copy = new ByteBuffer[buffers.length];
				try {
					for (int i = 0; i < copy.length; i++) {
						if (buffers[i] != null) {
							copy[i] = buffers[i].duplicate();
						}
					}
					sb.append(DataConverter.toString(copy, encoding, Integer.MAX_VALUE));
					
				} catch (UnsupportedEncodingException use) { 
					sb.append(DataConverter.toHexString(copy, Integer.MAX_VALUE));
				}
			}

			return sb.toString();
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy