/**
   * Encodes memcache message to a raw byte array.
   *
   * @param msg Message being serialized.
   * @return Serialized message.
   * @throws GridException If serialization failed.
   */
  private ByteBuffer encodeMemcache(GridTcpRestPacket msg) throws GridException {
    GridByteArrayList res = new GridByteArrayList(HDR_LEN);

    int keyLength = 0;

    int keyFlags = 0;

    if (msg.key() != null) {
      ByteArrayOutputStream rawKey = new ByteArrayOutputStream();

      keyFlags = encodeObj(msg.key(), rawKey);

      msg.key(rawKey.toByteArray());

      keyLength = rawKey.size();
    }

    int dataLength = 0;

    int valFlags = 0;

    if (msg.value() != null) {
      ByteArrayOutputStream rawVal = new ByteArrayOutputStream();

      valFlags = encodeObj(msg.value(), rawVal);

      msg.value(rawVal.toByteArray());

      dataLength = rawVal.size();
    }

    int flagsLength = 0;

    if (msg.addFlags()) // || keyFlags > 0 || valFlags > 0)
    flagsLength = FLAGS_LENGTH;

    res.add(MEMCACHE_RES_FLAG);

    res.add(msg.operationCode());

    // Cast is required due to packet layout.
    res.add((short) keyLength);

    // Cast is required due to packet layout.
    res.add((byte) flagsLength);

    // Data type is always 0x00.
    res.add((byte) 0x00);

    res.add((short) msg.status());

    res.add(keyLength + flagsLength + dataLength);

    res.add(msg.opaque(), 0, msg.opaque().length);

    // CAS, unused.
    res.add(0L);

    assert res.size() == HDR_LEN;

    if (flagsLength > 0) {
      res.add((short) keyFlags);
      res.add((short) valFlags);
    }

    assert msg.key() == null || msg.key() instanceof byte[];
    assert msg.value() == null || msg.value() instanceof byte[];

    if (keyLength > 0) res.add((byte[]) msg.key(), 0, ((byte[]) msg.key()).length);

    if (dataLength > 0) res.add((byte[]) msg.value(), 0, ((byte[]) msg.value()).length);

    return ByteBuffer.wrap(res.entireArray());
  }
  /**
   * Parses memcache protocol message.
   *
   * @param ses Session.
   * @param buf Buffer containing not parsed bytes.
   * @param state Current parser state.
   * @return Parsed packet.s
   * @throws IOException If packet cannot be parsed.
   * @throws GridException If deserialization error occurred.
   */
  @Nullable
  private GridClientMessage parseMemcachePacket(
      GridNioSession ses, ByteBuffer buf, ParserState state) throws IOException, GridException {
    assert state.packetType() == PacketType.MEMCACHE;
    assert state.packet() != null;
    assert state.packet() instanceof GridTcpRestPacket;

    GridTcpRestPacket req = (GridTcpRestPacket) state.packet();
    ByteArrayOutputStream tmp = state.buffer();
    int i = state.index();

    while (buf.remaining() > 0) {
      byte b = buf.get();

      if (i == 0) req.requestFlag(b);
      else if (i == 1) req.operationCode(b);
      else if (i == 2 || i == 3) {
        tmp.write(b);

        if (i == 3) {
          req.keyLength(U.bytesToShort(tmp.toByteArray(), 0));

          tmp.reset();
        }
      } else if (i == 4) req.extrasLength(b);
      else if (i >= 8 && i <= 11) {
        tmp.write(b);

        if (i == 11) {
          req.totalLength(U.bytesToInt(tmp.toByteArray(), 0));

          tmp.reset();
        }
      } else if (i >= 12 && i <= 15) {
        tmp.write(b);

        if (i == 15) {
          req.opaque(tmp.toByteArray());

          tmp.reset();
        }
      } else if (i >= HDR_LEN && i < HDR_LEN + req.extrasLength()) {
        tmp.write(b);

        if (i == HDR_LEN + req.extrasLength() - 1) {
          req.extras(tmp.toByteArray());

          tmp.reset();
        }
      } else if (i >= HDR_LEN + req.extrasLength()
          && i < HDR_LEN + req.extrasLength() + req.keyLength()) {
        tmp.write(b);

        if (i == HDR_LEN + req.extrasLength() + req.keyLength() - 1) {
          req.key(tmp.toByteArray());

          tmp.reset();
        }
      } else if (i >= HDR_LEN + req.extrasLength() + req.keyLength()
          && i < HDR_LEN + req.totalLength()) {
        tmp.write(b);

        if (i == HDR_LEN + req.totalLength() - 1) {
          req.value(tmp.toByteArray());

          tmp.reset();
        }
      }

      if (i == HDR_LEN + req.totalLength() - 1)
        // Assembled the packet.
        return assemble(ses, req);

      i++;
    }

    state.index(i);

    return null;
  }