/* * JLibs: Common Utilities for Java * Copyright (C) 2009 Santhosh Kumar T <santhosh.tekuri@gmail.com> * * 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. */ package jlibs.nio.util; import jlibs.core.io.IOUtil; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.util.Arrays; import java.util.NoSuchElementException; /** * @author Santhosh Kumar Tekuri */ public final class Buffers extends OutputStream{ public ByteBuffer array[]; public int offset; public int length; public Buffers(ByteBuffer array[], int offset, int length){ this.array = array; this.offset = offset; this.length = length; } public Buffers(){ this(new ByteBuffer[10], 0, 0); } public void append(ByteBuffer buffer){ if(offset+length>=array.length){ if(offset!=0) System.arraycopy(array, offset, array, 0, length-1); else array = Arrays.copyOf(array, 2*array.length); offset = 0; } array[offset+length] = buffer; ++length; } public void append(Buffers buffers){ if(offset+length+buffers.length>=array.length){ array = Arrays.copyOf(array, length+buffers.length); offset = 0; } System.arraycopy(buffers.array, buffers.offset, array, length, buffers.length); length += buffers.length; } public ByteBuffer remove(){ if(length==0) throw new NoSuchElementException(); ByteBuffer buffer = array[offset]; array[offset] = null; --length; if(length==0) offset = 0; else ++offset; return buffer; } public ByteBuffer peek(){ if(length==0) throw new NoSuchElementException(); return array[offset]; } public ByteBuffer removeLast(){ if(length==0) throw new NoSuchElementException(); --length; ByteBuffer buffer = array[offset+length]; array[offset+length] = null; return buffer; } public ByteBuffer peekLast(){ if(length==0) throw new NoSuchElementException(); return array[offset+length-1]; } public void removeEmpty(BufferAllocator allocator){ while(length>0){ if(array[offset].hasRemaining()) break; else{ allocator.free(array[offset]); ++offset; --length; } } } public long remaining(){ long remaining = 0; for(int i=0; i<length; i++) remaining += array[offset+i].remaining(); return remaining; } public boolean hasRemaining(){ for(int i=0; i<length; i++){ if(array[i].hasRemaining()) return true; } return false; } public Buffers copy(){ ByteBuffer array[] = new ByteBuffer[length]; for(int i=0; i<array.length; i++) array[i] = this.array[offset+i].duplicate(); return new Buffers(array, 0, array.length); } @Override public void write(int b){ write((byte)b); } @Override public void write(byte[] bytes){ write(bytes, 0, bytes.length); } @Override public void write(byte[] bytes, int offset, int length){ if(length>0){ ByteBuffer buffer = this.length==0 ? null : array[this.offset+this.length-1]; do{ if(buffer==null || buffer.limit()==buffer.capacity()) append(buffer=BufferAllocator.current().allocate()); else NIOUtil.compact(buffer); int min = Math.min(length, buffer.remaining()); buffer.put(bytes, offset, min); buffer.flip(); offset += min; length -= min; buffer = null; }while(length>0); } } public void write(byte b){ ByteBuffer buffer = this.length==0 ? null : array[this.offset+this.length-1]; if(buffer==null || buffer.limit()==buffer.capacity()) append(buffer=BufferAllocator.current().allocate()); else NIOUtil.compact(buffer); buffer.put(b); buffer.flip(); } public void write(ByteBuffer src){ ByteBuffer buffer = this.length==0 ? null : array[this.offset+this.length-1]; while(src.hasRemaining()){ if(buffer==null || buffer.limit()==buffer.capacity()) append(buffer=BufferAllocator.current().allocate()); else NIOUtil.compact(buffer); int min = Math.min(src.remaining(), buffer.remaining()); int srcLimit = src.limit(); src.limit(src.position()+min); buffer.put(src); src.limit(srcLimit); buffer.flip(); buffer = null; } } public void write(String ascii){ int len = ascii.length(); int i = 0; ByteBuffer buffer = this.length==0 ? null : array[this.offset+this.length-1]; while(i<len){ if(buffer==null || buffer.limit()==buffer.capacity()) append(buffer=BufferAllocator.current().allocate()); else NIOUtil.compact(buffer); while(i<len && buffer.hasRemaining()) buffer.put((byte)ascii.charAt(i++)); buffer.flip(); buffer = null; } } public void writeTo(OutputStream out) throws IOException{ ByteBuffer temp = null; try{ for(int i=0; i<length; i++){ ByteBuffer buffer = array[offset+i]; if(buffer.hasRemaining()){ if(buffer.hasArray()) out.write(buffer.array(), buffer.arrayOffset()+buffer.position(), buffer.remaining()); else{ if(temp==null) temp = BufferAllocator.current().allocateHeap(); int bufferPos = buffer.position(); int bufferLimit = buffer.limit(); try{ while(buffer.hasRemaining()){ temp.clear(); int min = Math.min(buffer.remaining(), temp.remaining()); buffer.limit(buffer.position()+min); temp.put(buffer); buffer.limit(bufferLimit); temp.flip(); out.write(temp.array(), 0, temp.remaining()); } }finally{ buffer.position(bufferPos); buffer.limit(bufferLimit); } } } } }finally{ if(temp!=null) BufferAllocator.current().free(temp); } } public class Input extends InputStream{ int i = 0; ByteBuffer buffer = length==0 ? null : array[offset].duplicate(); @Override public int read() throws IOException{ byte b[] = new byte[1]; int len = read(b, 0, 1); return len==1 ? (b[0]&0xff) : -1; } @Override public int read(byte[] bytes, int offset, int length) throws IOException{ if(buffer==null) return -1; int _length = length; while(buffer!=null && length>0){ int min = Math.min(length, buffer.remaining()); buffer.get(bytes, offset, min); offset += min; length -= min; if(buffer.hasRemaining()) break; else{ ++i; if(i==Buffers.this.length) buffer = null; else buffer = array[offset+i].duplicate(); } } return length==_length && buffer==null ? -1 : _length-length; } @Override public int available() throws IOException{ if(buffer==null) return 0; int available = buffer.remaining(); for(int j=i+1; j<length; j++) available += array[offset+j].remaining(); return available; } } public int read(ByteBuffer dst, BufferAllocator allocator){ int read = 0; while(length>0){ ByteBuffer src = array[offset]; read += NIOUtil.copy(src, dst); if(!src.hasRemaining()) allocator.free(remove()); if(!dst.hasRemaining()) break; } return read==0 && length==0 ? -1 : 0; } public String toString(Charset charset) throws IOException{ BytesDecoder decoder = new BytesDecoder(new StringBuilder(), charset.newDecoder(), 1024); ByteBuffer temp = null; for(int i=0; i<length; i++){ ByteBuffer buffer = array[offset+i].duplicate(); if(temp!=null && temp.hasRemaining()){ do{ temp.compact(); int min = Math.min(temp.remaining(), buffer.remaining()); int bufferLimit = buffer.limit(); buffer.limit(buffer.position()+min); temp.put(buffer); buffer.limit(bufferLimit); temp.flip(); decoder.write(temp, i==length-1 && !buffer.hasRemaining()); }while(buffer.hasRemaining()); }else{ decoder.write(buffer, i==length-1); if(buffer.hasRemaining()){ if(temp==null) temp = BufferAllocator.current().allocate(); temp.put(buffer); temp.flip(); } } } return decoder.appendable.toString(); } public String toString(){ try{ return toString(IOUtil.ISO_8859_1); }catch(IOException ex){ throw new RuntimeException(ex); } } }