package com.gmail.woodyc40.common.protocol; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import javax.annotation.concurrent.ThreadSafe; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; /** * Wraps a bytebuffer which is used by the pipeline internally to send or decode bytes * * <p>Each segment is written individually</p> * * @author Pierre C */ @ThreadSafe public class ByteAppender { /** The underlying bytebuf to append to */ private final ConcurrentLinkedQueue<ByteBuf> bufs = new ConcurrentLinkedQueue<>(); /** * Creates a new byte appender * * @param buf the buffer to wrap */ private ByteAppender(ByteBuf buf) { bufs.add(buf); } /** * Wraps a bytebuf with a new appender * * @param buf the buffer to wrap * @return the new byte appender */ public static ByteAppender wrap(ByteBuf buf) { return new ByteAppender(buf); } /** * Creates a new empty byte appender * * @return the new byte appender */ public static ByteAppender empty() { return wrap(Unpooled.directBuffer()); } /** * Separates the last byte entries and creates a new entry for subsequent bytes */ public void segment() { bufs.add(Unpooled.directBuffer()); } //////////////////////////////// APPENDING //////////////////////////////// /** * Appends a byte array to the buffer * * @param bytes the bytes to append */ public void append(byte[] bytes) { bufs.peek().writeBytes(bytes); } /** * Appends a byte to the buffer * * @param by the byte to append */ public void append(byte by) { bufs.peek().writeByte(by); } /** * Appends a byte buffer to the buffer * * @param buf the bytes to append */ public void append(ByteBuf buf) { bufs.peek().writeBytes(buf); } /** * Appends a byte array to the buffer * * @param stream the bytes to append */ public void append(ByteArrayOutputStream stream) { bufs.peek().writeBytes(stream.toByteArray()); } /** * Appends the btyes from the given appender to this appender * * @param appender the appender to grab the bytes from */ public void append(ByteAppender appender) { for (ByteBuf byteBuf : appender.bufs) { segment(); append(byteBuf); } } //////////////////////////////// WRITING TO //////////////////////////////// /** * Writes the buffer to another buffer * * @param buf the buffer to write to */ public void writeTo(ByteBuf buf) { for (ByteBuf b : bufs) { buf.writeBytes(b); } } /** * Writes the buffer to the stream * * @param stream the stream to write to */ public void writeTo(ByteArrayOutputStream stream) { try { for (ByteBuf b : bufs) { stream.write(b.array()); } } catch (IOException e) { e.printStackTrace(); } } /** * Writes and flushes the buffer's array to the channel * * @param ctx the channel to write to */ public void writeTo(Channel ctx) { for (ByteBuf b : bufs) { ctx.writeAndFlush(b.array()); } } //////////////////////////////// COPYING //////////////////////////////// /** * Returns the original internal buffer (non-array) * * @return the original buffer that was wrapped */ public Queue<ByteBuf> wrapped() { return bufs; } /** * Copies the internal bytes to an array * * @return the copy of the byte array */ public byte[] copy() { ByteArrayOutputStream stream = new ByteArrayOutputStream(); writeTo(stream); return stream.toByteArray(); } }