package org.maachang.util;

/**
 * Fnv-Hash.
 * 
 * @version 2007/10/18
 * @author masahito suzuki
 * @since MaachangUtil 1.00
 */
public class FnvHash {
    
    /**
     * fnv16コード.
     */
    private static final long FNV_16_CODE = 0x000000000000C34FL;

    /**
     * fnv32コード.
     */
    private static final long FNV_32_CODE = 0x00000000811C9DC5L;

    /**
     * fnv64コード.
     */
    private static final long FNV_64_CODE = 0xCBF29CE484222325L;

    /**
     * fnv16-Hash変換. <BR>
     * 
     * @param key
     *            対象のKeyを設定します.
     * @return int 変換されたHashが返されます.
     */
    public static int fnv16(String key) {
        if (key == null || key.length() <= 0) {
            throw new IllegalArgumentException("引数は不正です");
        }
        try {
            byte[] b = key.getBytes("UTF8");
            int len = b.length;
            long ret = FNV_16_CODE;
            for (int i = 0; i < len; i++) {
                ret += (ret << 1L) + (ret << 4L) + (ret << 7L) + (ret << 8L) ;
                ret ^= b[i];
            }
            return (int) (ret & 0x000000000000ffffL);
        } catch (Exception e) {
        }
        return 0;
    }

    /**
     * fnv16-Hash変換. <BR>
     * 
     * @param key
     *            対象のKeyを設定します.
     * @return int 変換されたHashが返されます.
     */
    public static int fnv16a(String key) {
        if (key == null || key.length() <= 0) {
            throw new IllegalArgumentException("引数は不正です");
        }
        try {
            byte[] b = key.getBytes("UTF8");
            int len = b.length;
            long ret = FNV_16_CODE;
            for (int i = 0; i < len; i++) {
                ret ^= b[i];
                ret += (ret << 1L) + (ret << 4L) + (ret << 7L) + (ret << 8L) ;
            }
            return (int) (ret & 0x000000000000ffffL);
        } catch (Exception e) {
        }
        return 0;
    }

    /**
     * fnv32-Hash変換. <BR>
     * 
     * @param key
     *            対象のKeyを設定します.
     * @return int 変換されたHashが返されます.
     */
    public static int fnv32(String key) {
        if (key == null || key.length() <= 0) {
            throw new IllegalArgumentException("引数は不正です");
        }
        try {
            byte[] b = key.getBytes("UTF8");
            int len = b.length;
            long ret = FNV_32_CODE;
            for (int i = 0; i < len; i++) {
                ret += (ret << 1L) + (ret << 4L) + (ret << 7L) + (ret << 8L)
                        + (ret << 24L);
                ret ^= b[i];
            }
            return (int) (ret & 0x00000000ffffffffL);
        } catch (Exception e) {
        }
        return 0;
    }

    /**
     * fnv32a-Hash変換. <BR>
     * 
     * @param key
     *            対象のKeyを設定します.
     * @return int 変換されたHashが返されます.
     */
    public static int fnv32a(String key) {
        if (key == null || key.length() <= 0) {
            throw new IllegalArgumentException("引数は不正です");
        }
        try {
            byte[] b = key.getBytes("UTF8");
            int len = b.length;
            long ret = FNV_32_CODE;
            for (int i = 0; i < len; i++) {
                ret ^= b[i];
                ret += (ret << 1L) + (ret << 4L) + (ret << 7L) + (ret << 8L)
                        + (ret << 24L);
            }
            return (int) (ret & 0x00000000ffffffffL);
        } catch (Exception e) {
        }
        return 0;
    }

    /**
     * fnv64-Hash変換. <BR>
     * 
     * @param key
     *            対象のKeyを設定します.
     * @return int 変換されたHashが返されます.
     */
    public static long fnv64(String key) {
        if (key == null || key.length() <= 0) {
            throw new IllegalArgumentException("引数は不正です");
        }
        try {
            byte[] b = key.getBytes("UTF8");
            int len = b.length;
            long ret = FNV_64_CODE;
            for (int i = 0; i < len; i++) {
                ret += (ret << 1L) + (ret << 4L) + (ret << 5L) + (ret << 7L)
                        + (ret << 8L) + (ret << 40L);
                ret ^= b[i];
            }
            return ret;
        } catch (Exception e) {
        }
        return 0L;
    }

    /**
     * fnv64a-Hash変換. <BR>
     * 
     * @param key
     *            対象のKeyを設定します.
     * @return int 変換されたHashが返されます.
     */
    public static long fnv64a(String key) {
        if (key == null || key.length() <= 0) {
            throw new IllegalArgumentException("引数は不正です");
        }
        try {
            byte[] b = key.getBytes("UTF8");
            int len = b.length;
            long ret = FNV_64_CODE;
            for (int i = 0; i < len; i++) {
                ret ^= b[i];
                ret += (ret << 1L) + (ret << 4L) + (ret << 5L) + (ret << 7L)
                        + (ret << 8L) + (ret << 40L);
            }
            return ret;
        } catch (Exception e) {
        }
        return 0L;
    }

}
