// Path: hibernate4-memcached-core/src/main/java/kr/pe/kwonnam/hibernate4memcached/util/IntToBytesUtils.java
// public class IntToBytesUtils {
// 
//     /**
//      * int value to 4 bytes array.
//      */
//     public static byte[] intToBytes(int value) {
//         return new byte[]{(byte) (value >>> 24), (byte) (value >>> 16), (byte) (value >>> 8), (byte) (value)
// 
//         };
//     }
// 
//     /**
//      * 4 bytes array to int
//      */
//     public static int bytesToInt(byte[] bytes) {
//         int value = ((int) bytes[0] & 0xFF) << 24;
//         value += ((int) bytes[1] & 0xFF) << 16;
//         value += ((int) bytes[2] & 0xFF) << 8;
//         value += (int) bytes[3] & 0xFF;
//         return value;
//     }
// 
// }
// 
// Path: hibernate4-memcached-core/src/main/java/kr/pe/kwonnam/hibernate4memcached/util/Lz4CompressUtils.java
// public class Lz4CompressUtils {
//     private static final LZ4Factory factory = LZ4Factory.fastestInstance();
// 
//     public static byte[] compress(final byte[] src) {
//         if (src == null) {
//             throw new IllegalArgumentException("src must not be null.");
//         }
// 
//         LZ4Compressor compressor = factory.fastCompressor();
// 
//         return compressor.compress(src);
//     }
// 
//     /**
//      * When the exact decompressed size is unknown.
//      * Decompress data size cannot be larger then maxDecompressedSize
//      */
//     public static byte[] decompressSafe(final byte[] src, int maxDecompressedSize) {
//         if (src == null) {
//             throw new IllegalArgumentException("src must not be null.");
//         }
// 
//         if (maxDecompressedSize <= 0) {
//             throw new IllegalArgumentException("maxDecompressedSize must be larger than 0 but " + maxDecompressedSize);
//         }
// 
//         LZ4SafeDecompressor decompressor = factory.safeDecompressor();
// 
//         return decompressor.decompress(src, maxDecompressedSize);
//     }
// 
//     /**
//      * When the exact decompressed size is known, use this method to decompress. It's faster.
//      *
//      * @see net.jpountz.lz4.LZ4FastDecompressor
//      */
//     public static byte[] decompressFast(byte[] src, int srcOffset, int exactDecompressedSize) {
//         if (src == null) {
//             throw new IllegalArgumentException("src must not be null.");
//         }
// 
//         if (srcOffset < 0) {
//             throw new IllegalArgumentException("srcOffset must equal to or larger than 0 but " + srcOffset);
//         }
// 
//         if (exactDecompressedSize < 0) {
//             throw new IllegalArgumentException("exactDecompressedSize must equal to or larger than 0 but " + exactDecompressedSize);
//         }
// 
//         LZ4FastDecompressor decompressor = factory.fastDecompressor();
// 
//         return decompressor.decompress(src, srcOffset, exactDecompressedSize);
//     }
// 
//     /**
//      * @see #decompressFast(byte[], int, int)
//      */
//     public static byte[] decompressFast(final byte[] src, int exactDecompressedSize) {
//         return decompressFast(src, 0, exactDecompressedSize);
//     }
// }
// 
// Path: hibernate4-memcached-core/src/main/java/kr/pe/kwonnam/hibernate4memcached/util/OverridableReadOnlyProperties.java
// public interface OverridableReadOnlyProperties {
//     String getProperty(String key);
// 
//     String getProperty(String key, String defaultValue);
// 
//     String getRequiredProperty(String key);
// }


import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import com.esotericsoftware.kryo.serializers.CompatibleFieldSerializer;
import de.javakaffee.kryoserializers.KryoReflectionFactorySupport;
import kr.pe.kwonnam.hibernate4memcached.util.IntToBytesUtils;
import kr.pe.kwonnam.hibernate4memcached.util.Lz4CompressUtils;
import kr.pe.kwonnam.hibernate4memcached.util.OverridableReadOnlyProperties;
import net.spy.memcached.CachedData;
import org.apache.commons.lang3.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

    @Override
    public CachedData encode(Object object) {
        int flags = BASE_FLAG;

        byte[] encodedBytes = kryoEncode(object);

        boolean compressionRequired = encodedBytes.length > compressionThreasholdBytes;

        if (compressionRequired) {
            int beforeSize = encodedBytes.length;
            encodedBytes = compress(encodedBytes);

            log.debug("kryotranscoder compress required : {}, original {} bytes -> compressed {} bytes", compressionRequired, beforeSize, encodedBytes.length);
            flags = flags | COMPRESS_FLAG;
        }
        return new CachedData(flags, encodedBytes, getMaxSize());
    }

    private byte[] kryoEncode(Object o) {
        Kryo kryo = createKryo();

        Output output = new Output(bufferSize, getMaxSize());
        kryo.writeClassAndObject(output, o);
        return output.toBytes();
    }

    byte[] compress(byte[] encodedBytes) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream(encodedBytes.length);
        try {
// Path: hibernate4-memcached-core/src/main/java/kr/pe/kwonnam/hibernate4memcached/util/IntToBytesUtils.java
// public class IntToBytesUtils {
// 
//     /**
//      * int value to 4 bytes array.
//      */
//     public static byte[] intToBytes(int value) {
//         return new byte[]{(byte) (value >>> 24), (byte) (value >>> 16), (byte) (value >>> 8), (byte) (value)
// 
//         };
//     }
// 
//     /**
//      * 4 bytes array to int
//      */
//     public static int bytesToInt(byte[] bytes) {
//         int value = ((int) bytes[0] & 0xFF) << 24;
//         value += ((int) bytes[1] & 0xFF) << 16;
//         value += ((int) bytes[2] & 0xFF) << 8;
//         value += (int) bytes[3] & 0xFF;
//         return value;
//     }
// 
// }
// 
// Path: hibernate4-memcached-core/src/main/java/kr/pe/kwonnam/hibernate4memcached/util/Lz4CompressUtils.java
// public class Lz4CompressUtils {
//     private static final LZ4Factory factory = LZ4Factory.fastestInstance();
// 
//     public static byte[] compress(final byte[] src) {
//         if (src == null) {
//             throw new IllegalArgumentException("src must not be null.");
//         }
// 
//         LZ4Compressor compressor = factory.fastCompressor();
// 
//         return compressor.compress(src);
//     }
// 
//     /**
//      * When the exact decompressed size is unknown.
//      * Decompress data size cannot be larger then maxDecompressedSize
//      */
//     public static byte[] decompressSafe(final byte[] src, int maxDecompressedSize) {
//         if (src == null) {
//             throw new IllegalArgumentException("src must not be null.");
//         }
// 
//         if (maxDecompressedSize <= 0) {
//             throw new IllegalArgumentException("maxDecompressedSize must be larger than 0 but " + maxDecompressedSize);
//         }
// 
//         LZ4SafeDecompressor decompressor = factory.safeDecompressor();
// 
//         return decompressor.decompress(src, maxDecompressedSize);
//     }
// 
//     /**
//      * When the exact decompressed size is known, use this method to decompress. It's faster.
//      *
//      * @see net.jpountz.lz4.LZ4FastDecompressor
//      */
//     public static byte[] decompressFast(byte[] src, int srcOffset, int exactDecompressedSize) {
//         if (src == null) {
//             throw new IllegalArgumentException("src must not be null.");
//         }
// 
//         if (srcOffset < 0) {
//             throw new IllegalArgumentException("srcOffset must equal to or larger than 0 but " + srcOffset);
//         }
// 
//         if (exactDecompressedSize < 0) {
//             throw new IllegalArgumentException("exactDecompressedSize must equal to or larger than 0 but " + exactDecompressedSize);
//         }
// 
//         LZ4FastDecompressor decompressor = factory.fastDecompressor();
// 
//         return decompressor.decompress(src, srcOffset, exactDecompressedSize);
//     }
// 
//     /**
//      * @see #decompressFast(byte[], int, int)
//      */
//     public static byte[] decompressFast(final byte[] src, int exactDecompressedSize) {
//         return decompressFast(src, 0, exactDecompressedSize);
//     }
// }
// 
// Path: hibernate4-memcached-core/src/main/java/kr/pe/kwonnam/hibernate4memcached/util/OverridableReadOnlyProperties.java
// public interface OverridableReadOnlyProperties {
//     String getProperty(String key);
// 
//     String getProperty(String key, String defaultValue);
// 
//     String getRequiredProperty(String key);
// }


// Path: hibernate4-memcached-spymemcached-adapter/src/main/java/kr/pe/kwonnam/hibernate4memcached/spymemcached/KryoTranscoder.java
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import com.esotericsoftware.kryo.serializers.CompatibleFieldSerializer;
import de.javakaffee.kryoserializers.KryoReflectionFactorySupport;
import kr.pe.kwonnam.hibernate4memcached.util.IntToBytesUtils;
import kr.pe.kwonnam.hibernate4memcached.util.Lz4CompressUtils;
import kr.pe.kwonnam.hibernate4memcached.util.OverridableReadOnlyProperties;
import net.spy.memcached.CachedData;
import org.apache.commons.lang3.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.ByteArrayOutputStream;
import java.io.IOException;


    @Override
    public CachedData encode(Object object) {
        int flags = BASE_FLAG;

        byte[] encodedBytes = kryoEncode(object);

        boolean compressionRequired = encodedBytes.length > compressionThreasholdBytes;

        if (compressionRequired) {
            int beforeSize = encodedBytes.length;
            encodedBytes = compress(encodedBytes);

            log.debug("kryotranscoder compress required : {}, original {} bytes -> compressed {} bytes", compressionRequired, beforeSize, encodedBytes.length);
            flags = flags | COMPRESS_FLAG;
        }
        return new CachedData(flags, encodedBytes, getMaxSize());
    }

    private byte[] kryoEncode(Object o) {
        Kryo kryo = createKryo();

        Output output = new Output(bufferSize, getMaxSize());
        kryo.writeClassAndObject(output, o);
        return output.toBytes();
    }

    byte[] compress(byte[] encodedBytes) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream(encodedBytes.length);
        try {
            baos.write(IntToBytesUtils.intToBytes(encodedBytes.length));