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

org.apache.geode.redis.internal.Coder Maven / Gradle / Ivy

Go to download

Apache Geode provides a database-like consistency model, reliable transaction processing and a shared-nothing architecture to maintain very low latency performance with high concurrency processing

There is a newer version: 1.15.1
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
 * agreements. See the NOTICE file distributed with this work for additional information regarding
 * copyright ownership. The ASF 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 org.apache.geode.redis.internal;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;

import java.io.UnsupportedEncodingException;
import java.text.DecimalFormat;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.geode.cache.EntryDestroyedException;
import org.apache.geode.cache.query.Struct;

/**
 * This is a safe encoder and decoder for all redis matching needs
 * 
 *
 */
public class Coder {

  /*
   * Take no chances on char to byte conversions with default charsets on jvms, so we'll hard code
   * the UTF-8 symbol values as bytes here
   */


  /**
   * byte identifier of a bulk string
   */
  public static final byte BULK_STRING_ID = 36; // '$'

  /**
   * byte identifier of an array
   */
  public static final byte ARRAY_ID = 42; // '*'

  /**
   * byte identifier of an error
   */
  public static final byte ERROR_ID = 45; // '-'

  /**
   * byte identifier of an integer
   */
  public static final byte INTEGER_ID = 58; // ':'

  public static final byte OPEN_BRACE_ID = 0x28; // '('
  public static final byte OPEN_BRACKET_ID = 0x5b; // '['
  public static final byte HYPHEN_ID = 0x2d; // '-'
  public static final byte PLUS_ID = 0x2b; // '+'
  public static final byte NUMBER_1_BYTE = 0x31; // '1'
  /**
   * byte identifier of a simple string
   */
  public static final byte SIMPLE_STRING_ID = 43; // '+'
  public static final String CRLF = "\r\n";
  public static final byte[] CRLFar = stringToBytes(CRLF); // {13, 10} == {'\r', '\n'}

  /**
   * byte array of a nil response
   */
  public static final byte[] bNIL = stringToBytes("$-1\r\n"); // {'$', '-', '1', '\r', '\n'};

  /**
   * byte array of an empty string
   */
  public static final byte[] bEMPTY_ARRAY = stringToBytes("*0\r\n"); // {'*', '0', '\r', '\n'};

  public static final byte[] err = stringToBytes("ERR ");
  public static final byte[] noAuth = stringToBytes("NOAUTH ");
  public static final byte[] wrongType = stringToBytes("WRONGTYPE ");

  /**
   * The charset being used by this coder, {@value #CHARSET}.
   */
  public static final String CHARSET = "UTF-8";

  protected static final DecimalFormat decimalFormatter = new DecimalFormat("#");
  static {
    decimalFormatter.setMaximumFractionDigits(10);
  }

  /**
   * Positive infinity string
   */
  public static final String P_INF = "+inf";

  /**
   * Negative infinity string
   */
  public static final String N_INF = "-inf";

  public static final ByteBuf getBulkStringResponse(ByteBufAllocator alloc, byte[] value) {
    ByteBuf response = alloc.buffer(value.length + 20);
    response.writeByte(BULK_STRING_ID);
    response.writeBytes(intToBytes(value.length));
    response.writeBytes(CRLFar);
    response.writeBytes(value);
    response.writeBytes(CRLFar);
    return response;
  }

  public static final ByteBuf getBulkStringResponse(ByteBufAllocator alloc, double value) {
    ByteBuf response = alloc.buffer();
    byte[] doub = doubleToBytes(value);
    response.writeByte(BULK_STRING_ID);
    response.writeBytes(intToBytes(doub.length));
    response.writeBytes(CRLFar);
    response.writeBytes(doub);
    response.writeBytes(CRLFar);
    return response;
  }

  public static final ByteBuf getBulkStringResponse(ByteBufAllocator alloc, String value) {
    byte[] valueAr = stringToBytes(value);
    int length = valueAr == null ? 0 : valueAr.length;
    ByteBuf response = alloc.buffer(length + 20);
    response.writeByte(BULK_STRING_ID);
    response.writeBytes(intToBytes(length));
    response.writeBytes(CRLFar);
    response.writeBytes(valueAr);
    response.writeBytes(CRLFar);
    return response;
  }

  public static final ByteBuf getBulkStringArrayResponse(ByteBufAllocator alloc,
      List items) {
    Iterator it = items.iterator();
    ByteBuf response = alloc.buffer();
    response.writeByte(ARRAY_ID);
    response.writeBytes(intToBytes(items.size()));
    response.writeBytes(CRLFar);
    while (it.hasNext()) {
      String next = it.next();
      response.writeByte(BULK_STRING_ID);
      response.writeBytes(intToBytes(next.length()));
      response.writeBytes(CRLFar);
      response.writeBytes(stringToBytes(next));
      response.writeBytes(CRLFar);
    }
    return response;
  }

  public static final ByteBuf getBulkStringArrayResponse(ByteBufAllocator alloc,
      Collection items) {
    Iterator it = items.iterator();
    ByteBuf response = alloc.buffer();
    response.writeByte(ARRAY_ID);
    response.writeBytes(intToBytes(items.size()));
    response.writeBytes(CRLFar);
    while (it.hasNext()) {
      ByteArrayWrapper nextWrapper = it.next();
      if (nextWrapper != null) {
        response.writeByte(BULK_STRING_ID);
        response.writeBytes(intToBytes(nextWrapper.length()));
        response.writeBytes(CRLFar);
        response.writeBytes(nextWrapper.toBytes());
        response.writeBytes(CRLFar);
      } else
        response.writeBytes(getNilResponse(alloc));
    }

    return response;
  }

  public static final ByteBuf getKeyValArrayResponse(ByteBufAllocator alloc,
      Collection> items) {
    Iterator> it = items.iterator();
    ByteBuf response = alloc.buffer();
    response.writeByte(ARRAY_ID);

    int size = 0;
    ByteBuf tmp = alloc.buffer();
    while (it.hasNext()) {
      Map.Entry next = it.next();
      byte[] key;
      byte[] nextByteArray;
      try {
        key = next.getKey().toBytes();
        nextByteArray = next.getValue().toBytes();
      } catch (EntryDestroyedException e) {
        continue;
      }
      tmp.writeByte(BULK_STRING_ID); // Add key
      tmp.writeBytes(intToBytes(key.length));
      tmp.writeBytes(CRLFar);
      tmp.writeBytes(key);
      tmp.writeBytes(CRLFar);
      tmp.writeByte(BULK_STRING_ID); // Add value
      tmp.writeBytes(intToBytes(nextByteArray.length));
      tmp.writeBytes(CRLFar);
      tmp.writeBytes(nextByteArray);
      tmp.writeBytes(CRLFar);
      size++;
    }

    response.writeBytes(intToBytes(size * 2));
    response.writeBytes(CRLFar);
    response.writeBytes(tmp);

    tmp.release();

    return response;
  }

  public static final ByteBuf getScanResponse(ByteBufAllocator alloc, List items) {
    ByteBuf response = alloc.buffer();
    response.writeByte(ARRAY_ID);
    response.writeBytes(intToBytes(2));
    response.writeBytes(CRLFar);
    response.writeByte(BULK_STRING_ID);
    byte[] cursor = stringToBytes((String) items.get(0));
    response.writeBytes(intToBytes(cursor.length));
    response.writeBytes(CRLFar);
    response.writeBytes(cursor);
    response.writeBytes(CRLFar);
    items = items.subList(1, items.size());
    Iterator it = items.iterator();
    response.writeByte(ARRAY_ID);
    response.writeBytes(intToBytes(items.size()));
    response.writeBytes(CRLFar);

    while (it.hasNext()) {
      Object nextObject = it.next();
      if (nextObject instanceof String) {
        String next = (String) nextObject;
        response.writeByte(BULK_STRING_ID);
        response.writeBytes(intToBytes(next.length()));
        response.writeBytes(CRLFar);
        response.writeBytes(stringToBytes(next));
        response.writeBytes(CRLFar);
      } else if (nextObject instanceof ByteArrayWrapper) {
        byte[] next = ((ByteArrayWrapper) nextObject).toBytes();
        response.writeByte(BULK_STRING_ID);
        response.writeBytes(intToBytes(next.length));
        response.writeBytes(CRLFar);
        response.writeBytes(next);
        response.writeBytes(CRLFar);
      }
    }
    return response;
  }

  public static final ByteBuf getEmptyArrayResponse(ByteBufAllocator alloc) {
    ByteBuf buf = alloc.buffer().writeBytes(bEMPTY_ARRAY);
    return buf;
  }

  public static final ByteBuf getSimpleStringResponse(ByteBufAllocator alloc, String string) {
    byte[] simpAr = stringToBytes(string);

    ByteBuf response = alloc.buffer(simpAr.length + 20);
    response.writeByte(SIMPLE_STRING_ID);
    response.writeBytes(simpAr);
    response.writeBytes(CRLFar);
    return response;
  }

  public static final ByteBuf getErrorResponse(ByteBufAllocator alloc, String error) {
    byte[] errorAr = stringToBytes(error);
    ByteBuf response = alloc.buffer(errorAr.length + 25);
    response.writeByte(ERROR_ID);
    response.writeBytes(err);
    response.writeBytes(errorAr);
    response.writeBytes(CRLFar);
    return response;
  }

  public static final ByteBuf getNoAuthResponse(ByteBufAllocator alloc, String error) {
    byte[] errorAr = stringToBytes(error);
    ByteBuf response = alloc.buffer(errorAr.length + 25);
    response.writeByte(ERROR_ID);
    response.writeBytes(noAuth);
    response.writeBytes(errorAr);
    response.writeBytes(CRLFar);
    return response;
  }

  public static final ByteBuf getWrongTypeResponse(ByteBufAllocator alloc, String error) {
    byte[] errorAr = stringToBytes(error);
    ByteBuf response = alloc.buffer(errorAr.length + 31);
    response.writeByte(ERROR_ID);
    response.writeBytes(wrongType);
    response.writeBytes(errorAr);
    response.writeBytes(CRLFar);
    return response;
  }

  public static final ByteBuf getIntegerResponse(ByteBufAllocator alloc, int integer) {
    ByteBuf response = alloc.buffer(15);
    response.writeByte(INTEGER_ID);
    response.writeBytes(intToBytes(integer));
    response.writeBytes(CRLFar);
    return response;
  }

  public static final ByteBuf getIntegerResponse(ByteBufAllocator alloc, long l) {
    ByteBuf response = alloc.buffer(25);
    response.writeByte(INTEGER_ID);
    response.writeBytes(longToBytes(l));
    response.writeBytes(CRLFar);
    return response;
  }

  public static final ByteBuf getNilResponse(ByteBufAllocator alloc) {
    ByteBuf buf = alloc.buffer().writeBytes(bNIL);
    return buf;
  }

  public static ByteBuf getBulkStringArrayResponseOfValues(ByteBufAllocator alloc,
      Collection items) {
    Iterator it = items.iterator();
    ByteBuf response = alloc.buffer();
    response.writeByte(Coder.ARRAY_ID);
    ByteBuf tmp = alloc.buffer();
    int size = 0;
    while (it.hasNext()) {
      Object next = it.next();
      ByteArrayWrapper nextWrapper = null;
      if (next instanceof Entry) {
        try {
          nextWrapper = (ByteArrayWrapper) ((Entry) next).getValue();
        } catch (EntryDestroyedException e) {
          continue;
        }
      } else if (next instanceof Struct) {
        nextWrapper = (ByteArrayWrapper) ((Struct) next).getFieldValues()[1];
      }
      if (nextWrapper != null) {
        tmp.writeByte(Coder.BULK_STRING_ID);
        tmp.writeBytes(intToBytes(nextWrapper.length()));
        tmp.writeBytes(Coder.CRLFar);
        tmp.writeBytes(nextWrapper.toBytes());
        tmp.writeBytes(Coder.CRLFar);
      } else {
        tmp.writeBytes(Coder.bNIL);
      }
      size++;
    }

    response.writeBytes(intToBytes(size));
    response.writeBytes(Coder.CRLFar);
    response.writeBytes(tmp);

    tmp.release();

    return response;
  }

  public static ByteBuf zRangeResponse(ByteBufAllocator alloc, Collection list,
      boolean withScores) {
    if (list.isEmpty())
      return Coder.getEmptyArrayResponse(alloc);

    ByteBuf buffer = alloc.buffer();
    buffer.writeByte(Coder.ARRAY_ID);
    ByteBuf tmp = alloc.buffer();
    int size = 0;

    for (Object entry : list) {
      ByteArrayWrapper key;
      DoubleWrapper score;
      if (entry instanceof Entry) {
        try {
          key = (ByteArrayWrapper) ((Entry) entry).getKey();
          score = (DoubleWrapper) ((Entry) entry).getValue();
        } catch (EntryDestroyedException e) {
          continue;
        }
      } else {
        Object[] fieldVals = ((Struct) entry).getFieldValues();
        key = (ByteArrayWrapper) fieldVals[0];
        score = (DoubleWrapper) fieldVals[1];
      }
      byte[] byteAr = key.toBytes();
      tmp.writeByte(Coder.BULK_STRING_ID);
      tmp.writeBytes(intToBytes(byteAr.length));
      tmp.writeBytes(Coder.CRLFar);
      tmp.writeBytes(byteAr);
      tmp.writeBytes(Coder.CRLFar);
      size++;
      if (withScores) {
        String scoreString = score.toString();
        byte[] scoreAr = stringToBytes(scoreString);
        tmp.writeByte(Coder.BULK_STRING_ID);
        tmp.writeBytes(intToBytes(scoreString.length()));
        tmp.writeBytes(Coder.CRLFar);
        tmp.writeBytes(scoreAr);
        tmp.writeBytes(Coder.CRLFar);
        size++;
      }
    }

    buffer.writeBytes(intToBytes(size));
    buffer.writeBytes(Coder.CRLFar);
    buffer.writeBytes(tmp);

    tmp.release();

    return buffer;
  }

  public static ByteBuf getArrayOfNils(ByteBufAllocator alloc, int length) {
    ByteBuf response = alloc.buffer();
    response.writeByte(Coder.ARRAY_ID);
    response.writeBytes(intToBytes(length));
    response.writeBytes(Coder.CRLFar);

    for (int i = 0; i < length; i++)
      response.writeBytes(bNIL);

    return response;
  }

  public static String bytesToString(byte[] bytes) {
    if (bytes == null)
      return null;
    try {
      return new String(bytes, CHARSET).intern();
    } catch (UnsupportedEncodingException e) {
      throw new RuntimeException(e);
    }
  }

  public static String doubleToString(double d) {
    if (d == Double.POSITIVE_INFINITY)
      return "Infinity";
    else if (d == Double.NEGATIVE_INFINITY)
      return "-Infinity";
    return String.valueOf(d);
  }

  public static byte[] stringToBytes(String string) {
    if (string == null || string.equals(""))
      return null;
    try {
      return string.getBytes(CHARSET);
    } catch (UnsupportedEncodingException e) {
      throw new RuntimeException(e);
    }
  }

  public static ByteArrayWrapper stringToByteArrayWrapper(String s) {
    return new ByteArrayWrapper(stringToBytes(s));
  }

  /*
   * These toByte methods convert to byte arrays of the string representation of the input, not
   * literal to byte
   */

  public static byte[] intToBytes(int i) {
    return stringToBytes(String.valueOf(i));
  }

  public static byte[] longToBytes(long l) {
    return stringToBytes(String.valueOf(l));
  }

  public static byte[] doubleToBytes(double d) {
    return stringToBytes(doubleToString(d));
  }

  public static int bytesToInt(byte[] bytes) {
    return Integer.parseInt(bytesToString(bytes));
  }

  public static long bytesToLong(byte[] bytes) {
    return Long.parseLong(bytesToString(bytes));
  }

  /**
   * A conversion where the byte array actually represents a string, so it is converted as a string
   * not as a literal double
   * 
   * @param bytes Array holding double
   * @return Parsed value
   * @throws NumberFormatException if bytes to string does not yield a convertible double
   */
  public static Double bytesToDouble(byte[] bytes) {
    return stringToDouble(bytesToString(bytes));
  }

  /**
   * Redis specific manner to parse floats
   * 
   * @param d String holding double
   * @return Value of string
   * @throws NumberFormatException if the double cannot be parsed
   */
  public static double stringToDouble(String d) {
    if (d.equalsIgnoreCase(P_INF))
      return Double.POSITIVE_INFINITY;
    else if (d.equalsIgnoreCase(N_INF))
      return Double.NEGATIVE_INFINITY;
    else
      return Double.parseDouble(d);
  }

  public static ByteArrayWrapper stringToByteWrapper(String s) {
    return new ByteArrayWrapper(stringToBytes(s));
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy