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

com.schooner.MemCached.NativeHandler Maven / Gradle / Ivy

There is a newer version: 3.0.2
Show newest version
/**
 * MemCached Java client
 * Copyright (c) 2007 Greg Whalin
 * All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the BSD license
 *
 * This library is distributed in the hope that it will be
 * useful, but WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 * PURPOSE.
 *
 * You should have received a copy of the BSD License along with this
 * library.
 *
 * @author Greg Whalin  
 * @version 2.0
 */
package com.schooner.MemCached;

import java.io.UnsupportedEncodingException;
import java.util.Date;

import com.whalin.MemCached.MemCachedClient;

/**
 * Handle encoding standard Java types directly which can result in significant
 * memory savings:
 * 
 * Currently the Memcached driver for Java supports the setSerialize() option.
 * This can increase performance in some situations but has a few issues:
 * 
 * Code that performs class casting will throw ClassCastExceptions when
 * setSerialize is enabled. For example:
 * 
 * mc.set( "foo", new Integer( 1 ) ); Integer output = (Integer)mc.get("foo");
 * 
 * Will work just file when setSerialize is true but when its false will just
 * throw a ClassCastException.
 * 
 * Also internally it doesn't support Boolean and since toString is called
 * wastes a lot of memory and causes additional performance issue. For example
 * an Integer can take anywhere from 1 byte to 10 bytes.
 * 
 * Due to the way the memcached slab allocator works it seems like a LOT of
 * wasted memory to store primitive types as serialized objects (from a
 * performance and memory perspective). In our applications we have millions of
 * small objects and wasted memory would become a big problem.
 * 
 * For example a Serialized Boolean takes 47 bytes which means it will fit into
 * the 64byte LRU. Using 1 byte means it will fit into the 8 byte LRU thus
 * saving 8x the memory. This also saves the CPU performance since we don't have
 * to serialize bytes back and forth and we can compute the byte[] value
 * directly.
 * 
 * One problem would be when the user calls get() because doing so would require
 * the app to know the type of the object stored as a bytearray inside memcached
 * (since the user will probably cast).
 * 
 * If we assume the basic types are interned we could use the first byte as the
 * type with the remaining bytes as the value. Then on get() we could read the
 * first byte to determine the type and then construct the correct object for
 * it. This would prevent the ClassCastException I talked about above.
 * 
 * We could remove the setSerialize() option and just assume that standard VM
 * types are always internd in this manner.
 * 
 * mc.set( "foo", new Boolean.TRUE ); Boolean b = (Boolean)mc.get( "foo" );
 * 
 * And the type casts would work because internally we would create a new
 * Boolean to return back to the client.
 * 
 * This would reduce memory footprint and allow for a virtual implementation of
 * the Externalizable interface which is much faster than Serialzation.
 * 
 * Currently the memory improvements would be:
 * 
 * java.lang.Boolean - 8x performance improvement (now just two bytes)
 * java.lang.Integer - 16x performance improvement (now just 5 bytes)
 * 
 * Most of the other primitive types would benefit from this optimization.
 * java.lang.Character being another obvious example.
 * 
 * I know it seems like I'm being really picky here but for our application I'd
 * save 1G of memory right off the bat. We'd go down from 1.152G of memory used
 * down to 144M of memory used which is much better IMO.
 * 
 * http://java.sun.com/docs/books/tutorial/native1.1/integrating/types.html
 * 
 * @author Kevin A. Burton
 * @author Greg Whalin 
 */
public class NativeHandler {

	/**
	 * Detemine of object can be natively serialized by this class.
	 * 
	 * @param value
	 *            Object to test.
	 * @return true/false
	 */
	public static final boolean isHandled(Object value) {

		return (value instanceof Byte || value instanceof Boolean || value instanceof Integer || value instanceof Long
				|| value instanceof Character || value instanceof String || value instanceof StringBuffer
				|| value instanceof Float || value instanceof Short || value instanceof Double || value instanceof Date
				|| value instanceof StringBuilder || value instanceof byte[]) ? true : false;
	}

	public static final boolean isHandled(int flag) {
		if ((flag & MemCachedClient.MARKER_BYTE) == MemCachedClient.MARKER_BYTE
				|| (flag & MemCachedClient.MARKER_BOOLEAN) == MemCachedClient.MARKER_BOOLEAN
				|| (flag & MemCachedClient.MARKER_INTEGER) == MemCachedClient.MARKER_INTEGER
				|| (flag & MemCachedClient.MARKER_LONG) == MemCachedClient.MARKER_LONG
				|| (flag & MemCachedClient.MARKER_CHARACTER) == MemCachedClient.MARKER_CHARACTER
				|| (flag & MemCachedClient.MARKER_STRING) == MemCachedClient.MARKER_STRING
				|| (flag & MemCachedClient.MARKER_STRINGBUFFER) == MemCachedClient.MARKER_STRINGBUFFER
				|| (flag & MemCachedClient.MARKER_FLOAT) == MemCachedClient.MARKER_FLOAT
				|| (flag & MemCachedClient.MARKER_SHORT) == MemCachedClient.MARKER_SHORT
				|| (flag & MemCachedClient.MARKER_DOUBLE) == MemCachedClient.MARKER_DOUBLE
				|| (flag & MemCachedClient.MARKER_DATE) == MemCachedClient.MARKER_DATE
				|| (flag & MemCachedClient.MARKER_STRINGBUILDER) == MemCachedClient.MARKER_STRINGBUILDER
				|| (flag & MemCachedClient.MARKER_BYTEARR) == MemCachedClient.MARKER_BYTEARR)
			return true;
		return false;
	}

	/**
	 * Returns the flag for marking the type of the byte array.
	 * 
	 * @param value
	 *            Object we are storing.
	 * @return int marker
	 */
	public static final int getMarkerFlag(Object value) {

		if (value instanceof Byte)
			return MemCachedClient.MARKER_BYTE;

		if (value instanceof Boolean)
			return MemCachedClient.MARKER_BOOLEAN;

		if (value instanceof Integer)
			return MemCachedClient.MARKER_INTEGER;

		if (value instanceof Long)
			return MemCachedClient.MARKER_LONG;

		if (value instanceof Character)
			return MemCachedClient.MARKER_CHARACTER;

		if (value instanceof String)
			return MemCachedClient.MARKER_STRING;

		if (value instanceof StringBuffer)
			return MemCachedClient.MARKER_STRINGBUFFER;

		if (value instanceof Float)
			return MemCachedClient.MARKER_FLOAT;

		if (value instanceof Short)
			return MemCachedClient.MARKER_SHORT;

		if (value instanceof Double)
			return MemCachedClient.MARKER_DOUBLE;

		if (value instanceof Date)
			return MemCachedClient.MARKER_DATE;

		if (value instanceof StringBuilder)
			return MemCachedClient.MARKER_STRINGBUILDER;

		if (value instanceof byte[])
			return MemCachedClient.MARKER_BYTEARR;

		return MemCachedClient.MARKER_OTHERS;
	}

	/**
	 * Encodes supported types
	 * 
	 * @param value
	 *            Object to encode.
	 * @return byte array
	 * 
	 * @throws UnsupportedEncodingException
	 *             If fail to encode.
	 */
	public static byte[] encode(Object value) throws UnsupportedEncodingException {

		if (value instanceof Byte)
			return encode((Byte) value);

		if (value instanceof Boolean)
			return encode((Boolean) value);

		if (value instanceof Integer)
			return encode(((Integer) value).intValue());

		if (value instanceof Long)
			return encode(((Long) value).longValue());

		if (value instanceof Character)
			return encode((Character) value);

		if (value instanceof String)
			return encode((String) value);

		if (value instanceof StringBuffer)
			return encode((StringBuffer) value);

		if (value instanceof Float)
			return encode(((Float) value).floatValue());

		if (value instanceof Short)
			return encode((Short) value);

		if (value instanceof Double)
			return encode(((Double) value).doubleValue());

		if (value instanceof Date)
			return encode((Date) value);

		if (value instanceof StringBuilder)
			return encode((StringBuilder) value);

		if (value instanceof byte[])
			return encode((byte[]) value);

		return null;
	}

	protected static byte[] encode(Byte value) {
		byte[] b = new byte[1];
		b[0] = value.byteValue();
		return b;
	}

	protected static byte[] encode(Boolean value) {
		byte[] b = new byte[1];

		if (value.booleanValue())
			b[0] = 1;
		else
			b[0] = 0;

		return b;
	}

	protected static byte[] encode(int value) {
		return getBytes(value);
	}

	protected static byte[] encode(long value) throws UnsupportedEncodingException {
		return getBytes(value);
	}

	protected static byte[] encode(Date value) {
		return getBytes(value.getTime());
	}

	protected static byte[] encode(Character value) {
		return encode(value.charValue());
	}

	protected static byte[] encode(String value) throws UnsupportedEncodingException {
		return value.getBytes("UTF-8");
	}

	protected static byte[] encode(StringBuffer value) throws UnsupportedEncodingException {
		return encode(value.toString());
	}

	protected static byte[] encode(float value) throws UnsupportedEncodingException {
		return encode((int) Float.floatToIntBits(value));
	}

	protected static byte[] encode(Short value) throws UnsupportedEncodingException {
		return encode((int) value.shortValue());
	}

	protected static byte[] encode(double value) throws UnsupportedEncodingException {
		return encode((long) Double.doubleToLongBits(value));
	}

	protected static byte[] encode(StringBuilder value) throws UnsupportedEncodingException {
		return encode(value.toString());
	}

	protected static byte[] encode(byte[] value) {
		return value;
	}

	protected static byte[] getBytes(long value) {
		byte[] b = new byte[8];
		b[0] = (byte) ((value >> 56) & 0xFF);
		b[1] = (byte) ((value >> 48) & 0xFF);
		b[2] = (byte) ((value >> 40) & 0xFF);
		b[3] = (byte) ((value >> 32) & 0xFF);
		b[4] = (byte) ((value >> 24) & 0xFF);
		b[5] = (byte) ((value >> 16) & 0xFF);
		b[6] = (byte) ((value >> 8) & 0xFF);
		b[7] = (byte) ((value >> 0) & 0xFF);
		return b;
	}

	protected static byte[] getBytes(int value) {
		byte[] b = new byte[4];
		b[0] = (byte) ((value >> 24) & 0xFF);
		b[1] = (byte) ((value >> 16) & 0xFF);
		b[2] = (byte) ((value >> 8) & 0xFF);
		b[3] = (byte) ((value >> 0) & 0xFF);
		return b;
	}

	/**
	 * Decodes byte array using memcache flag to determine type.
	 * 
	 * @param b
	 * @param marker
	 * @return
	 * @throws UnsupportedEncodingException
	 */
	public static Object decode(byte[] b, int flag) throws UnsupportedEncodingException {

		if (b.length < 1)
			return null;

		if ((flag & MemCachedClient.MARKER_BYTE) == MemCachedClient.MARKER_BYTE)
			return decodeByte(b);

		if ((flag & MemCachedClient.MARKER_BOOLEAN) == MemCachedClient.MARKER_BOOLEAN)
			return decodeBoolean(b);

		if ((flag & MemCachedClient.MARKER_INTEGER) == MemCachedClient.MARKER_INTEGER)
			return decodeInteger(b);

		if ((flag & MemCachedClient.MARKER_LONG) == MemCachedClient.MARKER_LONG)
			return decodeLong(b);

		if ((flag & MemCachedClient.MARKER_CHARACTER) == MemCachedClient.MARKER_CHARACTER)
			return decodeCharacter(b);

		if ((flag & MemCachedClient.MARKER_STRING) == MemCachedClient.MARKER_STRING)
			return decodeString(b);

		if ((flag & MemCachedClient.MARKER_STRINGBUFFER) == MemCachedClient.MARKER_STRINGBUFFER)
			return decodeStringBuffer(b);

		if ((flag & MemCachedClient.MARKER_FLOAT) == MemCachedClient.MARKER_FLOAT)
			return decodeFloat(b);

		if ((flag & MemCachedClient.MARKER_SHORT) == MemCachedClient.MARKER_SHORT)
			return decodeShort(b);

		if ((flag & MemCachedClient.MARKER_DOUBLE) == MemCachedClient.MARKER_DOUBLE)
			return decodeDouble(b);

		if ((flag & MemCachedClient.MARKER_DATE) == MemCachedClient.MARKER_DATE)
			return decodeDate(b);

		if ((flag & MemCachedClient.MARKER_STRINGBUILDER) == MemCachedClient.MARKER_STRINGBUILDER)
			return decodeStringBuilder(b);

		if ((flag & MemCachedClient.MARKER_BYTEARR) == MemCachedClient.MARKER_BYTEARR)
			return decodeByteArr(b);

		return null;
	}

	// decode methods
	protected static Byte decodeByte(byte[] b) {
		return new Byte(b[0]);
	}

	protected static Boolean decodeBoolean(byte[] b) {
		boolean value = b[0] == 1;
		return (value) ? Boolean.TRUE : Boolean.FALSE;
	}

	protected static Integer decodeInteger(byte[] b) {
		return new Integer(toInt(b));
	}

	protected static Long decodeLong(byte[] b) throws UnsupportedEncodingException {
		return new Long(toLong(b));
	}

	protected static Character decodeCharacter(byte[] b) {
		return new Character((char) decodeInteger(b).intValue());
	}

	protected static String decodeString(byte[] b) throws UnsupportedEncodingException {
		return new String(b, "UTF-8");
	}

	protected static StringBuffer decodeStringBuffer(byte[] b) throws UnsupportedEncodingException {
		return new StringBuffer(decodeString(b));
	}

	protected static Float decodeFloat(byte[] b) throws UnsupportedEncodingException {
		Integer l = decodeInteger(b);
		return new Float(Float.intBitsToFloat(l.intValue()));
	}

	protected static Short decodeShort(byte[] b) throws UnsupportedEncodingException {
		return new Short((short) decodeInteger(b).intValue());
	}

	protected static Double decodeDouble(byte[] b) throws UnsupportedEncodingException {
		Long l = decodeLong(b);
		return new Double(Double.longBitsToDouble(l.longValue()));
	}

	protected static Date decodeDate(byte[] b) {
		return new Date(toLong(b));
	}

	protected static StringBuilder decodeStringBuilder(byte[] b) throws UnsupportedEncodingException {
		return new StringBuilder(decodeString(b));
	}

	protected static byte[] decodeByteArr(byte[] b) {
		return b;
	}

	/**
	 * This works by taking each of the bit patterns and converting them to ints
	 * taking into account 2s complement and then adding them..
	 * 
	 * @param b
	 * @return
	 */
	protected static int toInt(byte[] b) {
		return (((((int) b[3]) & 0xFF) << 32) + ((((int) b[2]) & 0xFF) << 40) + ((((int) b[1]) & 0xFF) << 48) + ((((int) b[0]) & 0xFF) << 56));
	}

	protected static long toLong(byte[] b) {
		return ((((long) b[7]) & 0xFF) + ((((long) b[6]) & 0xFF) << 8) + ((((long) b[5]) & 0xFF) << 16)
				+ ((((long) b[4]) & 0xFF) << 24) + ((((long) b[3]) & 0xFF) << 32) + ((((long) b[2]) & 0xFF) << 40)
				+ ((((long) b[1]) & 0xFF) << 48) + ((((long) b[0]) & 0xFF) << 56));
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy