package redone.net; /** * A mutable sequence of bytes used to construct the immutable * Packet objects. By default, methods use big endian byte * ordering. */ public class StaticPacketBuilder implements PacketBuilder { /** * Default capacity */ private static final int DEFAULT_SIZE = 32; /** * The payload buffer */ private byte[] payload; /** * Current number of bytes used in the buffer */ private int curLength; /** * ID of the packet */ private int id; /** * Current index into the buffer by bits */ private int bitPosition = 0; private Packet.Size size = Packet.Size.Fixed; /** * Whether this packet does not use the standard packet header */ private boolean bare = false; /** * Bitmasks for addBits() */ private static int bitmasks[] = { 0, 0x1, 0x3, 0x7, 0xf, 0x1f, 0x3f, 0x7f, 0xff, 0x1ff, 0x3ff, 0x7ff, 0xfff, 0x1fff, 0x3fff, 0x7fff, 0xffff, 0x1ffff, 0x3ffff, 0x7ffff, 0xfffff, 0x1fffff, 0x3fffff, 0x7fffff, 0xffffff, 0x1ffffff, 0x3ffffff, 0x7ffffff, 0xfffffff, 0x1fffffff, 0x3fffffff, 0x7fffffff, -1 }; /** * Constructs a packet builder with no data and an initial capacity of * DEFAULT_SIZE. * * @see DEFAULT_SIZE */ public StaticPacketBuilder() { this(DEFAULT_SIZE); } public byte[] getPayload() { return payload; } /** * Constructs a packet builder with no data and an initial capacity of * capacity. * * @param capacity * The initial capacity of the buffer */ public StaticPacketBuilder(int capacity) { payload = new byte[capacity]; } /** * Ensures that the buffer is at least minimumBytes bytes. * * @param minimumCapacity * The size needed */ private void ensureCapacity(int minimumCapacity) { if (minimumCapacity >= payload.length) { expandCapacity(minimumCapacity); } } /** * Expands the buffer to the specified size. * * @param minimumCapacity * The minimum capacity to which to expand * @see java.lang.AbstractStringBuilder#expandCapacity(int) */ private void expandCapacity(int minimumCapacity) { int newCapacity = (payload.length + 1) * 2; if (newCapacity < 0) { newCapacity = Integer.MAX_VALUE; } else if (minimumCapacity > newCapacity) { newCapacity = minimumCapacity; } byte[] newPayload = new byte[newCapacity]; try { while (curLength > payload.length) { curLength--; } System.arraycopy(payload, 0, newPayload, 0, curLength); } catch (Exception e) { } payload = newPayload; } /** * Sets this packet as bare. A bare packet will contain only the payload * data, rather than having the standard packet header prepended. * * @param bare * Whether this packet is to be sent bare */ public StaticPacketBuilder setBare(boolean bare) { this.bare = bare; return this; } /** * Sets the ID for this packet. * * @param id * The ID of the packet */ public StaticPacketBuilder setId(int id) { this.id = id; return this; } public StaticPacketBuilder setSize(Packet.Size s) { size = s; return this; } public StaticPacketBuilder initBitAccess() { bitPosition = curLength * 8; return this; } public StaticPacketBuilder finishBitAccess() { curLength = (bitPosition + 7) / 8; return this; } /** * TODO needs a proper description. */ public StaticPacketBuilder addBits(int numBits, int value) { int bytePos = bitPosition >> 3; int bitOffset = 8 - (bitPosition & 7); bitPosition += numBits; curLength = (bitPosition + 7) / 8; ensureCapacity(curLength); for (; numBits > bitOffset; bitOffset = 8) { payload[bytePos] &= ~bitmasks[bitOffset]; // mask out the desired // area payload[bytePos++] |= value >> numBits - bitOffset & bitmasks[bitOffset]; numBits -= bitOffset; } if (numBits == bitOffset) { payload[bytePos] &= ~bitmasks[bitOffset]; payload[bytePos] |= value & bitmasks[bitOffset]; } else { payload[bytePos] &= ~(bitmasks[numBits] << bitOffset - numBits); payload[bytePos] |= (value & bitmasks[numBits]) << bitOffset - numBits; } return this; } /** * Adds the contents of byte array data to the * packet. The size of this packet will grow by the length of the provided * array. * * @param data * The bytes to add to this packet * @return A reference to this object */ public StaticPacketBuilder addBytes(byte[] data) { return addBytes(data, 0, data.length); } /** * Adds the contents of byte array data, starting * at index offset. The size of this packet will grow by * len bytes. * * @param data * The bytes to add to this packet * @param offset * The index of the first byte to append * @param len * The number of bytes to append * @return A reference to this object */ public StaticPacketBuilder addBytes(byte[] data, int offset, int len) { int newLength = curLength + len; ensureCapacity(newLength); System.arraycopy(data, offset, payload, curLength, len); curLength = newLength; return this; } public StaticPacketBuilder addLEShortA(int i) { ensureCapacity(curLength + 2); addByte((byte) (i + 128), false); addByte((byte) (i >> 8), false); return this; } public StaticPacketBuilder addShortA(int i) { ensureCapacity(curLength + 2); addByte((byte) (i >> 8), false); addByte((byte) (i + 128), false); return this; } /** * Adds a byte to the data buffer. The size of this packet will * grow by one byte. * * @param val * The byte value to add * @return A reference to this object */ public StaticPacketBuilder addByte(byte val) { return addByte(val, true); } public StaticPacketBuilder addByteA(int i) { return addByte((byte) (i + 128), true); } /** * Adds a byte to the data buffer. The size of this packet will * grow by one byte. * * @param val * The byte value to add * @param checkCapacity * Whether the buffer capacity should be checked * @return A reference to this object */ private StaticPacketBuilder addByte(byte val, boolean checkCapacity) { if (checkCapacity) { ensureCapacity(curLength + 1); } payload[curLength++] = val; return this; } /** * Adds a short to the data stream. The size of this packet * will grow by two bytes. * * @param val * The short value to add * @return A reference to this object */ public StaticPacketBuilder addShort(int val) { ensureCapacity(curLength + 2); addByte((byte) (val >> 8), false); addByte((byte) val, false); return this; } public StaticPacketBuilder addLEShort(int val) { ensureCapacity(curLength + 2); addByte((byte) val, false); addByte((byte) (val >> 8), false); return this; } public StaticPacketBuilder setShort(int val, int offset) { payload[offset++] = (byte) (val >> 8); payload[offset++] = (byte) val; if (curLength < offset + 2) { curLength += 2; } return this; } /** * Adds a int to the data stream. The size of this packet will * grow by four bytes. * * @param val * The int value to add * @return A reference to this object */ public StaticPacketBuilder addInt(int val) { ensureCapacity(curLength + 4); addByte((byte) (val >> 24), false); addByte((byte) (val >> 16), false); addByte((byte) (val >> 8), false); addByte((byte) val, false); return this; } public StaticPacketBuilder addInt1(int val) { ensureCapacity(curLength + 4); addByte((byte) (val >> 8), false); addByte((byte) val, false); addByte((byte) (val >> 24), false); addByte((byte) (val >> 16), false); return this; } public StaticPacketBuilder addInt2(int val) { ensureCapacity(curLength + 4); addByte((byte) (val >> 16), false); addByte((byte) (val >> 24), false); addByte((byte) val, false); addByte((byte) (val >> 8), false); return this; } public StaticPacketBuilder addLEInt(int val) { ensureCapacity(curLength + 4); addByte((byte) val, false); addByte((byte) (val >> 8), false); addByte((byte) (val >> 16), false); addByte((byte) (val >> 24), false); return this; } /** * Adds a long to the data stream. The size of this packet will * grow by eight bytes. * * @param val * The long value to add * @return A reference to this object */ public StaticPacketBuilder addLong(long val) { addInt((int) (val >> 32)); addInt((int) (val & -1L)); return this; } public StaticPacketBuilder addLELong(long val) { addLEInt((int) (val & -1L)); addLEInt((int) (val >> 32)); return this; } @SuppressWarnings("deprecation") public StaticPacketBuilder addString(String s) { ensureCapacity(curLength + s.length() + 1); s.getBytes(0, s.length(), payload, curLength); curLength += s.length(); payload[curLength++] = 0; return this; } public int getLength() { return curLength; } /** * Returns a Packet object for the data contained in this * builder. * * @return A Packet object */ public Packet toPacket() { byte[] data = new byte[curLength]; System.arraycopy(payload, 0, data, 0, curLength); return new Packet(null, id, data, bare, size); } public StaticPacketBuilder addByteC(int val) { addByte((byte) -val); return this; } public StaticPacketBuilder addByteS(int val) { addByte((byte) (128 - val)); return this; } }