protected void decode()

in folsom/src/main/java/com/spotify/folsom/client/ascii/AsciiMemcacheDecoder.java [53:241]


  protected void decode(final ChannelHandlerContext ctx, final ByteBuf buf, final List<Object> out)
      throws Exception {
    while (true) {
      int readableBytes = buf.readableBytes();
      if (readableBytes == 0) {
        return;
      }

      if (key != null) {
        final int toCopy = Math.min(value.length - valueOffset, readableBytes);
        if (toCopy > 0) {
          buf.readBytes(value, valueOffset, toCopy);
          readableBytes -= toCopy;
          valueOffset += toCopy;
          if (valueOffset < value.length) {
            return;
          }
        }
        final ByteBuffer line = readLine(buf, readableBytes);
        if (line == null) {
          return;
        }
        if (line.remaining() > 0) {
          throw new IOException(String.format("Unexpected end of data block: %s", toString(line)));
        }
        valueResponse.addGetResult(key, value, cas, flags);
        key = null;
        value = null;
        cas = 0;
        flags = 0;
      } else {
        final ByteBuffer line = readLine(buf, readableBytes);
        if (line == null) {
          return;
        }
        readNextToken();
        int tokenLength = token.remaining();
        if (tokenLength < 1) {
          throw fail();
        }

        final byte firstChar = token.get();

        if (Character.isDigit(firstChar)) {
          try {
            long numeric = parseLong(firstChar, token);
            out.add(new NumericAsciiResponse(numeric));
          } catch (NumberFormatException e) {
            throw new IOException("Unexpected line: " + line, e);
          }
        } else if (tokenLength == 2) {
          expect(firstChar, "OK");
          out.add(AsciiResponse.OK);
          return;
        } else if (tokenLength == 3) {
          expect(firstChar, "END");
          if (!valueResponse.isEmpty()) {
            out.add(valueResponse);
            valueResponse = new ValueAsciiResponse();
            valueMode = false;
            return;
          } else if (!statsAsciiResponse.isEmpty()) {
            out.add(statsAsciiResponse);
            statsAsciiResponse = new StatsAsciiResponse();
            return;
          } else {
            out.add(AsciiResponse.EMPTY_LIST);
            return;
          }
        } else if (tokenLength == 4) {
          expect(firstChar, "STAT");

          readNextToken();
          if (token.remaining() < 1) {
            throw fail();
          }
          final String statName = toString(token);

          readNextToken();
          if (token.remaining() < 1) {
            throw fail();
          }
          final String statValue = toString(token);

          statsAsciiResponse.addStat(statName, statValue);
        } else if (tokenLength == 5) {
          if (firstChar == 'E') {
            expect(firstChar, "ERROR");
            out.add(AsciiResponse.ERROR);
            return;
          } else {
            expect(firstChar, "VALUE");
            valueMode = true;
            // VALUE <key> <flags> <bytes> [<cas unique>]\r\n

            // key
            readNextToken();
            int keyLen = token.remaining();
            if (keyLen <= 0) {
              throw fail();
            }
            byte[] key = new byte[token.remaining()];
            token.get(key);

            // flags
            readNextToken();
            int flagLen = token.remaining();
            if (flagLen <= 0) {
              throw fail();
            }
            int flags = (int) parseLong(token.get(), token);

            // size
            readNextToken();
            int sizeLen = token.remaining();
            if (sizeLen <= 0) {
              throw fail();
            }
            final int size = (int) parseLong(token.get(), token);

            // cas
            readNextToken();
            int casLen = token.remaining();
            long cas = 0;
            if (casLen > 0) {
              cas = parseLong(token.get(), token);
            }

            this.key = key;
            this.value = new byte[size];
            this.valueOffset = 0;
            this.cas = cas;
            this.flags = flags;
          }
        } else if (valueMode) {
          // when in valueMode, the only valid responses are "END" and "VALUE"
          throw fail();
        } else if (tokenLength == 6) {
          if (firstChar == 'S') {
            expect(firstChar, "STORED");
            out.add(AsciiResponse.STORED);
            return;
          } else {
            expect(firstChar, "EXISTS");
            out.add(AsciiResponse.EXISTS);
            return;
          }
        } else if (tokenLength == 7) {
          if (firstChar == 'T') {
            expect(firstChar, "TOUCHED");
            out.add(AsciiResponse.TOUCHED);
            return;
          } else {
            expect(firstChar, "DELETED");
            out.add(AsciiResponse.DELETED);
            return;
          }
        } else if (tokenLength == 9) {
          expect(firstChar, "NOT_FOUND");
          out.add(AsciiResponse.NOT_FOUND);
          return;
        } else if (tokenLength == 10) {
          expect(firstChar, "NOT_STORED");
          out.add(AsciiResponse.NOT_STORED);
          return;
        } else if (tokenLength == 12) {
          if (firstChar == 'C') {
            expect(firstChar, "CLIENT_ERROR");
            out.add(AsciiResponse.CLIENT_ERROR);
            return;
          } else {
            final String lineStr = toString(line);
            switch (lineStr) {
              case "SERVER_ERROR object too large for cache":
                out.add(AsciiResponse.VALUE_TOO_LARGE);
                return;
              case "SERVER_ERROR out of memory storing object":
                out.add(AsciiResponse.OUT_OF_MEMORY);
                return;
              default:
                throw new IOException("Unexpected line: " + lineStr);
            }
          }
        } else {
          throw new IOException("Unexpected line: " + toString(line));
        }
      }
    }
  }