diff --git a/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java b/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java
index 3e231285a0..9588758ca2 100644
--- a/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java
+++ b/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java
@@ -365,7 +365,7 @@ public final class ByteBufUtil {
*/
public static ByteBuf writeUtf8(ByteBufAllocator alloc, CharSequence seq) {
// UTF-8 uses max. 3 bytes per char, so calculate the worst case.
- ByteBuf buf = alloc.buffer(seq.length() * MAX_BYTES_PER_CHAR_UTF8);
+ ByteBuf buf = alloc.buffer(utf8MaxBytes(seq));
writeUtf8(buf, seq);
return buf;
}
@@ -378,7 +378,7 @@ public final class ByteBufUtil {
*/
public static int writeUtf8(ByteBuf buf, CharSequence seq) {
final int len = seq.length();
- buf.ensureWritable(len * MAX_BYTES_PER_CHAR_UTF8);
+ buf.ensureWritable(utf8MaxBytes(seq));
for (;;) {
if (buf instanceof AbstractByteBuf) {
@@ -445,6 +445,13 @@ public final class ByteBufUtil {
return writerIndex - oldWriterIndex;
}
+ /**
+ * Returns max bytes length of UTF8 character sequence.
+ */
+ public static int utf8MaxBytes(CharSequence seq) {
+ return seq.length() * MAX_BYTES_PER_CHAR_UTF8;
+ }
+
/**
* Encode a {@link CharSequence} in ASCII and write
* it to a {@link ByteBuf} allocated with {@code alloc}.
diff --git a/codec-redis/pom.xml b/codec-redis/pom.xml
new file mode 100644
index 0000000000..0b4a80faee
--- /dev/null
+++ b/codec-redis/pom.xml
@@ -0,0 +1,39 @@
+
+
+
+
+ 4.0.0
+
+ io.netty
+ netty-parent
+ 4.1.0.Final-SNAPSHOT
+
+
+ netty-codec-redis
+ jar
+
+ Netty/Codec/Redis
+
+
+
+ ${project.groupId}
+ netty-codec
+ ${project.version}
+
+
+
+
diff --git a/codec-redis/src/main/java/io/netty/handler/codec/redis/AbstractStringRedisMessage.java b/codec-redis/src/main/java/io/netty/handler/codec/redis/AbstractStringRedisMessage.java
new file mode 100644
index 0000000000..3088fa17ab
--- /dev/null
+++ b/codec-redis/src/main/java/io/netty/handler/codec/redis/AbstractStringRedisMessage.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2016 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License, version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package io.netty.handler.codec.redis;
+
+import io.netty.util.internal.ObjectUtil;
+
+/**
+ * Abstract class for Simple Strings or Errors.
+ */
+public abstract class AbstractStringRedisMessage implements RedisMessage {
+
+ private final String content;
+
+ AbstractStringRedisMessage(String content) {
+ this.content = ObjectUtil.checkNotNull(content, "content");
+ }
+
+ /**
+ * Get string content of this {@link AbstractStringRedisMessage}.
+ *
+ * @return content of this message.
+ */
+ public final String content() {
+ return content;
+ }
+}
diff --git a/codec-redis/src/main/java/io/netty/handler/codec/redis/ArrayHeaderRedisMessage.java b/codec-redis/src/main/java/io/netty/handler/codec/redis/ArrayHeaderRedisMessage.java
new file mode 100644
index 0000000000..1fb893d2d4
--- /dev/null
+++ b/codec-redis/src/main/java/io/netty/handler/codec/redis/ArrayHeaderRedisMessage.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2016 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License, version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package io.netty.handler.codec.redis;
+
+import io.netty.util.internal.StringUtil;
+
+/**
+ * Header of Redis Array Message.
+ */
+public class ArrayHeaderRedisMessage implements RedisMessage {
+
+ private final long length;
+
+ /**
+ * Creates a {@link ArrayHeaderRedisMessage} for the given {@code length}.
+ */
+ public ArrayHeaderRedisMessage(long length) {
+ if (length < RedisConstants.NULL_VALUE) {
+ throw new RedisCodecException("length: " + length + " (expected: >= " + RedisConstants.NULL_VALUE + ")");
+ }
+ this.length = length;
+ }
+
+ /**
+ * Get length of this array object.
+ */
+ public final long length() {
+ return length;
+ }
+
+ /**
+ * Returns whether the content of this message is {@code null}.
+ *
+ * @return indicates whether the content of this message is {@code null}.
+ */
+ public boolean isNull() {
+ return length == RedisConstants.NULL_VALUE;
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder(StringUtil.simpleClassName(this))
+ .append('[')
+ .append("length=")
+ .append(length)
+ .append(']').toString();
+ }
+}
diff --git a/codec-redis/src/main/java/io/netty/handler/codec/redis/ArrayRedisMessage.java b/codec-redis/src/main/java/io/netty/handler/codec/redis/ArrayRedisMessage.java
new file mode 100644
index 0000000000..c9490ab8a5
--- /dev/null
+++ b/codec-redis/src/main/java/io/netty/handler/codec/redis/ArrayRedisMessage.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2016 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License, version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package io.netty.handler.codec.redis;
+
+import io.netty.util.AbstractReferenceCounted;
+import io.netty.util.ReferenceCountUtil;
+import io.netty.util.internal.ObjectUtil;
+import io.netty.util.internal.StringUtil;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Arrays of RESP.
+ */
+public class ArrayRedisMessage extends AbstractReferenceCounted implements RedisMessage {
+
+ private final List children;
+
+ private ArrayRedisMessage() {
+ children = Collections.emptyList();
+ }
+
+ /**
+ * Creates a {@link ArrayRedisMessage} for the given {@code content}.
+ *
+ * @param children the children.
+ */
+ public ArrayRedisMessage(List children) {
+ // do not retain here. children are already retained when created.
+ this.children = ObjectUtil.checkNotNull(children, "children");
+ }
+
+ /**
+ * Get children of this Arrays. It can be null or empty.
+ *
+ * @return list of {@link RedisMessage}s.
+ */
+ public final List children() {
+ return children;
+ }
+
+ /**
+ * Returns whether the content of this message is {@code null}.
+ *
+ * @return indicates whether the content of this message is {@code null}.
+ */
+ public boolean isNull() {
+ return false;
+ }
+
+ @Override
+ protected void deallocate() {
+ for (RedisMessage msg : children) {
+ ReferenceCountUtil.release(msg);
+ }
+ }
+
+ @Override
+ public ArrayRedisMessage touch(Object hint) {
+ for (RedisMessage msg : children) {
+ ReferenceCountUtil.touch(msg);
+ }
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder(StringUtil.simpleClassName(this))
+ .append('[')
+ .append("children=")
+ .append(children.size())
+ .append(']').toString();
+ }
+
+ /**
+ * A predefined null array instance for {@link ArrayRedisMessage}.
+ */
+ public static final ArrayRedisMessage NULL_INSTANCE = new ArrayRedisMessage() {
+ @Override
+ public boolean isNull() {
+ return true;
+ }
+
+ @Override
+ public ArrayRedisMessage retain() {
+ return this;
+ }
+
+ @Override
+ public ArrayRedisMessage retain(int increment) {
+ return this;
+ }
+
+ @Override
+ public ArrayRedisMessage touch() {
+ return this;
+ }
+
+ @Override
+ public ArrayRedisMessage touch(Object hint) {
+ return this;
+ }
+
+ @Override
+ public boolean release() {
+ return false;
+ }
+
+ @Override
+ public boolean release(int decrement) {
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return "NullArrayRedisMessage";
+ }
+ };
+
+ /**
+ * A predefined empty array instance for {@link ArrayRedisMessage}.
+ */
+ public static final ArrayRedisMessage EMPTY_INSTANCE = new ArrayRedisMessage() {
+ @Override
+ public boolean isNull() {
+ return false;
+ }
+
+ @Override
+ public ArrayRedisMessage retain() {
+ return this;
+ }
+
+ @Override
+ public ArrayRedisMessage retain(int increment) {
+ return this;
+ }
+
+ @Override
+ public ArrayRedisMessage touch() {
+ return this;
+ }
+
+ @Override
+ public ArrayRedisMessage touch(Object hint) {
+ return this;
+ }
+
+ @Override
+ public boolean release() {
+ return false;
+ }
+
+ @Override
+ public boolean release(int decrement) {
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return "EmptyArrayRedisMessage";
+ }
+ };
+
+}
diff --git a/codec-redis/src/main/java/io/netty/handler/codec/redis/BulkStringHeaderRedisMessage.java b/codec-redis/src/main/java/io/netty/handler/codec/redis/BulkStringHeaderRedisMessage.java
new file mode 100644
index 0000000000..7bc46812a6
--- /dev/null
+++ b/codec-redis/src/main/java/io/netty/handler/codec/redis/BulkStringHeaderRedisMessage.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2016 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License, version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package io.netty.handler.codec.redis;
+
+/**
+ * The header of Bulk Strings in RESP.
+ */
+public class BulkStringHeaderRedisMessage implements RedisMessage {
+
+ private final int bulkStringLength;
+
+ /**
+ * Creates a {@link BulkStringHeaderRedisMessage}.
+ *
+ * @param bulkStringLength follow content length.
+ */
+ public BulkStringHeaderRedisMessage(int bulkStringLength) {
+ this.bulkStringLength = bulkStringLength;
+ }
+
+ /**
+ * Return {@code bulkStringLength} for this content.
+ */
+ public final int bulkStringLength() {
+ return bulkStringLength;
+ }
+
+ /**
+ * Returns whether the content of this message is {@code null}.
+ *
+ * @return indicates whether the content of this message is {@code null}.
+ */
+ public boolean isNull() {
+ return bulkStringLength == RedisConstants.NULL_VALUE;
+ }
+}
diff --git a/codec-redis/src/main/java/io/netty/handler/codec/redis/BulkStringRedisContent.java b/codec-redis/src/main/java/io/netty/handler/codec/redis/BulkStringRedisContent.java
new file mode 100644
index 0000000000..00598b75dc
--- /dev/null
+++ b/codec-redis/src/main/java/io/netty/handler/codec/redis/BulkStringRedisContent.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2016 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License, version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package io.netty.handler.codec.redis;
+
+import io.netty.buffer.ByteBufHolder;
+import io.netty.channel.ChannelPipeline;
+
+/**
+ * A chunk of bulk strings which is used for Redis chunked transfer-encoding.
+ * {@link RedisDecoder} generates {@link BulkStringRedisContent} after
+ * {@link BulkStringHeaderRedisMessage} when the content is large or the encoding of the content is chunked.
+ * If you prefer not to receive {@link BulkStringRedisContent} in your handler,
+ * place {@link RedisBulkStringAggregator} after {@link RedisDecoder} in the {@link ChannelPipeline}.
+ */
+public interface BulkStringRedisContent extends RedisMessage, ByteBufHolder {
+}
diff --git a/codec-redis/src/main/java/io/netty/handler/codec/redis/DefaultBulkStringRedisContent.java b/codec-redis/src/main/java/io/netty/handler/codec/redis/DefaultBulkStringRedisContent.java
new file mode 100644
index 0000000000..9328700499
--- /dev/null
+++ b/codec-redis/src/main/java/io/netty/handler/codec/redis/DefaultBulkStringRedisContent.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2016 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License, version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package io.netty.handler.codec.redis;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.DefaultByteBufHolder;
+import io.netty.util.internal.StringUtil;
+
+/**
+ * A default implementation of {@link BulkStringRedisContent}.
+ */
+public class DefaultBulkStringRedisContent extends DefaultByteBufHolder implements BulkStringRedisContent {
+
+ /**
+ * Creates a {@link DefaultBulkStringRedisContent} for the given {@code content}.
+ *
+ * @param content the content, can be {@code null}.
+ */
+ public DefaultBulkStringRedisContent(ByteBuf content) {
+ super(content);
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder(StringUtil.simpleClassName(this))
+ .append('[')
+ .append("content=")
+ .append(content())
+ .append(']').toString();
+ }
+}
diff --git a/codec-redis/src/main/java/io/netty/handler/codec/redis/DefaultLastBulkStringRedisContent.java b/codec-redis/src/main/java/io/netty/handler/codec/redis/DefaultLastBulkStringRedisContent.java
new file mode 100644
index 0000000000..f171ccd32c
--- /dev/null
+++ b/codec-redis/src/main/java/io/netty/handler/codec/redis/DefaultLastBulkStringRedisContent.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2016 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License, version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package io.netty.handler.codec.redis;
+
+import io.netty.buffer.ByteBuf;
+
+/**
+ * A default implementation for {@link LastBulkStringRedisContent}.
+ */
+public final class DefaultLastBulkStringRedisContent extends DefaultBulkStringRedisContent
+ implements LastBulkStringRedisContent {
+
+ /**
+ * Creates a {@link DefaultLastBulkStringRedisContent} for the given {@code content}.
+ * @param content the content, can be {@code null}.
+ */
+ public DefaultLastBulkStringRedisContent(ByteBuf content) {
+ super(content);
+ }
+}
diff --git a/codec-redis/src/main/java/io/netty/handler/codec/redis/ErrorRedisMessage.java b/codec-redis/src/main/java/io/netty/handler/codec/redis/ErrorRedisMessage.java
new file mode 100644
index 0000000000..fd3f4b161b
--- /dev/null
+++ b/codec-redis/src/main/java/io/netty/handler/codec/redis/ErrorRedisMessage.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2016 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License, version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package io.netty.handler.codec.redis;
+
+import io.netty.util.internal.StringUtil;
+
+/**
+ * Errors of RESP.
+ */
+public final class ErrorRedisMessage extends AbstractStringRedisMessage {
+
+ /**
+ * Creates a {@link ErrorRedisMessage} from {@code content}.
+ *
+ * @param content the message content, must not be {@code null}.
+ */
+ public ErrorRedisMessage(String content) {
+ super(content);
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder(StringUtil.simpleClassName(this))
+ .append('[')
+ .append("content=")
+ .append(content())
+ .append(']').toString();
+ }
+}
diff --git a/codec-redis/src/main/java/io/netty/handler/codec/redis/FixedRedisMessagePool.java b/codec-redis/src/main/java/io/netty/handler/codec/redis/FixedRedisMessagePool.java
new file mode 100644
index 0000000000..4b910715cb
--- /dev/null
+++ b/codec-redis/src/main/java/io/netty/handler/codec/redis/FixedRedisMessagePool.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2016 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License, version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package io.netty.handler.codec.redis;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.util.CharsetUtil;
+import io.netty.util.collection.LongObjectHashMap;
+import io.netty.util.collection.LongObjectMap;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A default fixed redis message pool.
+ */
+public final class FixedRedisMessagePool implements RedisMessagePool {
+
+ private static final String[] DEFAULT_SIMPLE_STRINGS = {
+ "OK",
+ "PONG",
+ "QUEUED",
+ };
+
+ private static final String[] DEFAULT_ERRORS = {
+ "ERR",
+ "ERR index out of range",
+ "ERR no such key",
+ "ERR source and destination objects are the same",
+ "ERR syntax error",
+ "BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE.",
+ "BUSYKEY Target key name already exists.",
+ "EXECABORT Transaction discarded because of previous errors.",
+ "LOADING Redis is loading the dataset in memory",
+ "MASTERDOWN Link with MASTER is down and slave-serve-stale-data is set to 'no'.",
+ "MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk. " +
+ "Commands that may modify the data set are disabled. Please check Redis logs for details " +
+ "about the error.",
+ "NOAUTH Authentication required.",
+ "NOREPLICAS Not enough good slaves to write.",
+ "NOSCRIPT No matching script. Please use EVAL.",
+ "OOM command not allowed when used memory > 'maxmemory'.",
+ "READONLY You can't write against a read only slave.",
+ "WRONGTYPE Operation against a key holding the wrong kind of value",
+ };
+
+ private static final long MIN_CACHED_INTEGER_NUMBER = RedisConstants.NULL_VALUE; // inclusive
+ private static final long MAX_CACHED_INTEGER_NUMBER = 128; // exclusive
+
+ // cached integer size cannot larger than `int` range because of Collection.
+ private static final int SIZE_CACHED_INTEGER_NUMBER = (int) (MAX_CACHED_INTEGER_NUMBER - MIN_CACHED_INTEGER_NUMBER);
+
+ /**
+ * A shared object for {@link FixedRedisMessagePool}.
+ */
+ public static final FixedRedisMessagePool INSTANCE = new FixedRedisMessagePool();
+
+ // internal caches.
+ private Map byteBufToSimpleStrings;
+ private Map stringToSimpleStrings;
+ private Map byteBufToErrors;
+ private Map stringToErrors;
+ private Map byteBufToIntegers;
+ private LongObjectMap longToIntegers;
+ private LongObjectMap longToByteBufs;
+
+ /**
+ * Creates a {@link FixedRedisMessagePool} instance.
+ */
+ private FixedRedisMessagePool() {
+ byteBufToSimpleStrings = new HashMap(DEFAULT_SIMPLE_STRINGS.length, 1.0f);
+ stringToSimpleStrings = new HashMap(DEFAULT_SIMPLE_STRINGS.length, 1.0f);
+ for (String message : DEFAULT_SIMPLE_STRINGS) {
+ ByteBuf key = Unpooled.unmodifiableBuffer(
+ Unpooled.unreleasableBuffer(Unpooled.wrappedBuffer(message.getBytes(CharsetUtil.UTF_8))));
+ SimpleStringRedisMessage cached = new SimpleStringRedisMessage(message);
+ byteBufToSimpleStrings.put(key, cached);
+ stringToSimpleStrings.put(message, cached);
+ }
+
+ byteBufToErrors = new HashMap(DEFAULT_ERRORS.length, 1.0f);
+ stringToErrors = new HashMap(DEFAULT_ERRORS.length, 1.0f);
+ for (String message : DEFAULT_ERRORS) {
+ ByteBuf key = Unpooled.unmodifiableBuffer(
+ Unpooled.unreleasableBuffer(Unpooled.wrappedBuffer(message.getBytes(CharsetUtil.UTF_8))));
+ ErrorRedisMessage cached = new ErrorRedisMessage(message);
+ byteBufToErrors.put(key, cached);
+ stringToErrors.put(message, cached);
+ }
+
+ byteBufToIntegers = new HashMap(SIZE_CACHED_INTEGER_NUMBER, 1.0f);
+ longToIntegers = new LongObjectHashMap(SIZE_CACHED_INTEGER_NUMBER, 1.0f);
+ longToByteBufs = new LongObjectHashMap(SIZE_CACHED_INTEGER_NUMBER, 1.0f);
+ for (long value = MIN_CACHED_INTEGER_NUMBER; value < MAX_CACHED_INTEGER_NUMBER; value++) {
+ byte[] keyBytes = RedisCodecUtil.longToAsciiBytes(value);
+ ByteBuf keyByteBuf = Unpooled.unmodifiableBuffer(Unpooled.unreleasableBuffer(
+ Unpooled.wrappedBuffer(keyBytes)));
+ IntegerRedisMessage cached = new IntegerRedisMessage(value);
+ byteBufToIntegers.put(keyByteBuf, cached);
+ longToIntegers.put(value, cached);
+ longToByteBufs.put(value, keyBytes);
+ }
+ }
+
+ @Override
+ public SimpleStringRedisMessage getSimpleString(String content) {
+ return stringToSimpleStrings.get(content);
+ }
+
+ @Override
+ public SimpleStringRedisMessage getSimpleString(ByteBuf content) {
+ return byteBufToSimpleStrings.get(content);
+ }
+
+ @Override
+ public ErrorRedisMessage getError(String content) {
+ return stringToErrors.get(content);
+ }
+
+ @Override
+ public ErrorRedisMessage getError(ByteBuf content) {
+ return byteBufToErrors.get(content);
+ }
+
+ @Override
+ public IntegerRedisMessage getInteger(long value) {
+ return longToIntegers.get(value);
+ }
+
+ @Override
+ public IntegerRedisMessage getInteger(ByteBuf content) {
+ return byteBufToIntegers.get(content);
+ }
+
+ @Override
+ public byte[] getByteBufOfInteger(long value) {
+ return longToByteBufs.get(value);
+ }
+}
diff --git a/codec-redis/src/main/java/io/netty/handler/codec/redis/FullBulkStringRedisMessage.java b/codec-redis/src/main/java/io/netty/handler/codec/redis/FullBulkStringRedisMessage.java
new file mode 100644
index 0000000000..54213139c8
--- /dev/null
+++ b/codec-redis/src/main/java/io/netty/handler/codec/redis/FullBulkStringRedisMessage.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2016 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License, version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package io.netty.handler.codec.redis;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.DefaultByteBufHolder;
+import io.netty.buffer.Unpooled;
+import io.netty.util.internal.StringUtil;
+
+/**
+ * An aggregated bulk string of RESP.
+ */
+public class FullBulkStringRedisMessage extends DefaultByteBufHolder implements LastBulkStringRedisContent {
+
+ private FullBulkStringRedisMessage() {
+ this(Unpooled.EMPTY_BUFFER);
+ }
+
+ /**
+ * Creates a {@link FullBulkStringRedisMessage} for the given {@code content}.
+ *
+ * @param content the content, must not be {@code null}. If content is null or empty,
+ * use {@link FullBulkStringRedisMessage#NULL_INSTANCE} or {@link FullBulkStringRedisMessage#EMPTY_INSTANCE}
+ * instead of constructor.
+ */
+ public FullBulkStringRedisMessage(ByteBuf content) {
+ super(content);
+ }
+
+ /**
+ * Returns whether the content of this message is {@code null}.
+ *
+ * @return indicates whether the content of this message is {@code null}.
+ */
+ public boolean isNull() {
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder(StringUtil.simpleClassName(this))
+ .append('[')
+ .append("content=")
+ .append(content())
+ .append(']').toString();
+ }
+
+ /**
+ * A predefined null instance of {@link FullBulkStringRedisMessage}.
+ */
+ public static final FullBulkStringRedisMessage NULL_INSTANCE = new FullBulkStringRedisMessage() {
+ @Override
+ public boolean isNull() {
+ return true;
+ }
+
+ @Override
+ public ByteBuf content() {
+ return Unpooled.EMPTY_BUFFER;
+ }
+
+ @Override
+ public FullBulkStringRedisMessage copy() {
+ return this;
+ }
+
+ @Override
+ public FullBulkStringRedisMessage duplicate() {
+ return this;
+ }
+
+ @Override
+ public int refCnt() {
+ return 1;
+ }
+
+ @Override
+ public FullBulkStringRedisMessage retain() {
+ return this;
+ }
+
+ @Override
+ public FullBulkStringRedisMessage retain(int increment) {
+ return this;
+ }
+
+ @Override
+ public FullBulkStringRedisMessage touch() {
+ return this;
+ }
+
+ @Override
+ public FullBulkStringRedisMessage touch(Object hint) {
+ return this;
+ }
+
+ @Override
+ public boolean release() {
+ return false;
+ }
+
+ @Override
+ public boolean release(int decrement) {
+ return false;
+ }
+ };
+
+ /**
+ * A predefined empty instance of {@link FullBulkStringRedisMessage}.
+ */
+ public static final FullBulkStringRedisMessage EMPTY_INSTANCE = new FullBulkStringRedisMessage() {
+ @Override
+ public ByteBuf content() {
+ return Unpooled.EMPTY_BUFFER;
+ }
+
+ @Override
+ public FullBulkStringRedisMessage copy() {
+ return EMPTY_INSTANCE;
+ }
+
+ @Override
+ public FullBulkStringRedisMessage duplicate() {
+ return this;
+ }
+
+ @Override
+ public int refCnt() {
+ return 1;
+ }
+
+ @Override
+ public FullBulkStringRedisMessage retain() {
+ return this;
+ }
+
+ @Override
+ public FullBulkStringRedisMessage retain(int increment) {
+ return this;
+ }
+
+ @Override
+ public FullBulkStringRedisMessage touch() {
+ return this;
+ }
+
+ @Override
+ public FullBulkStringRedisMessage touch(Object hint) {
+ return this;
+ }
+
+ @Override
+ public boolean release() {
+ return false;
+ }
+
+ @Override
+ public boolean release(int decrement) {
+ return false;
+ }
+ };
+}
diff --git a/codec-redis/src/main/java/io/netty/handler/codec/redis/IntegerRedisMessage.java b/codec-redis/src/main/java/io/netty/handler/codec/redis/IntegerRedisMessage.java
new file mode 100644
index 0000000000..abac2a910b
--- /dev/null
+++ b/codec-redis/src/main/java/io/netty/handler/codec/redis/IntegerRedisMessage.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2016 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License, version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package io.netty.handler.codec.redis;
+
+import io.netty.util.internal.StringUtil;
+
+/**
+ * Integers of RESP.
+ */
+public final class IntegerRedisMessage implements RedisMessage {
+
+ private final long value;
+
+ /**
+ * Creates a {@link IntegerRedisMessage} for the given {@code content}.
+ *
+ * @param value the message content.
+ */
+ public IntegerRedisMessage(long value) {
+ this.value = value;
+ }
+
+ /**
+ * Get long value of this {@link IntegerRedisMessage}.
+ *
+ * @return long value
+ */
+ public long value() {
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder(StringUtil.simpleClassName(this))
+ .append('[')
+ .append("value=")
+ .append(value)
+ .append(']').toString();
+ }
+}
diff --git a/codec-redis/src/main/java/io/netty/handler/codec/redis/LastBulkStringRedisContent.java b/codec-redis/src/main/java/io/netty/handler/codec/redis/LastBulkStringRedisContent.java
new file mode 100644
index 0000000000..e8fc0d9be8
--- /dev/null
+++ b/codec-redis/src/main/java/io/netty/handler/codec/redis/LastBulkStringRedisContent.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2016 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License, version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package io.netty.handler.codec.redis;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufHolder;
+import io.netty.buffer.Unpooled;
+
+/**
+ * A last chunk of Bulk Strings.
+ */
+public interface LastBulkStringRedisContent extends BulkStringRedisContent {
+
+ /**
+ * The 'end of content' marker in chunked encoding.
+ */
+ LastBulkStringRedisContent EMPTY_LAST_CONTENT = new LastBulkStringRedisContent() {
+
+ @Override
+ public ByteBuf content() {
+ return Unpooled.EMPTY_BUFFER;
+ }
+
+ @Override
+ public ByteBufHolder copy() {
+ return this;
+ }
+
+ @Override
+ public ByteBufHolder retain(int increment) {
+ return this;
+ }
+
+ @Override
+ public ByteBufHolder retain() {
+ return this;
+ }
+
+ @Override
+ public int refCnt() {
+ return 1;
+ }
+
+ @Override
+ public ByteBufHolder touch() {
+ return this;
+ }
+
+ @Override
+ public ByteBufHolder touch(Object hint) {
+ return this;
+ }
+
+ @Override
+ public boolean release() {
+ return false;
+ }
+
+ @Override
+ public boolean release(int decrement) {
+ return false;
+ }
+
+ @Override
+ public ByteBufHolder duplicate() {
+ return this;
+ }
+ };
+}
diff --git a/codec-redis/src/main/java/io/netty/handler/codec/redis/RedisArrayAggregator.java b/codec-redis/src/main/java/io/netty/handler/codec/redis/RedisArrayAggregator.java
new file mode 100644
index 0000000000..b8444fd3d0
--- /dev/null
+++ b/codec-redis/src/main/java/io/netty/handler/codec/redis/RedisArrayAggregator.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2016 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License, version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package io.netty.handler.codec.redis;
+
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.CodecException;
+import io.netty.handler.codec.MessageToMessageDecoder;
+import io.netty.util.ReferenceCountUtil;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.List;
+
+/**
+ * Aggregates {@link RedisMessage} parts into {@link ArrayRedisMessage}. This decoder
+ * should be used together with {@link RedisDecoder}.
+ */
+public final class RedisArrayAggregator extends MessageToMessageDecoder {
+
+ private final Deque depths = new ArrayDeque(4);
+
+ @Override
+ protected void decode(ChannelHandlerContext ctx, RedisMessage msg, List