X-Git-Url: http://dolda2000.com/gitweb/?a=blobdiff_plain;f=src%2Fjrw%2Futil%2FLazyPChannel.java;fp=src%2Fjrw%2Futil%2FLazyPChannel.java;h=222039133fc3ac08503a8377b7120007dd114fa1;hb=6e0043cc3f99a31bac74d6d0c7399e4c0b60d3fb;hp=0000000000000000000000000000000000000000;hpb=ad84ba3b456f1a9858fcfe1fee28b81197654c8d;p=jrw.git diff --git a/src/jrw/util/LazyPChannel.java b/src/jrw/util/LazyPChannel.java new file mode 100644 index 0000000..2220391 --- /dev/null +++ b/src/jrw/util/LazyPChannel.java @@ -0,0 +1,136 @@ +package jrw.util; + +import jrw.*; +import java.nio.*; +import java.nio.channels.*; +import java.nio.charset.*; + +public abstract class LazyPChannel implements ReadableByteChannel { + private ByteBuffer curbuf = null; + private boolean eof = false; + private CharsetEncoder enc = null; + private Runnable rem = null; + + protected boolean write(byte[] data, int off, int len) { + if(rem != null) throw(new IllegalStateException("buffer filled")); + int t = Math.min(curbuf.remaining(), len); + curbuf.put(data, off, t); + if(len > t) { + rem = () -> write(data, off + t, len - t); + return(true); + } + return(false); + } + protected boolean write(byte[] data) {return(write(data, 0, data.length));} + + protected boolean write(CharBuffer buf) { + if(rem != null) throw(new IllegalStateException("buffer filled")); + if(enc == null) + enc = charset().newEncoder(); + while(true) { + int pp = buf.position(); + CoderResult res = enc.encode(buf, curbuf, false); + if(buf.remaining() == 0) + return(false); + if(res.isUnderflow()) { + if(pp == buf.position()) { + /* XXX? Not sure if this can be expected to + * happen. I'm not aware of any charsets that should + * require it, and it would complicate the design + * significantly. */ + throw(new RuntimeException("encoder not consuming input")); + } + } else if(res.isOverflow()) { + rem = () -> write(buf); + return(true); + } else { + try { + res.throwException(); + } catch(CharacterCodingException e) { + throw(new RuntimeException(e)); + } + } + } + } + + protected boolean write(CharSequence chars) { + CharBuffer buf = (chars instanceof CharBuffer) ? ((CharBuffer)chars).duplicate() : CharBuffer.wrap(chars); + return(write(buf)); + } + + private void encflush2() { + while(true) { + CoderResult res = enc.flush(curbuf); + if(res.isOverflow()) { + rem = this::encflush1; + return; + } else if(res.isUnderflow()) { + return; + } else { + try { + res.throwException(); + } catch(CharacterCodingException e) { + throw(new RuntimeException(e)); + } + } + } + } + + private void encflush1() { + CharBuffer empty = CharBuffer.wrap(""); + while(true) { + CoderResult res = enc.encode(empty, curbuf, true); + if(res.isOverflow()) { + rem = this::encflush1; + return; + } else if(res.isUnderflow()) { + rem = this::encflush2; + return; + } else { + try { + res.throwException(); + } catch(CharacterCodingException e) { + throw(new RuntimeException(e)); + } + } + } + } + + private void encflush() { + if(enc != null) + rem = this::encflush1; + } + + protected Charset charset() {return(Http.UTF8);} + + protected abstract boolean produce(); + + public int read(ByteBuffer buf) { + curbuf = buf; + try { + int op = buf.position(); + while(buf.remaining() > 0) { + Runnable rem = this.rem; + this.rem = null; + if(rem != null) { + rem.run(); + } else { + if(eof) { + break; + } else if(produce()) { + encflush(); + eof = true; + } + } + } + if(eof && (buf.position() == op)) + return(-1); + return(buf.position() - op); + } finally { + curbuf = null; + } + } + + public void close() {} + public boolean isOpen() {return(true);} +}