Package com.lambdaworks.redis.protocol

Source Code of com.lambdaworks.redis.protocol.RedisStateMachine$State

// Copyright (C) 2011 - Will Glozer.  All rights reserved.

package com.lambdaworks.redis.protocol;

import com.lambdaworks.redis.RedisException;
import io.netty.buffer.ByteBuf;

import java.nio.ByteBuffer;
import java.util.LinkedList;

import static com.lambdaworks.redis.protocol.Charsets.buffer;
import static com.lambdaworks.redis.protocol.RedisStateMachine.State.Type.*;

/**
* State machine that decodes redis server responses encoded according to the
* <a href="http://redis.io/topics/protocol">Unified Request Protocol</a>.
*
* @author Will Glozer
*/
public class RedisStateMachine<K, V> {
    private static final ByteBuffer QUEUED = buffer("QUEUED");

    static class State {
        enum Type { SINGLE, ERROR, INTEGER, BULK, MULTI, BYTES }
        Type type  = null;
        int  count = -1;
    }

    private LinkedList<State> stack;

    /**
     * Initialize a new instance.
     */
    public RedisStateMachine() {
        stack = new LinkedList<State>();
    }

    /**
     * Attempt to decode a redis response and return a flag indicating whether a complete
     * response was read.
     *
     * @param buffer    Buffer containing data from the server.
     * @param output    Current command output.
     *
     * @return true if a complete response was read.
     */
    public boolean decode(ByteBuf buffer, CommandOutput<K, V, ?> output) {
        int length, end;
        ByteBuffer bytes;

        if (stack.isEmpty()) {
            stack.add(new State());
        }

        if (output == null) {
            return stack.isEmpty();
        }

        loop:

        while (!stack.isEmpty()) {
            State state = stack.peek();

            if (state.type == null) {
                if (!buffer.isReadable()) break;
                state.type = readReplyType(buffer);
                buffer.markReaderIndex();
            }

            switch (state.type) {
                case SINGLE:
                    if ((bytes = readLine(buffer)) == null) break loop;
                    if (!QUEUED.equals(bytes)) {
                        output.set(bytes);
                    }
                    break;
                case ERROR:
                    if ((bytes = readLine(buffer)) == null) break loop;
                    output.setError(bytes);
                    break;
                case INTEGER:
                    if ((end = findLineEnd(buffer)) == -1) break loop;
                    output.set(readLong(buffer, buffer.readerIndex(), end));
                    break;
                case BULK:
                    if ((end = findLineEnd(buffer)) == -1) break loop;
                    length = (int) readLong(buffer, buffer.readerIndex(), end);
                    if (length == -1) {
                        output.set(null);
                    } else {
                        state.type = BYTES;
                        state.count = length + 2;
                        buffer.markReaderIndex();
                        continue loop;
                    }
                    break;
                case MULTI:
                    if (state.count == -1) {
                        if ((end = findLineEnd(buffer)) == -1) break loop;
                        length = (int) readLong(buffer, buffer.readerIndex(), end);
                        state.count = length;
                        buffer.markReaderIndex();
                    }

                    if (state.count <= 0) break;

                    state.count--;
                    stack.addFirst(new State());
                    continue loop;
                case BYTES:
                    if ((bytes = readBytes(buffer, state.count)) == null) break loop;
                    output.set(bytes);
            }

            buffer.markReaderIndex();
            stack.remove();
            output.complete(stack.size());
        }

        return stack.isEmpty();
    }

    private int findLineEnd(ByteBuf buffer) {
        int start = buffer.readerIndex();
        int index = buffer.indexOf(start, buffer.writerIndex(), (byte) '\n');
        return (index > 0 && buffer.getByte(index - 1) == '\r') ? index : -1;
    }

    private State.Type readReplyType(ByteBuf buffer) {
        switch (buffer.readByte()) {
            case '+': return SINGLE;
            case '-': return ERROR;
            case ':': return INTEGER;
            case '$': return BULK;
            case '*': return MULTI;
            defaultthrow new RedisException("Invalid first byte");
        }
    }

    private long readLong(ByteBuf buffer, int start, int end) {
        long value = 0;

        boolean negative = buffer.getByte(start) == '-';
        int offset = negative ? start + 1 : start;
        while (offset < end - 1) {
            int digit = buffer.getByte(offset++) - '0';
            value = value * 10 - digit;
        }
        if (!negative) value = -value;
        buffer.readerIndex(end + 1);

        return value;
    }

    private ByteBuffer readLine(ByteBuf buffer) {
        ByteBuffer bytes = null;
        int end = findLineEnd(buffer);
        if (end > -1) {
            int start = buffer.readerIndex();
            bytes = buffer.nioBuffer(start, end - start - 1);
            buffer.readerIndex(end + 1);
        }
        return bytes;
    }

    private ByteBuffer readBytes(ByteBuf buffer, int count) {
        ByteBuffer bytes = null;
        if (buffer.readableBytes() >= count) {
            bytes = buffer.nioBuffer(buffer.readerIndex(), count - 2);
            buffer.readerIndex(buffer.readerIndex() + count);
        }
        return bytes;
    }
}
TOP

Related Classes of com.lambdaworks.redis.protocol.RedisStateMachine$State

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.