content: package io.pkts.buffer;
  
  import java.io.IOException;
  import java.io.InputStream;
  import java.io.UnsupportedEncodingException;
  
  /**
   * Yet another buffer class!
   * 
   * Kind of the same as the awesome classes in netty.io but currently the buffer
   * classes in netty are a little too tied into the rest of the netty stack.
   * Therefore, copy-pasting the necessary pieces needed for this project. Also,
   * since this entire project (i.e. yajpcap) is only for one particular use case,
   * we don't need all the flexibility of netty's buffers, even though somewhat
   * silly to re-invent the wheel.
   *
   * @author jonas@jonasborjesson.com
   */
  public interface Buffer extends Cloneable {
  
      /**
       * Helper method to "parse" out a unsigned int from the given 4 bytes.
       *
       * @param a
       * @param b
       * @param c
       * @param d
       * @return
       */
      static long unsignedInt(final byte a, final byte b, final byte c, final byte d) {
          return ((long) (a & 0xff)) << 24 | (b & 0xff) << 16 | (c & 0xff) << 8 | d & 0xff;
      }
  
      /**
       * Same as calling {@link #getBytes(int, Buffer)} where the index is
       * {@link #getReaderIndex()}.
       *
       * @param dst
       * @throws IndexOutOfBoundsException
       */
      void getBytes(Buffer dst);
  
      /**
       * Transfer this buffer's data to the destination buffer. This method does
       * not change the <code>readerIndex</code> or the <code>writerIndex</code>
       * of the src buffer (i.e. this) but will increase the
       * <code>writerIndex</code> of the destination buffer.
       * 
       * @param index
       * @param dst
       * @throws IndexOutOfBoundsException
       *             in case index < 0
       */
      void getBytes(final int index, final Buffer dst) throws IndexOutOfBoundsException;
  
      void getBytes(final byte[] dst) throws IndexOutOfBoundsException;
  
      /**
       * @see #getBytes(byte[])
       * @deprecated Please use {@link #getBytes(byte[])} instead
       */
      @Deprecated
      default void getByes(final byte[] dst) throws IndexOutOfBoundsException {
          getBytes(dst);
      }
  
      /**
       * Read the requested number of bytes and increase the readerIndex with the
       * corresponding number of bytes. The new buffer and this buffer both share
       * the same backing array so changing either one of them will affect the
       * other.
       * 
       * @param length
       * @return
       * @throws IndexOutOfBoundsException
       * @throws IOException
       */
      Buffer readBytes(int length) throws IndexOutOfBoundsException, IOException;
  
      /**
       * Reads a line, i.e., it reads until we hit a line feed ('\n') or a
       * carriage return ('\r'), or a carriage return followed immediately by a
       * line feed.
       * 
       * @return a buffer containing the line but without the line terminating
       *         characters
       */
      Buffer readLine() throws IOException;
  
      /**
       * Read until we find a single CRLF. The single CRLF will NOT be part of the
       * returned buffer but will be consumed.
       *
       * If we cannot find a single CRLF then null will be returned and the passed
       * in buffer will be reset to the same reader index as when it was passed
       * in.
       *
       * Note that this one is very similar to {@link Buffer#readLine()} but the
       * readLine doesn't enforce the CRLF being present, which is typical for
       * e.g. SIP and is important when reading bytes being streamed over e.g.
       * a network connection
       *
       * @return the resulting buffer containing everything up until (but not
       *         inclusive) the single-crlf or null if no single-crlf was not
       *         found.
       */
      Buffer readUntilSingleCRLF() throws IOException;
  
      /**
       * Read until we find a double CRLF. The double CRLF will NOT be part of the
       * returned buffer but they will be consumed.
       * 
       * If we cannot find a double CRLF then null will be returned and the passed
       * in buffer will be reset to the same reader index as when it was passed
       * in.
       * 
       * @return the resulting buffer containing everything up until (but not
       *         inclusive) the double-crlf or null if no double-crlf was not
       *         found.
       */
      Buffer readUntilDoubleCRLF() throws IOException;
  
      /**
       * Returns the number of available bytes for reading without blocking. If
       * this returns less than what you want, there may still be more bytes
       * available depending on the underlying implementation. E.g., a Buffer
       * backed by an {@link InputStream} may be able to read more off the stream,
       * however, it may not be able to do so without blocking.
       * 
       * @return
       */
      int getReadableBytes();
  
      /**
       * Checks whether this buffer has any bytes available for reading without
       * blocking.
       * 
       * This is the same as <code>{@link #getReadableBytes()} > 0</code>
       * 
       * @return
       */
      boolean hasReadableBytes();
  
      /**
       * Check whether this buffer is empty or not. This is the same as
       * <code>!{@link #hasReadableBytes()}</code>
       * 
       * @return
       */
      boolean isEmpty();
  
      /**
       * Get the backing array.
       *
       * @return
       */
      byte[] getArray();
  
      default byte[] getRawArray() {
          return getArray();
      }
  
      /**
       * Check whether a particular bit within a byte is on or off.
       *
       * @param byteIndex the index of the byte we are going to check.
       * @param bitNo     the bit no, which is zero indexed and of course needs to be within 0 to 7.
       * @return
       * @throws IllegalArgumentException in case the bit number is not between 0-7 (inclusive).
       */
      default boolean getBit(final int byteIndex, final int bitNo) throws IllegalArgumentException {
          switch (bitNo) {
              case 0:
                  return getBit0(byteIndex);
              case 1:
                  return getBit1(byteIndex);
              case 2:
                  return getBit2(byteIndex);
              case 3:
                  return getBit3(byteIndex);
              case 4:
                  return getBit4(byteIndex);
              case 5:
                  return getBit5(byteIndex);
              case 6:
                  return getBit6(byteIndex);
              case 7:
                  return getBit7(byteIndex);
              default:
                  throw new IllegalArgumentException("The bit number has to be between 0 - 7 (inclusive)");
          }
      }
  
      default boolean getBit0(final int index) {
          try {
              return (getByte(index) & 0b00000001) == 0b00000001;
          } catch (final IOException e) {
              throw new RuntimeException(e);
          }
      }
  
      default boolean getBit1(final int index) {
          try {
              return (getByte(index) & 0b00000010) == 0b00000010;
          } catch (final IOException e) {
              throw new RuntimeException(e);
          }
      }
  
      default boolean getBit2(final int index) {
          try {
              return (getByte(index) & 0b00000100) == 0b00000100;
          } catch (final IOException e) {
              throw new RuntimeException(e);
          }
      }
  
      default boolean getBit3(final int index) {
          try {
              return (getByte(index) & 0b00001000) == 0b00001000;
          } catch (final IOException e) {
              throw new RuntimeException(e);
          }
      }
  
      default boolean getBit4(final int index) {
          try {
              return (getByte(index) & 0b00010000) == 0b00010000;
          } catch (final IOException e) {
              throw new RuntimeException(e);
          }
      }
  
      default boolean getBit5(final int index) {
          try {
              return (getByte(index) & 0b00100000) == 0b00100000;
          } catch (final IOException e) {
              throw new RuntimeException(e);
          }
      }
  
      default boolean getBit6(final int index) {
          try {
              return (getByte(index) & 0b01000000) == 0b01000000;
          } catch (final IOException e) {
              throw new RuntimeException(e);
          }
      }
  
      default boolean getBit7(final int index) {
          try {
              return (getByte(index) & 0b10000000) == 0b10000000;
          } catch (final IOException e) {
              throw new RuntimeException(e);
          }
      }
  
      /**
       * Same as {@link #readUntil(4096, 'b')}
       *
       * Read until the specified byte is encountered and return a buffer
       * representing that section of the buffer.
       *
       * If the byte isn't found, then a {@link ByteNotFoundException} is thrown
       * and the {@link #getReaderIndex()} is left where we bailed out.
       *
       * Note, the byte we are looking for will have been consumed so whatever
       * that is left in the {@link Buffer} will not contain that byte.
       * 
       * Example: <code>
       *    Buffer buffer = Buffers.wrap("hello world");
       *    Buffer hello = buffer.readUntil((byte)' ');
       *    System.out.println(hello);  // will contain "hello"
       *    System.out.println(buffer); // will contain "world"
       * </code>
       * 
       * As the example above illustrates, we are looking for a space, which is
       * found between "hello" and "world". Since the space will be consumed, the
       * original buffer will now only contain "world" and not " world".
       * 
       * @param b
       *            the byte to look for
       * @return a buffer containing the content from the initial reader index to
       *         the the position where the byte was found (exclusive the byte we
       *         are looking for)
       * 
       * @throws ByteNotFoundException
       *             in case the byte we were looking for is not found.
       */
      Buffer readUntil(byte b) throws IOException, ByteNotFoundException;
  
      /**
       * Read until any of the specified bytes have been encountered or until we
       * have read a maximum amount of bytes. This one works exactly the same as
       * {@link #readUntil(byte)} except it allows you to look for multiple bytes
       * and to specify for how many bytes we should be looking before we give up.
       * 
       * Example, we want to read until we either find
       * 
       * @param maxBytes
       *            the maximum number of bytes we would like to read before
       *            giving up.
       * @param bytes
       *            the bytes we are looking for (either one of them)
       * @return a buffer containing the content from the initial reader index to
       *         the the position where the byte was found (exclusive the byte we
       *         are looking for)
       * @throws IOException
       * @throws ByteNotFoundException
       *             in case none of the bytes we were looking for are found
       *             within the specified maximum number of bytes.
       * @throws IllegalArgumentException
       *             in no bytes to look for is specified.
       */
      Buffer readUntil(int maxBytes, byte... bytes) throws IOException, ByteNotFoundException, IllegalArgumentException;
  
      /**
       * Same as {@link #readUntil(int, byte...)} but will return null instead of
       * throwing a {@link ByteNotFoundException}
       *
       * @param maxBytes
       * @param bytes
       * @return
       * @throws IOException
       * @throws IllegalArgumentException
       */
      Buffer readUntilSafe(int maxBytes, byte... bytes) throws IOException, IllegalArgumentException;
  
      /**
       * Same as {@link #readUntil(int, byte...)} but instead of returning the
       * buffer with everything up until the specified byte it returns the index
       * instead.
       * 
       * NOTE. The index is representing where in the {@link Buffer} you can find
       * the byte and the index is in relation to the entire {@link Buffer} and
       * its capacity so even if you have already read x bytes, it would not
       * change the index of what you search for.
       * 
       * Example:
       * 
       * 
       * @param maxBytes
       *            the maximum number of bytes we would like to read before
       *            giving up.
       * @param bytes
       *            the bytes we are looking for (either one of them)
       * @return the index of the found byte or -1 (negative one) if we couldn't
       *         find it.
       * @throws IOException
       * @throws ByteNotFoundException
       *             will ONLY be thrown if we haven't found the byte within the
       *             maxBytes limit. If the buffer we are searching in is less
       *             than maxBytes and we can't find what we are looking for then
       *             negative one will be returned instead.
       */
      int indexOf(int maxBytes, byte... bytes) throws IOException, ByteNotFoundException, IllegalArgumentException;
  
      /**
       * 
       * @param b
       * @return
       * @throws IOException
       * @throws ByteNotFoundException
       * @throws IllegalArgumentException
       */
      int indexOf(byte b) throws IOException, ByteNotFoundException, IllegalArgumentException;
  
      /**
       * Get a slice of the buffer starting at <code>start</code> (inclusive)
       * ending at <code>stop</code> (exclusive). Hence, the new capacity of the
       * buffer is <code>stop - start</code>
       * 
       * @return
       */
      Buffer slice(int start, int stop);
  
      /**
       * Same as {@link #slice(Buffer.getReaderIndex(), int)}
       * 
       * @param stop
       * @return
       */
      Buffer slice(int stop);
  
      /**
       * Slice off the rest of the buffer. Same as {@link
       * #slice(Buffer.getReaderIndex(), buffer.getCapacity())}
       * 
       * Note, if you slice an empty buffer you will get back another empty
       * buffer. Same goes for when you slice a buffer whose bytes already have
       * been consumed.
       * 
       * @return
       */
      Buffer slice();
  
      /**
       * If you access the {@link Buffer#getRawArray()} you will get just that. The raw
       * un-guarded array, which may be useful but should be used with care since you
       * now can do whatever you want with the data. However, if you do so and also
       * want to know which 'slice' of that array this buffer is seeing, then you
       * need to know the lower boundary as well since the array can e.g be 1k long
       * but the data seen by this buffer starts at index 200.
       *
       * @return
       */
      int getLowerBoundary();
  
      /**
       * See explanation in {@link Buffer#getLowerBoundary()} since this is the same, just
       * the upper limit of that raw array.
       *
       * @return
       */
      int getUpperBoundary();
  
      /**
       * The reader index
       * 
       * @return
       */
      int getReaderIndex();
  
      void setReaderIndex(int index);
  
      /**
       * Mark the current position of the reader index.
       * 
       * @see #reset()
       */
      void markReaderIndex();
  
      /**
       * Reset the reader index to the marked position or to the beginning of the
       * buffer if mark hasn't explicitly been called.
       */
      void resetReaderIndex();
  
      /**
       * The capacity of this buffer. The capacity is not affected by where the
       * reader index is etc
       * 
       * @return the capacity
       */
      int capacity();
  
      /**
       * Get the byte at the index.
       * 
       * Note, depending on the underlying implementing buffer, this method may
       * block and try and read the missing bytes off a stream. E.g., the
       * {@link InputStreamBuffer} gets its bytes off of a {@link InputStream} so
       * let's say you have read 10 bytes off of the stream already but ask to
       * access the byte at index 20, we will try and ready an additional 10 bytes
       * from the InputStream so we can return the byte at index 20.
       * 
       * @param index
       * @return the byte at the specified index
       * @throws IndexOutOfBoundsException
       *             in case the index is greater than the capacity of this buffer
       */
      byte getByte(int index) throws IndexOutOfBoundsException, IOException;
  
      /**
       * Read the next byte, which will also increase the readerIndex by one.
       * 
       * @return the next byte
       * @throws IndexOutOfBoundsException
       *             in case there is nothing left to read
       */
      byte readByte() throws IndexOutOfBoundsException, IOException;
  
      /**
       * Peak a head to see what the next byte is. This method will not change the
       * readerIndex
       * 
       * @return the next byte
       * @throws IndexOutOfBoundsException
       *             in case there is nothing left to read
       */
      byte peekByte() throws IndexOutOfBoundsException, IOException;
  
      /**
       * Read an unsigned int and will increase the reader index of this buffer by
       * 4
       * 
       * @return a long representing the unsigned int
       * @throws IndexOutOfBoundsException
       *             in case there is not 4 bytes left to read
       */
      long readUnsignedInt() throws IndexOutOfBoundsException;
  
      /**
       * Read an int and will increase the reader index of this buffer by 4
       * 
       * @return the int value
       * @throws IndexOutOfBoundsException
       *             in case there is not 4 bytes left to read
       */
      int readInt() throws IndexOutOfBoundsException;
  
      /**
       * Get a 32-bit integer at the specified absolute index. This method will
       * not modify the readerIndex of this buffer.
       * 
       * @param index
       * @return
       * @throws IndexOutOfBoundsException
       *             in case there is not 4 bytes left to read
       */
      int getInt(int index) throws IndexOutOfBoundsException;
  
      void setInt(int index, int value) throws IndexOutOfBoundsException;
  
      void setUnsignedInt(int index, long value) throws IndexOutOfBoundsException;
  
      short getShort(int index) throws IndexOutOfBoundsException;
  
      int readUnsignedShort() throws IndexOutOfBoundsException;
  
      int getUnsignedShort(int index) throws IndexOutOfBoundsException;
  
      void setUnsignedShort(int index, int value) throws IndexOutOfBoundsException;
  
      short readShort() throws IndexOutOfBoundsException;
  
      short readUnsignedByte() throws IndexOutOfBoundsException, IOException;
  
      long getUnsignedInt(int index) throws IndexOutOfBoundsException;
  
      short getUnsignedByte(int index) throws IndexOutOfBoundsException;
  
      void setUnsignedByte(int index, short value) throws IndexOutOfBoundsException;
  
      /**
       * Parse all the readable bytes in this buffer as a unsigned integer value.
       * The reader index will not be modified.
       *
       * @return
       * @throws NumberFormatException
       *             in case the bytes in the buffer cannot be converted into an
       *             integer value.
       * @throws IOException
       *             in case anything goes wrong when reading from the underlying
       */
      int parseToInt() throws NumberFormatException, IOException;
  
      /**
       * Convert the entire buffer to a signed integer value
       * 
       * @param radix
       * @return
       */
      int parseToInt(int radix) throws NumberFormatException, IOException;
  
      /**
       * Dump the content of this buffer as a hex dump ala Wireshark. Mainly for
       * debugging purposes
       * 
       * @return
       */
      String dumpAsHex();
  
      /**
       * Performs a deep clone of this object. I.e., the array that is backed by
       * this buffer will be copied and a new buffer will be returned. Hence, any
       * changes to the backing of this buffer will not affect the cloned buffer.
       * 
       * @return
       */
      Buffer clone();
  
      /**
       * Set the byte at given index to a new value
       * 
       * @param index
       *            the index
       * @param value
       *            the value
       * @throws IndexOutOfBoundsException
       */
      void setByte(int index, byte value) throws IndexOutOfBoundsException;
  
      /**
       * The writer index. This is where we will be writing our next byte if asked
       * to do so.
       * 
       * @return
       */
      int getWriterIndex();
  
      void setWriterIndex(int index);
  
      /**
       * Get the number of writable bytes.
       * 
       * @return
       */
      int getWritableBytes();
  
      /**
       * Checks whether this {@link Buffer} has any space left for writing. Same
       * as {@link #getWritableBytes()} > 0
       * 
       * @return
       */
      boolean hasWritableBytes();
  
      /**
       * Check whether this {@link Buffer} has write support or not. There are
       * buffers that do not allow you to write to them (such as the
       * {@link EmptyBuffer}) so if you try and write to such a buffer it will
       * throw a {@link WriteNotSupportedException}.
       * 
       * @return
       */
      boolean hasWriteSupport();
  
      /**
       * Write a byte to where the current writer index is pointing. Note, many
       * implementations may not support writing and if they don't, they will
       * throw a {@link WriteNotSupportedException}
       * 
       * @param b
       * @throws IndexOutOfBoundsException
       *             in case there is no more space to write to (which includes
       *             those cases where the underlying implementation does not
       *             support writing)
       * @throws WriteNotSupportedException
       *             in case the underlying implementation does not support
       *             writes.
       */
      void write(byte b) throws IndexOutOfBoundsException, WriteNotSupportedException;
  
      void write(byte[] bytes) throws IndexOutOfBoundsException, WriteNotSupportedException;
  
      void write(int value) throws IndexOutOfBoundsException, WriteNotSupportedException;
  
      void write(long value) throws IndexOutOfBoundsException, WriteNotSupportedException;
  
      /**
       * Same as {@link Buffer#write(String, String)} where the charset is set to
       * "UTF-8"
       * 
       * @param s
       * @throws IndexOutOfBoundsException
       *             in case we cannot write entire String to this {@link Buffer}.
       * @throws WriteNotSupportedException
       *             in case the underlying implementation does not support
       *             writes.
       * @throws UnsupportedEncodingException
       *             in case the charset "UTF-8" is not supported by the platform.
       */
      void write(String s) throws IndexOutOfBoundsException, WriteNotSupportedException, UnsupportedEncodingException;
  
      /**
       * Write the integer value to this {@link Buffer} as a String.
       * 
       * @param value
       *            the value that will be converted to a String before being
       *            written to this {@link Buffer}.
       * @throws IndexOutOfBoundsException
       * @throws WriteNotSupportedException
       */
      void writeAsString(int value) throws IndexOutOfBoundsException, WriteNotSupportedException;
  
      /**
       * Write the long value to this {@link Buffer} as a String.
       * 
       * @param value the value that will be converted to a String before being written to this
       *        {@link Buffer}.
       * @throws IndexOutOfBoundsException
       * @throws WriteNotSupportedException
       */
      void writeAsString(long value) throws IndexOutOfBoundsException, WriteNotSupportedException;
  
      /**
       * Write a string to this buffer using the specified charset to convert the String into bytes.
       * The <code>writerInxex</code> of this buffer will be increased with the corresponding number
       * of bytes.
       * 
       * Note, either the entire string is written to this buffer or if it doesn't fit then nothing is
       * written to this buffer.
       * 
       * @param s
       * @param charset
       * @throws IndexOutOfBoundsException in case we cannot write entire String to this
       *         {@link Buffer}.
       * @throws WriteNotSupportedException
       * @throws UnsupportedEncodingException in case the specified charset is not supported
       */
      void write(final String s, String charset) throws IndexOutOfBoundsException, WriteNotSupportedException,
      UnsupportedEncodingException;
  
      /**
       * Check whether to buffers are considered to be equal.
       * 
       * To buffers are equal if the underlying visible area of the byte array are
       * equal.
       * 
       * @param b
       * @return
       */
      @Override
      boolean equals(Object b);
  
      boolean equalsIgnoreCase(Object b);
  
      @Override
      int hashCode();
  
      @Override
      String toString();
  }
  