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));
}
}
}
}