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

net.freeutils.util.Base32 Maven / Gradle / Ivy

The newest version!
/*
 *  Copyright © 2003-2024 Amichai Rothman
 *
 *  This file is part of JElementary - the Java Elementary Utilities package.
 *
 *  JElementary is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  JElementary 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.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with JElementary.  If not, see .
 *
 *  For additional info see https://www.freeutils.net/source/jelementary/
 */

package net.freeutils.util;

import java.io.UnsupportedEncodingException;

/**
 * The {@code Base32} class contains RFC 4648 compliant base32 and base32hex
 * encoding and decoding methods.
 */
public class Base32 {

    protected static final byte[] ENCODE, DECODE, ENCODE_HEX, DECODE_HEX;
    static {
        byte[][] tables = Base64.createLookupTables("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567");
        byte[][] tablesHex = Base64.createLookupTables("0123456789ABCDEFGHIJKLMNOPQRSTUV");
        ENCODE = tables[0];
        DECODE = tables[1];
        ENCODE_HEX = tablesHex[0];
        DECODE_HEX = tablesHex[1];
    }

    /**
     * Encodes the given bytes using the given encoding lookup table.
     *
     * @param b the bytes to encode
     * @param lookup the encoding lookup table
     * @return the encoded bytes
     */
    public static byte[] encode(byte[] b, byte[] lookup) {
        int full = b.length / 5 * 5;
        int rem = b.length - full;
        byte[] out = new byte[(b.length + 4) / 5 * 8];
        int i = 0;
        int j = 0;
        // encode full byte quantums
        while (i < full) {
            long t = (long)(b[i++] & 0xFF) << 32 | (long)(b[i++] & 0xFF) << 24
                | (b[i++] & 0xFF) << 16 | (b[i++] & 0xFF) << 8 | (b[i++] & 0xFF);
            out[j++] = lookup[(int)(t >> 35)];
            out[j++] = lookup[(int)(t >> 30) & 0x01F];
            out[j++] = lookup[(int)(t >> 25) & 0x01F];
            out[j++] = lookup[(int)(t >> 20) & 0x01F];
            out[j++] = lookup[(int)(t >> 15) & 0x01F];
            out[j++] = lookup[(int)(t >> 10) & 0x01F];
            out[j++] = lookup[(int)(t >> 5) & 0x01F];
            out[j++] = lookup[(int)t & 0x01F];
        }
        // encode remainder (0, 1, 2, 3 or 4 last bytes)
        if (rem > 0) {
            long t = (long)(b[i++] & 0xFF) << 32
                | (rem <= 1 ? 0 : (long)(b[i++] & 0xFF) << 24)
                | (rem <= 2 ? 0 : (b[i++] & 0xFF) << 16)
                | (rem <= 3 ? 0 : (b[i] & 0xFF) << 8);
            out[j++] = lookup[(int)(t >> 35)];
            out[j++] = lookup[(int)(t >> 30) & 0x01F];
            out[j++] = rem <= 1 ? (byte)'=' : lookup[(int)(t >> 25) & 0x01F];
            out[j++] = rem <= 1 ? (byte)'=' : lookup[(int)(t >> 20) & 0x01F];
            out[j++] = rem <= 2 ? (byte)'=' : lookup[(int)(t >> 15) & 0x01F];
            out[j++] = rem <= 3 ? (byte)'=' : lookup[(int)(t >> 10) & 0x01F];
            out[j++] = rem <= 3 ? (byte)'=' : lookup[(int)(t >> 5) & 0x01F];
            out[j] = '=';
        }
        return out;
    }

    /**
     * Decodes the given bytes using the given decoding lookup table.
     *
     * @param b the bytes to decode
     * @param lookup the decoding lookup table
     * @return the decoded bytes
     */
    public static byte[] decode(byte[] b, byte[] lookup) {
        int pad = 0;
        for (int i = b.length - 1; i >= 0 && b[i] == '='; i--)
            pad++;
        int full = (b.length - pad) / 8 * 8;
        int rem = pad == 0 ? 0 : (9 - pad) / 2 - 5;
        byte[] out = new byte[b.length / 8 * 5 + rem];
        int i = 0;
        int j = 0;
        // decode full byte quantums
        while (i < full) {
            long t = (long)lookup[b[i++]] << 35 | (long)lookup[b[i++]] << 30
                | lookup[b[i++]] << 25 | lookup[b[i++]] << 20 | lookup[b[i++]] << 15
                | lookup[b[i++]] << 10 | lookup[b[i++]] << 5 | lookup[b[i++]];
            out[j++] = (byte)(t >> 32);
            out[j++] = (byte)(t >> 24);
            out[j++] = (byte)(t >> 16);
            out[j++] = (byte)(t >> 8);
            out[j++] = (byte)t;
        }
        // decode remainder (0, 1, 2, 3 or 4 last bytes)
        if (pad > 0) {
            long t = (long)lookup[b[i++]] << 35
                | (long)lookup[b[i++]] << 30
                | (pad <= 4 ? lookup[b[i++]] << 25 : 0)
                | (pad <= 4 ? lookup[b[i++]] << 20 : 0)
                | (pad <= 3 ? lookup[b[i++]] << 15 : 0)
                | (pad <= 1 ? lookup[b[i++]] << 10 : 0)
                | (pad <= 1 ? lookup[b[i]] << 5 : 0);
            out[j++] = (byte)(t >> 32);
            if (pad <= 4)
                out[j++] = (byte)(t >> 24);
            if (pad <= 3)
                out[j++] = (byte)(t >> 16);
            if (pad <= 1)
                out[j] = (byte)(t >> 8);
        }
        return out;
    }

    /**
     * Checks whether the given bytes are validly encoded.
     *
     * @param b the bytes to check
     * @param lookup the decoding lookup table
     * @return true if the bytes are validly encoded, false otherwise
     */
    public static boolean isValid(byte[] b, byte[] lookup) {
        int len = b.length;
        if (len % 8 != 0)
            return false;
        int pad = 0;
        for (int i = b.length - 1; i >= 0 && b[i] == '='; i--)
            pad++;
        if (pad != 0 && pad != 1 && pad != 3 && pad != 4 && pad != 6)
            return false;
        for (int i = 0; i < len; i++)
            if (lookup[b[i]] < 0 && (i < len - pad || b[i] != '='))
                return false;
        return true;
    }

    /**
     * Encodes the given bytes using standard base32 encoding.
     *
     * @param b the bytes to encode
     * @return the encoded bytes
     */
    public static byte[] encode(byte[] b) {
        return encode(b, ENCODE);
    }

    /**
     * Encodes the given bytes using base32hex encoding.
     *
     * @param b the bytes to encode
     * @return the encoded bytes
     */
    public static byte[] encodeHex(byte[] b) {
        return encode(b, ENCODE_HEX);
    }

    /**
     * Decodes the given bytes using standard base32 decoding.
     * The input it assumed to be valid, as this method does not
     * validate that it is indeed legal base32-encoded data.
     *
     * @param b the bytes to decode
     * @return the decoded bytes
     */
    public static byte[] decode(byte[] b) {
        return decode(b, DECODE);
    }

    /**
     * Decodes the given bytes using base32hex decoding.
     * The input it assumed to be valid, as this method does not
     * validate that it is indeed legal base32-encoded data.
     *
     * @param b the bytes to decode
     * @return the decoded bytes
     */
    public static byte[] decodeHex(byte[] b) {
        return decode(b, DECODE_HEX);
    }

    /**
     * Checks whether the given bytes are validly encoded in standard base32 encoding.
     *
     * @param b the bytes to check
     * @return true if the bytes are validly encoded, false otherwise
     */
    public static boolean isValid(byte[] b) {
        return isValid(b, DECODE);
    }

    /**
     * Checks whether the given bytes are validly encoded in base32hex encoding.
     *
     * @param b the bytes to check
     * @return true if the bytes are validly encoded, false otherwise
     */
    public static boolean isValidHex(byte[] b) {
        return isValid(b, DECODE_HEX);
    }

    /**
     * Decodes the given string using standard base32 decoding.
     *
     * @param s the string to decode
     * @param validate if true, the bytes are validated to be legal base32 bytes
     * @return the decoded bytes
     * @throws IllegalArgumentException if the given string contains non US-ASCII characters,
     *         or if validate is true and the given bytes are not valid base32 bytes
     */
    public static byte[] decode(String s, boolean validate) {
        return decode(Strings.getASCIIBytes(s), validate);
    }

    /**
     * Decodes the given bytes using standard base32 decoding.
     *
     * @param b the bytes to decode
     * @param validate if true, the bytes are validated to be legal base32 bytes
     * @return the decoded bytes
     * @throws IllegalArgumentException if validate is true and the given
     *         bytes are not valid base32 bytes
     */
    public static byte[] decode(byte[] b, boolean validate) {
        if (validate && !isValid(b))
            throw new IllegalArgumentException("invalid base32 bytes");
        return decode(b);
    }

    /**
     * Decodes the given string using base32hex decoding.
     *
     * @param s the string to decode
     * @param validate if true, the bytes are validated to be legal base32 bytes
     * @return the decoded bytes
     * @throws IllegalArgumentException if the given string contains non US-ASCII characters,
     *         or if validate is true and the given bytes are not valid base32 bytes
     */
    public static byte[] decodeHex(String s, boolean validate) {
        return decodeHex(Strings.getASCIIBytes(s), validate);
    }

    /**
     * Decodes the given bytes using base32hex decoding.
     *
     * @param b the bytes to decode
     * @param validate if true, the bytes are validated to be legal base32 bytes
     * @return the decoded bytes
     * @throws IllegalArgumentException if validate is true and the given
     *         bytes are not valid base32 bytes
     */
    public static byte[] decodeHex(byte[] b, boolean validate) {
        if (validate && !isValidHex(b))
            throw new IllegalArgumentException("invalid base32 bytes");
        return decodeHex(b);
    }

    /**
     * Encodes the given bytes using standard base32 encoding.
     *
     * @param b the bytes to encode
     * @return the encoded bytes as an ASCII hex string
     */
    public static String encodeString(byte[] b) {
        try {
            return new String(encode(b, ENCODE), "US-ASCII");
        } catch (UnsupportedEncodingException uee) {
            return null; // can't happen
        }
    }

    /**
     * Decodes the given string using standard base32 decoding.
     *
     * @param s the string to decode
     * @param validate if true, the bytes are validated to be legal base32 bytes
     * @param charset the charset to interpret the decoded bytes in
     * @return the decoded bytes as string in the given charset
     * @throws IllegalArgumentException if the given string contains non US-ASCII characters,
     *         or if validate is true and the given bytes are not valid base32 bytes,
     *         or if the given charset does not exist
     */
    public static String decodeString(String s, boolean validate, String charset) {
        try {
            return new String(decode(Strings.getASCIIBytes(s), validate), charset);
        } catch (UnsupportedEncodingException uee) {
            throw new IllegalArgumentException(uee);
        }
    }

    /**
     * Decodes the given string using standard base32 decoding.
     *
     * @param s the string to decode
     * @param validate if true, the bytes are validated to be legal base32 bytes
     * @return the decoded bytes as string in the ISO-8869-1 charset
     * @throws IllegalArgumentException if the given string contains non US-ASCII characters,
     *         or if validate is true and the given bytes are not valid base32 bytes
     */
    public static String decodeString(String s, boolean validate) {
        return decodeString(s, validate, "ISO8859_1");
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy