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:
parent
eaf8e46570
commit
4c455e4f9b
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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"));
|
Loading…
Reference in New Issue