class SyslogUtils {

  public Event extractEvent(ChannelBuffer in) {

    /* for protocol debugging
    ByteBuffer bb = in.toByteBuffer();
    int remaining = bb.remaining();
    byte[] buf = new byte[remaining];
    bb.get(buf);
    HexDump.dump(buf, 0, System.out, 0);
    */

    byte b = 0;
    Event e = null;
    boolean doneReading = false;

    try {
      while (!doneReading && in.readable()) {
        b = in.readByte();
        switch (m) {
          case START:
            if (b == '<') {
              baos.write(b);
              m = Mode.PRIO;
            } else if (b == '\n') {
              //If the character is \n, it was because the last event was exactly
              //as long  as the maximum size allowed and
              //the only remaining character was the delimiter - '\n', or
              //multiple delimiters were sent in a row.
              //Just ignore it, and move forward, don't change the mode.
              //This is a no-op, just ignore it.
              logger.debug("Delimiter found while in START mode, ignoring..");

            } else {
              isBadEvent = true;
              baos.write(b);
              //Bad event, just dump everything as if it is data.
              m = Mode.DATA;
            }
            break;
          case PRIO:
            baos.write(b);
            if (b == '>') {
              if (prio.length() == 0) {
                isBadEvent = true;
              }
              m = Mode.DATA;
            } else {
              char ch = (char) b;
              prio.append(ch);
              // Priority is max 3 digits per both RFC 3164 and 5424
              // With this check there is basically no danger of
              // boas.size() exceeding this.maxSize before getting to the
              // DATA state where this is actually checked
              if (!Character.isDigit(ch) || prio.length() > 3) {
                isBadEvent = true;
                //If we hit a bad priority, just write as if everything is data.
                m = Mode.DATA;
              }
            }
            break;
          case DATA:
            // TCP syslog entries are separated by '\n'
            if (b == '\n') {
              e = buildEvent();
              doneReading = true;
            } else {
              baos.write(b);
            }
            if (baos.size() == this.maxSize && !doneReading) {
              isIncompleteEvent = true;
              e = buildEvent();
              doneReading = true;
            }
            break;
        }

      }

      // UDP doesn't send a newline, so just use what we received
      if (e == null && isUdp) {
        doneReading = true;
        e = buildEvent();
      }
    } finally {
      // no-op
    }

    return e;
  }

  public  SyslogUtils();
  public  SyslogUtils(boolean isUdp);
  public  SyslogUtils(Integer defaultSize, Set<String> keepFields, boolean isUdp);
  public  SyslogUtils(Integer eventSize, Set<String> keepFields, boolean isUdp, Clock clock);

}