All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.navercorp.nbasearc.gcp.RedisDecoder Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2015 NAVER Corp.
 *
 * Licensed 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 com.navercorp.nbasearc.gcp;

import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import io.netty.buffer.ByteBuf;
import redis.clients.jedis.exceptions.JedisConnectionException;

/**
 * @author [email protected]
 * @Author [email protected] (maintenance)
 *
 */
class RedisDecoder {
    private final static Logger log = LoggerFactory.getLogger(RedisDecoder.class);

    private final byte[] lookasideBuffer;
    private int lookasideBufferReaderIndex;
    private int lookasideBufferLength;
    
    RedisDecoder() {
        // 32 : 20(bytes of a maximum long) + 2(\r\n) + 1($ or *)
        //      and a power of 2
        this.lookasideBuffer = new byte[32];
    }

    void getFrames(ByteBuf in, List out) {
        while (true) {
            in.markReaderIndex();
            final int stx = in.readerIndex();
            
            if (hasFrame(in) == false) {
                in.resetReaderIndex();
                break;
            }
            
            int length = in.readerIndex() - stx;
            byte[] readBytes = new byte[length];

            in.resetReaderIndex();
            in.readBytes(readBytes);
            out.add(readBytes);
        }
    }
    
    private int readableLookasideBufferBytes() {
        return lookasideBufferLength - lookasideBufferReaderIndex;
    }

    private boolean hasFrame(ByteBuf in) {
        if (in.isReadable() == false) {
            return false;
        }
     
        lookasideBufferLength = Math.min(in.readableBytes(), lookasideBuffer.length);
        in.readBytes(lookasideBuffer, 0, lookasideBufferLength);
        lookasideBufferReaderIndex = 0;
        in.readerIndex(in.readerIndex() - lookasideBufferLength);
        
        byte b = lookasideBuffer[lookasideBufferReaderIndex++];
        in.skipBytes(1);
        if (b == MINUS_BYTE) {
            return processError(in);
        } else if (b == ASTERISK_BYTE) {
            return processMultiBulkReply(in);
        } else if (b == COLON_BYTE) {
            return processInteger(in);
        } else if (b == DOLLAR_BYTE) {
            return processBulkReply(in);
        } else if (b == PLUS_BYTE) {
            return processStatusCodeReply(in);
        } else {
            throw new JedisConnectionException("Unknown reply: " + (char) b);
        }
    }

    /**
     * Process error.
     *
     * @param is the is
     */
    private boolean processError(ByteBuf in) {
        int length = lineLength(in);
        if (length == 0) {
            return false;
        }
        
        in.skipBytes(length);
        return true;
    }
    
    /**
     * Process multi bulk reply.
     *
     * @param is the is
     * @return the list
     */
    private boolean processMultiBulkReply(ByteBuf in) {
        final int length = lineLength();
        if (length == 0) {
            return false;
        }
        in.skipBytes(length);
        
        int num = readInt(length - 2);
        if (num == -1) {
            return true;
        }
        
        for (int i = 0; i < num; i++) {
            if (hasFrame(in) == false) {
                return false;
            }
        }
        return true;
    }

    /**
     * Process integer.
     *
     * @param is the is
     * @return the long
     */
    private boolean processInteger(ByteBuf in) {
        int length = lineLength();
        if (length == 0) {
            return false;
        }
        
        in.skipBytes(length);
        return true;
    }

    /**
     * Process bulk reply.
     *
     * @param is the is
     * @return the byte[]
     */
    private boolean processBulkReply(ByteBuf in) {
        int length = lineLength();
        if (length == 0) {
            return false;
        }
        in.skipBytes(length);

        int numBytes = readInt(length - 2); // 2 == \r\n
        if (numBytes == -1) {
            return true;
        }
        
        if (in.readableBytes() < numBytes + 2) {
            return false;
        }
        
        in.skipBytes(numBytes + 2);

        return true;
    }

    /**
     * Process status code reply.
     *
     * @param is the is
     * @return the byte[]
     */
    private boolean processStatusCodeReply(ByteBuf in) {
        int length = lineLength();
        if (length == 0) {
            return false;
        }
        
        in.skipBytes(length);
        return true;
    }
    
    private int readInt(int lineLength) {
        /* minus */
        boolean minus = false;
        
        if (lookasideBuffer[lookasideBufferReaderIndex] == MINUS_BYTE) {
            minus = true;
            
            lookasideBufferReaderIndex++;
            lineLength--;
        }

        if (lineLength <= 0) {
            throw new RuntimeException("Redis protocol exception; malformed number format");
        }
        
        /* get value */
        int value = 0;
        
        for (int i = 0; i < lineLength; ++i) {
            byte b = lookasideBuffer[lookasideBufferReaderIndex++];
            if (b < '0' || '9' < b) {
                throw new RuntimeException("Reids protocol exception; invalid byte in number format; byte=" + b);
            }
            
            value = value * 10 + (b - '0');
        }
        
        if (minus) {
            value = -value;
        }
        
        return value;
    }
    
    private int lineLength(ByteBuf in) {
        int readableBytes = in.readableBytes();
        if (readableBytes < 2) {
            return -1;
        }       

        /* CR */
        int length = in.bytesBefore(CR_BYTE);
        if (length < 0) {
            return -1;
        }
        
        if (readableBytes < length + 2) {
            return -1;
        }
        
        /* LF */
        byte eolLF = in.getByte(in.readerIndex() + length + 1);
        if (eolLF != LF_BYTE) {
            throw new RuntimeException("Redis protocol exception; malformed end of line; byte=" + eolLF);
        }
        
        return length + 2;
    }
    
    private int lineLength() {
        final int stx = lookasideBufferReaderIndex;
        try {
            while (readableLookasideBufferBytes() > 0) {
                if (lookasideBuffer[lookasideBufferReaderIndex++] == CR_BYTE) {
                    if (readableLookasideBufferBytes() < 1) {
                        return 0;
                    }
    
                    if (lookasideBuffer[lookasideBufferReaderIndex++] == LF_BYTE) {
                        return lookasideBufferReaderIndex - stx;
                    }
                }
            }
            
            if (lookasideBufferReaderIndex >= lookasideBuffer.length) {
                throw new RuntimeException("Redis protocol exception; malformed end of line");
            }
            
            return 0;
        } finally {
            lookasideBufferReaderIndex = stx;
        }
    }

    /**
     * The Constant DEFAULT_DATABASE.
     */
    static final int DEFAULT_DATABASE = 0;

    /**
     * The Constant CHARSET.
     */
    static final String CHARSET = "UTF-8";

    /**
     * The Constant DOLLAR_BYTE.
     */
    static final byte DOLLAR_BYTE = '$';

    /**
     * The Constant ASTERISK_BYTE.
     */
    static final byte ASTERISK_BYTE = '*';

    /**
     * The Constant PLUS_BYTE.
     */
    static final byte PLUS_BYTE = '+';

    /**
     * The Constant MINUS_BYTE.
     */
    static final byte MINUS_BYTE = '-';

    /**
     * The Constant COLON_BYTE.
     */
    static final byte COLON_BYTE = ':';
    
    static final byte CR_BYTE = '\r';
    static final byte LF_BYTE = '\n';
}
/* end of class RedisDecoder */




© 2015 - 2024 Weber Informatics LLC | Privacy Policy