public synchronized void addFrame(byte flags, byte opcode, byte[] content, int offset, int length)
      throws IOException {
    long blockFor = _endp.getMaxIdleTime();

    if (_buffer == null) _buffer = _buffers.getDirectBuffer();

    if (_buffer.space() == 0) expelBuffer(blockFor);

    bufferPut(opcode, blockFor);

    if (isLengthFrame(opcode)) {
      // Send a length delimited frame

      // How many bytes we need for the length ?
      // We have 7 bits available, so log2(length) / 7 + 1
      // For example, 50000 bytes is 2 8-bytes: 11000011 01010000
      // but we need to write it in 3 7-bytes 0000011 0000110 1010000
      // 65536 == 1 00000000 00000000 => 100 0000000 0000000
      int lengthBytes = new BigInteger(String.valueOf(length)).bitLength() / 7 + 1;
      for (int i = lengthBytes - 1; i > 0; --i) {
        byte lengthByte = (byte) (0x80 | (0x7F & (length >> 7 * i)));
        bufferPut(lengthByte, blockFor);
      }
      bufferPut((byte) (0x7F & length), blockFor);
    }

    int remaining = length;
    while (remaining > 0) {
      int chunk = remaining < _buffer.space() ? remaining : _buffer.space();
      _buffer.put(content, offset + (length - remaining), chunk);
      remaining -= chunk;
      if (_buffer.space() > 0) {
        if (!isLengthFrame(opcode)) _buffer.put((byte) 0xFF);
        // Gently flush the data, issuing a non-blocking write
        flushBuffer();
      } else {
        // Forcibly flush the data, issuing a blocking write
        expelBuffer(blockFor);
        if (remaining == 0) {
          if (!isLengthFrame(opcode)) _buffer.put((byte) 0xFF);
          // Gently flush the data, issuing a non-blocking write
          flushBuffer();
        }
      }
    }
  }
  @Test
  public void testReuse() throws Exception {
    HttpFields header = new HttpFields();
    Buffer n1 = new ByteArrayBuffer("name1");
    Buffer va = new ByteArrayBuffer("value1");
    Buffer vb = new ByteArrayBuffer(10);
    vb.put((byte) 'v');
    vb.put((byte) 'a');
    vb.put((byte) 'l');
    vb.put((byte) 'u');
    vb.put((byte) 'e');
    vb.put((byte) '1');

    header.put("name0", "value0");
    header.put(n1, va);
    header.put("name2", "value2");

    assertEquals("value0", header.getStringField("name0"));
    assertEquals("value1", header.getStringField("name1"));
    assertEquals("value2", header.getStringField("name2"));
    assertNull(header.getStringField("name3"));

    header.remove(n1);
    assertNull(header.getStringField("name1"));
    header.put(n1, vb);
    assertEquals("value1", header.getStringField("name1"));

    int matches = 0;
    Enumeration e = header.getFieldNames();
    while (e.hasMoreElements()) {
      Object o = e.nextElement();
      if ("name0".equals(o)) matches++;
      if ("name1".equals(o)) matches++;
      if ("name2".equals(o)) matches++;
    }
    assertEquals(3, matches);

    e = header.getValues("name1");
    assertEquals(true, e.hasMoreElements());
    assertEquals(e.nextElement(), "value1");
    assertEquals(false, e.hasMoreElements());
  }
    /* ------------------------------------------------------------ */
    public void putTo(Buffer buffer) throws IOException {
      int o = (_name instanceof CachedBuffer) ? ((CachedBuffer) _name).getOrdinal() : -1;
      if (o >= 0) buffer.put(_name);
      else {
        int s = _name.getIndex();
        int e = _name.putIndex();
        while (s < e) {
          byte b = _name.peek(s++);
          switch (b) {
            case '\r':
            case '\n':
            case ':':
              continue;
            default:
              buffer.put(b);
          }
        }
      }

      buffer.put((byte) ':');
      buffer.put((byte) ' ');

      o = (_value instanceof CachedBuffer) ? ((CachedBuffer) _value).getOrdinal() : -1;
      if (o >= 0) buffer.put(_value);
      else {
        int s = _value.getIndex();
        int e = _value.putIndex();
        while (s < e) {
          byte b = _value.peek(s++);
          switch (b) {
            case '\r':
            case '\n':
              continue;
            default:
              buffer.put(b);
          }
        }
      }

      BufferUtil.putCRLF(buffer);
    }
 private synchronized void bufferPut(byte datum, long blockFor) throws IOException {
   if (_buffer == null) _buffer = _buffers.getDirectBuffer();
   _buffer.put(datum);
   if (_buffer.space() == 0) expelBuffer(blockFor);
 }
  public synchronized void addFrame(byte flags, byte opcode, byte[] content, int offset, int length)
      throws IOException {
    // System.err.printf("<< %s %s
    // %s\n",TypeUtil.toHexString(flags),TypeUtil.toHexString(opcode),length);

    long blockFor = _endp.getMaxIdleTime();

    if (_buffer == null)
      _buffer = (_maskGen != null) ? _buffers.getBuffer() : _buffers.getDirectBuffer();

    boolean last = WebSocketConnectionD06.isLastFrame(flags);
    opcode = (byte) (((0xf & flags) << 4) + 0xf & opcode);

    int space = (_maskGen != null) ? 14 : 10;

    do {
      opcode = _opsent ? WebSocketConnectionD06.OP_CONTINUATION : opcode;
      _opsent = true;

      int payload = length;
      if (payload + space > _buffer.capacity()) {
        // We must fragement, so clear FIN bit
        opcode &= (byte) 0x7F; // Clear the FIN bit
        payload = _buffer.capacity() - space;
      } else if (last) opcode |= (byte) 0x80; // Set the FIN bit

      // ensure there is space for header
      if (_buffer.space() <= space) expelBuffer(blockFor);

      // write mask
      if ((_maskGen != null)) {
        _maskGen.genMask(_mask);
        _m = 0;
        _buffer.put(_mask);
      }

      // write the opcode and length
      if (payload > 0xffff) {
        bufferPut(
            new byte[] {
              opcode,
              (byte) 0x7f,
              (byte) 0,
              (byte) 0,
              (byte) 0,
              (byte) 0,
              (byte) ((payload >> 24) & 0xff),
              (byte) ((payload >> 16) & 0xff),
              (byte) ((payload >> 8) & 0xff),
              (byte) (payload & 0xff)
            });
      } else if (payload >= 0x7e) {
        bufferPut(new byte[] {opcode, (byte) 0x7e, (byte) (payload >> 8), (byte) (payload & 0xff)});
      } else {
        bufferPut(opcode);
        bufferPut((byte) payload);
      }

      // write payload
      int remaining = payload;
      while (remaining > 0) {
        _buffer.compact();
        int chunk = remaining < _buffer.space() ? remaining : _buffer.space();

        if ((_maskGen != null)) {
          for (int i = 0; i < chunk; i++) bufferPut(content[offset + (payload - remaining) + i]);
        } else _buffer.put(content, offset + (payload - remaining), chunk);

        remaining -= chunk;
        if (_buffer.space() > 0) {
          // Gently flush the data, issuing a non-blocking write
          flushBuffer();
        } else {
          // Forcibly flush the data, issuing a blocking write
          expelBuffer(blockFor);
          if (remaining == 0) {
            // Gently flush the data, issuing a non-blocking write
            flushBuffer();
          }
        }
      }
      offset += payload;
      length -= payload;
    } while (length > 0);
    _opsent = !last;
  }
 private synchronized void bufferPut(byte data) throws IOException {
   _buffer.put((byte) (data ^ _mask[+_m++ % 4]));
 }
 private synchronized void bufferPut(byte[] data) throws IOException {
   if (_maskGen != null) for (int i = 0; i < data.length; i++) data[i] ^= _mask[+_m++ % 4];
   _buffer.put(data);
 }