JSR-356 - cleaning up Writer support

+ Eliminating AppendableByteBuffer in favor of direct CharBuffer use
+ Renaming Utf8ByteBuffer to Utf8CharBuffer to better suit usage
This commit is contained in:
Joakim Erdfelt 2013-05-28 15:09:48 -07:00
parent eaf8e46570
commit 4c455e4f9b
6 changed files with 164 additions and 192 deletions

View File

@ -1,100 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.common.message;
import java.io.IOException;
import java.nio.ByteBuffer;
import org.eclipse.jetty.util.BufferUtil;
/**
* Wrap a {@link ByteBuffer} to support {@link Appendable}.
* <p>
* Used by {@link Utf8ByteBuffer}
*/
public class AppendableByteBuffer implements Appendable
{
private final ByteBuffer buffer;
public AppendableByteBuffer(ByteBuffer buffer)
{
this.buffer = buffer;
}
@Override
public Appendable append(char c) throws IOException
{
buffer.put((byte)c);
return this;
}
@Override
public Appendable append(CharSequence csq) throws IOException
{
if (csq == null)
{
append("null");
return this;
}
int len = csq.length();
return this.append(csq,0,len);
}
@Override
public Appendable append(CharSequence csq, int start, int end) throws IOException
{
if (csq == null)
{
append("null");
return this;
}
for (int idx = start; idx < end; idx++)
{
buffer.put((byte)csq.charAt(idx));
}
return this;
}
/**
* Clear the buffer data, reset to position 0, ready to fill with more data.
*/
public void clear()
{
BufferUtil.clearToFill(buffer);
}
/**
* Get a {@link ByteBuffer#slice()} view of the buffer data.
*
* @return the buffer data, in slice form.
*/
public ByteBuffer getBufferSlice()
{
BufferUtil.flipToFlush(buffer,0);
ByteBuffer slice = buffer.slice();
BufferUtil.flipToFill(buffer);
return slice;
}
public int remaining()
{
return buffer.remaining();
}
}

View File

@ -158,10 +158,15 @@ public class MessageOutputStream extends OutputStream
int offset = off; // offset within provided array
while (left > 0)
{
LOG.debug("buffer: {}",BufferUtil.toDetailString(buffer));
if (LOG.isDebugEnabled())
{
LOG.debug("buffer: {}",BufferUtil.toDetailString(buffer));
}
int space = buffer.remaining();
assert (space > 0);
int size = Math.min(space,left);
buffer.put(b,offset,size);
assert (size > 0);
left -= size; // decrement bytes left
if (left > 0)
{

View File

@ -44,7 +44,7 @@ public class MessageWriter extends Writer
private long frameCount = 0;
private WebSocketFrame frame;
private ByteBuffer buffer;
private Utf8ByteBuffer utf;
private Utf8CharBuffer utf;
private FutureWriteCallback blocker;
private boolean closed = false;
@ -53,8 +53,8 @@ public class MessageWriter extends Writer
this.outgoing = outgoing;
this.bufferPool = bufferPool;
this.buffer = bufferPool.acquire(bufferSize,true);
this.utf = Utf8ByteBuffer.wrap(buffer);
BufferUtil.flipToFill(buffer);
this.utf = Utf8CharBuffer.wrap(buffer);
this.frame = new WebSocketFrame(OpCode.TEXT);
}
@ -75,27 +75,23 @@ public class MessageWriter extends Writer
public synchronized void close() throws IOException
{
assertNotClosed();
LOG.debug("close()");
// finish sending whatever in the buffer with FIN=true
flush(true);
// close stream
LOG.debug("Sent Frame Count: {}",frameCount);
closed = true;
bufferPool.release(buffer);
LOG.debug("closed");
LOG.debug("closed (frame count={})",frameCount);
}
@Override
public void flush() throws IOException
{
LOG.debug("flush()");
assertNotClosed();
// flush whatever is in the buffer with FIN=false
flush(false);
LOG.debug("flushed");
}
/**
@ -107,8 +103,7 @@ public class MessageWriter extends Writer
*/
private synchronized void flush(boolean fin) throws IOException
{
ByteBuffer data = utf.getBuffer();
LOG.debug("flush({}): {}",fin,BufferUtil.toDetailString(data));
ByteBuffer data = utf.getByteBuffer();
frame.setPayload(data);
frame.setFin(fin);
@ -149,21 +144,21 @@ public class MessageWriter extends Writer
@Override
public void write(char[] cbuf) throws IOException
{
LOG.debug("write(char[{}])",cbuf.length);
this.write(cbuf,0,cbuf.length);
}
@Override
public void write(char[] cbuf, int off, int len) throws IOException
{
LOG.debug("write(char[{}], {}, {})",cbuf.length,off,len);
assertNotClosed();
int left = len; // bytes left to write
int offset = off; // offset within provided array
while (left > 0)
{
LOG.debug("buffer: {}",BufferUtil.toDetailString(buffer));
int space = utf.length();
int space = utf.remaining();
int size = Math.min(space,left);
assert (space > 0);
assert (size > 0);
utf.append(cbuf,offset,size); // append with utf logic
left -= size; // decrement char left
if (left > 0)
@ -180,8 +175,8 @@ public class MessageWriter extends Writer
assertNotClosed();
// buffer up to limit, flush once buffer reached.
utf.append((byte)c); // append with utf logic
if (utf.length() <= 0)
utf.append(c); // append with utf logic
if (utf.remaining() <= 0)
{
flush(false);
}

View File

@ -1,66 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.common.message;
import java.nio.ByteBuffer;
import org.eclipse.jetty.util.Utf8Appendable;
public class Utf8ByteBuffer extends Utf8Appendable
{
public static Utf8ByteBuffer wrap(ByteBuffer buffer)
{
AppendableByteBuffer abuffer = new AppendableByteBuffer(buffer);
return new Utf8ByteBuffer(abuffer);
}
private final AppendableByteBuffer abb;
public Utf8ByteBuffer(AppendableByteBuffer buffer)
{
super(buffer);
this.abb = buffer;
}
public void append(char[] cbuf, int offset, int size)
{
int start = offset;
int end = offset + size;
for (int i = start; i < end; i++)
{
append((byte)(cbuf[i] & 0xFF));
}
}
public void clear()
{
abb.clear();
}
public ByteBuffer getBuffer()
{
return abb.getBufferSlice();
}
@Override
public int length()
{
return abb.remaining();
}
}

View File

@ -0,0 +1,107 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.common.message;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.Utf8Appendable;
/**
* A CharBuffer wrapped with the Utf8Appendable logic.
*/
public class Utf8CharBuffer extends Utf8Appendable
{
private static final Charset UTF8 = StringUtil.__UTF8_CHARSET;
/**
* Convenience method to wrap a ByteBuffer with a {@link Utf8CharBuffer}
*
* @param buffer
* the buffer to wrap
* @return the Utf8ByteBuffer for the provided ByteBuffer
*/
public static Utf8CharBuffer wrap(ByteBuffer buffer)
{
return new Utf8CharBuffer(buffer.asCharBuffer());
}
private final CharBuffer buffer;
private Utf8CharBuffer(CharBuffer buffer)
{
super(buffer);
this.buffer = buffer;
}
public void append(char[] cbuf, int offset, int size)
{
append(BufferUtil.toDirectBuffer(new String(cbuf,offset,size),UTF8));
}
public void append(int c)
{
buffer.append((char)c);
}
public void clear()
{
buffer.position(0);
buffer.limit(buffer.capacity());
}
public ByteBuffer getByteBuffer()
{
// flip to flush
buffer.limit(buffer.position());
buffer.position(0);
// get byte buffer
ByteBuffer bb = UTF8.encode(buffer);
return bb;
}
@Override
public int length()
{
return buffer.capacity();
}
public int remaining()
{
return buffer.remaining();
}
@Override
public String toString()
{
StringBuilder str = new StringBuilder();
str.append("Utf8CharBuffer@").append(hashCode());
str.append("[p=").append(buffer.position());
str.append(",l=").append(buffer.limit());
str.append(",c=").append(buffer.capacity());
str.append(",r=").append(buffer.remaining());
str.append("]");
return str.toString();
}
}

View File

@ -27,7 +27,7 @@ import org.eclipse.jetty.util.StringUtil;
import org.junit.Assert;
import org.junit.Test;
public class Utf8ByteBufferTest
public class Utf8CharBufferTest
{
private static String asString(ByteBuffer buffer)
{
@ -43,15 +43,15 @@ public class Utf8ByteBufferTest
public void testAppendGetAppendGet()
{
ByteBuffer buf = ByteBuffer.allocate(64);
Utf8ByteBuffer utf = Utf8ByteBuffer.wrap(buf);
Utf8CharBuffer utf = Utf8CharBuffer.wrap(buf);
byte hellobytes[] = asUTF("Hello ");
byte worldbytes[] = asUTF("World!");
utf.append(hellobytes, 0, hellobytes.length);
ByteBuffer hellobuf = utf.getBuffer();
ByteBuffer hellobuf = utf.getByteBuffer();
utf.append(worldbytes, 0, worldbytes.length);
ByteBuffer worldbuf = utf.getBuffer();
ByteBuffer worldbuf = utf.getByteBuffer();
Assert.assertThat("Hello buffer",asString(hellobuf),is("Hello "));
Assert.assertThat("World buffer",asString(worldbuf),is("Hello World!"));
@ -60,33 +60,64 @@ public class Utf8ByteBufferTest
@Test
public void testAppendGetClearAppendGet()
{
int bufsize = 64;
ByteBuffer buf = ByteBuffer.allocate(64);
Utf8ByteBuffer utf = Utf8ByteBuffer.wrap(buf);
Utf8CharBuffer utf = Utf8CharBuffer.wrap(buf);
int expectedSize = bufsize / 2;
Assert.assertThat("Remaining (initial)",utf.remaining(),is(expectedSize));
byte hellobytes[] = asUTF("Hello World");
utf.append(hellobytes,0,hellobytes.length);
ByteBuffer hellobuf = utf.getBuffer();
ByteBuffer hellobuf = utf.getByteBuffer();
Assert.assertThat("Remaining (after append)",utf.remaining(),is(expectedSize - hellobytes.length));
Assert.assertThat("Hello buffer",asString(hellobuf),is("Hello World"));
utf.clear();
Assert.assertThat("Remaining (after clear)",utf.remaining(),is(expectedSize));
byte whatnowbytes[] = asUTF("What Now?");
utf.append(whatnowbytes,0,whatnowbytes.length);
ByteBuffer whatnowbuf = utf.getBuffer();
ByteBuffer whatnowbuf = utf.getByteBuffer();
Assert.assertThat("Remaining (after 2nd append)",utf.remaining(),is(expectedSize - whatnowbytes.length));
Assert.assertThat("What buffer",asString(whatnowbuf),is("What Now?"));
}
@Test
public void testAppendUnicodeGetBuffer()
{
ByteBuffer buf = ByteBuffer.allocate(64);
Utf8CharBuffer utf = Utf8CharBuffer.wrap(buf);
byte bb[] = asUTF("Hello A\u00ea\u00f1\u00fcC");
utf.append(bb,0,bb.length);
ByteBuffer actual = utf.getByteBuffer();
Assert.assertThat("Buffer length should be retained",actual.remaining(),is(bb.length));
Assert.assertThat("Message",asString(actual),is("Hello A\u00ea\u00f1\u00fcC"));
}
@Test
public void testSimpleGetBuffer()
{
ByteBuffer buf = ByteBuffer.allocate(64);
Utf8ByteBuffer utf = Utf8ByteBuffer.wrap(buf);
int bufsize = 64;
ByteBuffer buf = ByteBuffer.allocate(bufsize);
Utf8CharBuffer utf = Utf8CharBuffer.wrap(buf);
int expectedSize = bufsize / 2;
Assert.assertThat("Remaining (initial)",utf.remaining(),is(expectedSize));
byte bb[] = asUTF("Hello World");
utf.append(bb,0,bb.length);
ByteBuffer actual = utf.getBuffer();
expectedSize -= bb.length;
Assert.assertThat("Remaining (after append)",utf.remaining(),is(expectedSize));
ByteBuffer actual = utf.getByteBuffer();
Assert.assertThat("Buffer length",actual.remaining(),is(bb.length));
Assert.assertThat("Message",asString(actual),is("Hello World"));