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

squidpony.StringKit Maven / Gradle / Ivy

Go to download

SquidLib platform-independent logic and utility code. Please refer to https://github.com/SquidPony/SquidLib .

There is a newer version: 3.0.6
Show newest version
package squidpony;

import regexodus.Matcher;
import regexodus.Pattern;
import squidpony.squidmath.CrossHash;
import squidpony.squidmath.NumberTools;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

/**
 * Various utility functions for dealing with Strings, CharSequences, and char[]s; mostly converting numbers.
 * Created by Tommy Ettinger on 3/21/2016.
 */
public class StringKit {

    /**
     * Searches text for the exact contents of the char array search; returns true if text contains search.
     * @param text a CharSequence, such as a String or StringBuilder, that might contain search
     * @param search a char array to try to find in text
     * @return true if search was found
     */
    public static boolean contains(CharSequence text, char[] search) {
        return !(text == null || text.length() == 0 || search == null || search.length <= 0)
                && containsPart(text, search, "", "") == search.length;
    }

    public static int containsPart(CharSequence text, char[] search)
    {
        return containsPart(text, search, "", "");
    }
    public static int containsPart(CharSequence text, char[] search, CharSequence prefix, CharSequence suffix)
    {
        if(prefix == null) prefix = "";
        if(suffix == null) suffix = "";
        int bl = prefix.length(), el = suffix.length();
        if(text == null || text.length() == 0 || search == null || (search.length + bl + el <= 0))
            return 0;
        int sl = bl + search.length + el, tl = text.length() - sl, f = 0, sl2 = sl - el;
        char s = (bl <= 0) ? (search.length <= 0 ? suffix.charAt(0) : search[0]) : prefix.charAt(0);
        PRIMARY:
        for (int i = 0; i <= tl; i++) {
            if(text.charAt(i) == s)
            {
                for (int j = i+1, x = 1; x < sl; j++, x++) {
                    if(x < bl)
                    {
                        if (text.charAt(j) != prefix.charAt(x)) {
                            f = Math.max(f, x);
                            continue PRIMARY;
                        }
                    }
                    else if(x < sl2)
                    {
                        if (text.charAt(j) != search[x-bl]) {
                            f = Math.max(f, x);
                            continue PRIMARY;
                        }
                    }
                    else
                    {
                        if (text.charAt(j) != suffix.charAt(x - sl2)) {
                            f = Math.max(f, x);
                            continue PRIMARY;
                        }
                    }
                }
                return sl;
            }
        }
        return f;
    }

    public static String join(CharSequence delimiter, CharSequence... elements) {
        if (elements == null || elements.length == 0) return "";
        StringBuilder sb = new StringBuilder(64);
        sb.append(elements[0]);
        for (int i = 1; i < elements.length; i++) {
            sb.append(delimiter).append(elements[i]);
        }
        return sb.toString();
    }

    public static String join(CharSequence delimiter, Collection elements) {
        if (elements == null || elements.isEmpty()) return "";
        StringBuilder sb = new StringBuilder(64);
        Iterator it = elements.iterator();
        sb.append(it.next());
        while(it.hasNext()) {
            sb.append(delimiter).append(it.next());
        }
        return sb.toString();
    }

    public static String joinArrays(CharSequence delimiter, char[]... elements) {
        if (elements == null || elements.length == 0) return "";
        StringBuilder sb = new StringBuilder(64);
        sb.append(elements[0]);
        for (int i = 1; i < elements.length; i++) {
            sb.append(delimiter).append(elements[i]);
        }
        return sb.toString();
    }

    public static String join(CharSequence delimiter, long... elements) {
        if (elements == null || elements.length == 0) return "";
        StringBuilder sb = new StringBuilder(64);
        sb.append(elements[0]);
        for (int i = 1; i < elements.length; i++) {
            sb.append(delimiter).append(elements[i]);
        }
        return sb.toString();
    }
    public static String join(CharSequence delimiter, double... elements) {
        if (elements == null || elements.length == 0) return "";
        StringBuilder sb = new StringBuilder(64);
        sb.append(elements[0]);
        for (int i = 1; i < elements.length; i++) {
            sb.append(delimiter).append(elements[i]);
        }
        return sb.toString();
    }
    public static String join(CharSequence delimiter, int... elements) {
        if (elements == null || elements.length == 0) return "";
        StringBuilder sb = new StringBuilder(64);
        sb.append(elements[0]);
        for (int i = 1; i < elements.length; i++) {
            sb.append(delimiter).append(elements[i]);
        }
        return sb.toString();
    }
    public static String join(CharSequence delimiter, float... elements) {
        if (elements == null || elements.length == 0) return "";
        StringBuilder sb = new StringBuilder(64);
        sb.append(elements[0]);
        for (int i = 1; i < elements.length; i++) {
            sb.append(delimiter).append(elements[i]);
        }
        return sb.toString();
    }
    public static String join(CharSequence delimiter, short... elements) {
        if (elements == null || elements.length == 0) return "";
        StringBuilder sb = new StringBuilder(64);
        sb.append(elements[0]);
        for (int i = 1; i < elements.length; i++) {
            sb.append(delimiter).append(elements[i]);
        }
        return sb.toString();
    }
    public static String join(CharSequence delimiter, char... elements) {
        if (elements == null || elements.length == 0) return "";
        StringBuilder sb = new StringBuilder(64);
        sb.append(elements[0]);
        for (int i = 1; i < elements.length; i++) {
            sb.append(delimiter).append(elements[i]);
        }
        return sb.toString();
    }
    public static String join(CharSequence delimiter, byte... elements) {
        if (elements == null || elements.length == 0) return "";
        StringBuilder sb = new StringBuilder(64);
        sb.append(elements[0]);
        for (int i = 1; i < elements.length; i++) {
            sb.append(delimiter).append(elements[i]);
        }
        return sb.toString();
    }
    public static String join(CharSequence delimiter, boolean... elements) {
        if (elements == null || elements.length == 0) return "";
        StringBuilder sb = new StringBuilder(64);
        sb.append(elements[0]);
        for (int i = 1; i < elements.length; i++) {
            sb.append(delimiter).append(elements[i]);
        }
        return sb.toString();
    }

    /**
     * Joins the items in {@code elements} by calling their toString method on them (or just using the String "null" for
     * null items), and separating each item with {@code delimiter}. Unlike other join methods in this class, this does
     * not take a vararg of Object items, since that would cause confusion with the overloads that take one object, such
     * as {@link #join(CharSequence, Iterable)}; it takes a non-vararg Object array instead.
     * @param delimiter the String or other CharSequence to separate items in elements with
     * @param elements the Object items to stringify and join into one String; if the array is null or empty, this
     *                 returns an empty String, and if items are null, they are shown as "null"
     * @return the String representations of the items in elements, separated by delimiter and put in one String
     */
    public static String join(CharSequence delimiter, Object[] elements) {
        if (elements == null || elements.length == 0) return "";
        StringBuilder sb = new StringBuilder(64);
        sb.append(elements[0]);
        for (int i = 1; i < elements.length; i++) {
            sb.append(delimiter).append(elements[i]);
        }
        return sb.toString();
    }
    /**
     * Joins the items in {@code elements} by calling their toString method on them (or just using the String "null" for
     * null items), and separating each item with {@code delimiter}. This can take any Iterable of any type for its
     * elements parameter.
     * @param delimiter the String or other CharSequence to separate items in elements with
     * @param elements the Object items to stringify and join into one String; if Iterable is null or empty, this
     *                 returns an empty String, and if items are null, they are shown as "null"
     * @return the String representations of the items in elements, separated by delimiter and put in one String
     */
    public static String join(CharSequence delimiter, Iterable elements) {
        if (elements == null) return "";
        Iterator it = elements.iterator();
        if(!it.hasNext()) return "";
        StringBuilder sb = new StringBuilder(64);
        sb.append(it.next());
        while(it.hasNext()) {
            sb.append(delimiter).append(it.next());
        }
        return sb.toString();
    }

    /**
     * Joins the boolean array {@code elements} without delimiters into a String, using "1" for true and "0" for false.
     * @param elements an array or vararg of booleans
     * @return a String using 1 for true elements and 0 for false, or "N" if elements is null
     */
    public static String joinAlt(boolean... elements) {
        if (elements == null) return "N";
        if(elements.length == 0) return "";
        StringBuilder sb = new StringBuilder(64);
        for (int i = 0; i < elements.length; i++) {
            sb.append(elements[i] ? '1' : '0');
        }
        return sb.toString();
    }

    /**
     * Like {@link #join(CharSequence, long...)}, but this appends an 'L' to each number so they can be read in by Java.
     * @param delimiter
     * @param elements
     * @return
     */
    public static String joinAlt(CharSequence delimiter, long... elements) {
        if (elements == null || elements.length == 0) return "";
        StringBuilder sb = new StringBuilder(elements.length << 2);
        sb.append(elements[0]).append('L');
        for (int i = 1; i < elements.length; i++) {
            sb.append(delimiter).append(elements[i]).append('L');
        }
        return sb.toString();
    }
    /**
     * Scans repeatedly in {@code source} for the String {@code search}, not scanning the same char twice except as part
     * of a larger String, and returns the number of instances of search that were found, or 0 if source is null or if
     * search is null or empty.
     * @param source a String to look through
     * @param search a String to look for
     * @return the number of times search was found in source
     */
    public static int count(final String source, final String search)
    {
        if(source == null || search == null || source.isEmpty() || search.isEmpty())
            return 0;
        int amount = 0, idx = -1;
        while ((idx = source.indexOf(search, idx+1)) >= 0)
            ++amount;
        return amount;
    }

    /**
     * Scans repeatedly in {@code source} for the codepoint {@code search} (which is usually a char literal), not
     * scanning the same section twice, and returns the number of instances of search that were found, or 0 if source is
     * null.
     * @param source a String to look through
     * @param search a codepoint or char to look for
     * @return the number of times search was found in source
     */
    public static int count(final String source, final int search)
    {
        if(source == null || source.isEmpty())
            return 0;
        int amount = 0, idx = -1;
        while ((idx = source.indexOf(search, idx+1)) >= 0)
            ++amount;
        return amount;
    }
    /**
     * Scans repeatedly in {@code source} (only using the area from startIndex, inclusive, to endIndex, exclusive) for
     * the String {@code search}, not scanning the same char twice except as part of a larger String, and returns the
     * number of instances of search that were found, or 0 if source or search is null or if the searched area is empty.
     * If endIndex is negative, this will search from startIndex until the end of the source.
     * @param source a String to look through
     * @param search a String to look for
     * @param startIndex the first index to search through, inclusive
     * @param endIndex the last index to search through, exclusive; if negative this will search the rest of source
     * @return the number of times search was found in source
     */
    public static int count(final String source, final String search, final int startIndex, int endIndex)
    {
        if(endIndex < 0) endIndex = 0x7fffffff;
        if(source == null || search == null || source.isEmpty() || search.isEmpty()
                || startIndex < 0 || startIndex >= endIndex)
            return 0;
        int amount = 0, idx = startIndex-1;
        while ((idx = source.indexOf(search, idx+1)) >= 0 && idx < endIndex)
            ++amount;
        return amount;
    }

    /**
     * Scans repeatedly in {@code source} (only using the area from startIndex, inclusive, to endIndex, exclusive) for
     * the codepoint {@code search} (which is usually a char literal), not scanning the same section twice, and returns
     * the number of instances of search that were found, or 0 if source is null or if the searched area is empty.
     * If endIndex is negative, this will search from startIndex until the end of the source.
     * @param source a String to look through
     * @param search a codepoint or char to look for
     * @param startIndex the first index to search through, inclusive
     * @param endIndex the last index to search through, exclusive; if negative this will search the rest of source
     * @return the number of times search was found in source
     */
    public static int count(final String source, final int search, final int startIndex, int endIndex)
    {
        if(endIndex < 0) endIndex = 0x7fffffff;
        if(source == null || source.isEmpty() || startIndex < 0 || startIndex >= endIndex)
            return 0;
        int amount = 0, idx = startIndex-1;
        while ((idx = source.indexOf(search, idx+1)) >= 0 && idx < endIndex)
            ++amount;
        return amount;
    }

    /**
     * Like {@link String#substring(int, int)} but returns "" instead of throwing any sort of Exception.
     * @param source the String to get a substring from
     * @param beginIndex the first index, inclusive; will be treated as 0 if negative
     * @param endIndex the index after the last character (exclusive); if negative this will be source.length()
     * @return the substring of source between beginIndex and endIndex, or "" if any parameters are null/invalid
     */
    public static String safeSubstring(String source, int beginIndex, int endIndex)
    {
        if(source == null || source.isEmpty()) return "";
        if(beginIndex < 0) beginIndex = 0;
        if(endIndex < 0 || endIndex > source.length()) endIndex = source.length();
        if(beginIndex > endIndex) return "";
        return source.substring(beginIndex, endIndex);
    }

    /**
     * Like {@link String#split(String)} but doesn't use any regex for splitting (delimiter is a literal String).
     * @param source the String to get split-up substrings from
     * @param delimiter the literal String to split on (not a regex); will not be included in the returned String array
     * @return a String array consisting of at least one String (all of Source if nothing was split)
     */
    public static String[] split(String source, String delimiter) {
        int amount = count(source, delimiter);
        if (amount <= 0) return new String[]{source};
        String[] splat = new String[amount+1];
        int dl = delimiter.length(), idx = -dl, idx2;
        for (int i = 0; i < amount; i++) {
            splat[i] = safeSubstring(source, idx+dl, idx = source.indexOf(delimiter, idx+dl));
        }
        if((idx2 = source.indexOf(delimiter, idx+dl)) < 0)
        {
            splat[amount] = safeSubstring(source, idx+dl, source.length());
        }
        else
        {
            splat[amount] = safeSubstring(source, idx+dl, idx2);
        }
        return splat;
    }

    public static final String mask64 = "0000000000000000000000000000000000000000000000000000000000000000",
            mask32 = "00000000000000000000000000000000",
            mask16 = "0000000000000000",
            mask8 = "00000000";

    private static final char[] keyBase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".toCharArray(),
            valBase64 = new char[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                    62, 0, 0, 0, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, 64, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
                    0, 0, 0, 0, 0, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0, 0, 0, 0, 0};

    private static StringBuilder hexBuilder = new StringBuilder(16).append("0000000000000000");
    public static String hex(long number) {
        for (int i = 0; i < 16; i++) {
            hexBuilder.setCharAt(15 - i, hexDigits[(int)(number >> (i << 2) & 15)]);
        }
        return hexBuilder.toString();
    }

    public static String hex(double number) {
        // avoids creating temporary long values, which can be slow on GWT
        int h = NumberTools.doubleToLowIntBits(number);
        for (int i = 0; i < 8; i++) {
            hexBuilder.setCharAt(15 - i, hexDigits[(h >> (i << 2) & 15)]);
        }
        h = NumberTools.doubleToHighIntBits(number);
        for (int i = 0; i < 8; i++) {
            hexBuilder.setCharAt(7 - i, hexDigits[(h >> (i << 2) & 15)]);
        }
        return hexBuilder.toString();
    }

    public static String hex(int number) {
        for (int i = 0; i < 8; i++) {
            hexBuilder.setCharAt(7 - i, hexDigits[(number >> (i << 2) & 15)]);
        }
        return hexBuilder.substring(0, 8);
    }
    
    public static String hex(float number) {
        final int h = NumberTools.floatToIntBits(number);
        for (int i = 0; i < 8; i++) {
            hexBuilder.setCharAt(7 - i, hexDigits[(h >> (i << 2) & 15)]);
        }
        return hexBuilder.substring(0, 8);
    }

    public static String hex(short number) {
        for (int i = 0; i < 4; i++) {
            hexBuilder.setCharAt(3 - i, hexDigits[(number >> (i << 2) & 15)]);
        }
        return hexBuilder.substring(0, 4);
    }

    public static String hex(char number) {
        for (int i = 0; i < 4; i++) {
            hexBuilder.setCharAt(3 - i, hexDigits[(number >> (i << 2) & 15)]);
        }
        return hexBuilder.substring(0, 4);
    }

    public static String hex(byte number) {
        hexBuilder.setCharAt(0, hexDigits[(number >> 4 & 15)]);
        hexBuilder.setCharAt(1, hexDigits[(number & 15)]);
        return hexBuilder.substring(0, 2);
    }

    public static StringBuilder appendHex(StringBuilder builder, long number){
        for (int i = 60; i >= 0; i -= 4) {
            builder.append(hexDigits[(int)(number >> i & 15)]);
        }
        return builder;
    }
    public static StringBuilder appendHex(StringBuilder builder, double number){
        // avoids creating temporary long values, which can be slow on GWT
        int h = NumberTools.doubleToHighIntBits(number);
        for (int i = 28; i >= 0; i -= 4) {
            builder.append(hexDigits[(h >> i & 15)]);
        }
        h = NumberTools.doubleToLowIntBits(number);
        for (int i = 28; i >= 0; i -= 4) {
            builder.append(hexDigits[(h >> i & 15)]);
        }
        return builder;
    }
    public static StringBuilder appendHex(StringBuilder builder, int number){
        for (int i = 28; i >= 0; i -= 4) {
            builder.append(hexDigits[(number >> i & 15)]);
        }
        return builder;
    }
    public static StringBuilder appendHex(StringBuilder builder, float number){
        final int h = NumberTools.floatToIntBits(number);
        for (int i = 28; i >= 0; i -= 4) {
            builder.append(hexDigits[(h >> i & 15)]);
        }
        return builder;
    }
    public static StringBuilder appendHex(StringBuilder builder, short number){
        for (int i = 12; i >= 0; i -= 4) {
            builder.append(hexDigits[(number >> i & 15)]);
        }
        return builder;
    }
    public static StringBuilder appendHex(StringBuilder builder, char number){
        for (int i = 12; i >= 0; i -= 4) {
            builder.append(hexDigits[(number >> i & 15)]);
        }
        return builder;
    }
    public static StringBuilder appendHex(StringBuilder builder, byte number){
        builder.append(hexDigits[(number >> 4 & 15)]);
        builder.append(hexDigits[(number & 15)]);
        return builder;
    }

    public static String hex(long[] numbers) {
        int len;
        if (numbers == null || (len = numbers.length) <= 0) return "";
        StringBuilder sb = new StringBuilder(numbers.length << 4);
        for (int i = 0; i < len; i++) {
            appendHex(sb, numbers[i]);
        }
        return sb.toString();
    }

    public static String hex(double[] numbers) {
        int len;
        if (numbers == null || (len = numbers.length) <= 0) return "";
        StringBuilder sb = new StringBuilder(numbers.length << 4);
        for (int i = 0; i < len; i++) {
            appendHex(sb, numbers[i]);
        }
        return sb.toString();
    }

    public static String hex(int[] numbers) {
        int len;
        if (numbers == null || (len = numbers.length) <= 0) return "";
        StringBuilder sb = new StringBuilder(numbers.length << 3);
        for (int i = 0; i < len; i++) {
            appendHex(sb, numbers[i]);
        }
        return sb.toString();
    }


    public static String hex(float[] numbers) {
        int len;
        if (numbers == null || (len = numbers.length) <= 0) return "";
        StringBuilder sb = new StringBuilder(numbers.length << 3);
        for (int i = 0; i < len; i++) {
            appendHex(sb, numbers[i]);
        }
        return sb.toString();
    }

    public static String hex(short[] numbers) {
        int len;
        if (numbers == null || (len = numbers.length) <= 0) return "";
        StringBuilder sb = new StringBuilder(numbers.length << 2);
        for (int i = 0; i < len; i++) {
            appendHex(sb, numbers[i]);
        }
        return sb.toString();
    }

    public static String hex(char[] numbers) {
        int len;
        if (numbers == null || (len = numbers.length) <= 0) return "";
        StringBuilder sb = new StringBuilder(numbers.length << 2);
        for (int i = 0; i < len; i++) {
            appendHex(sb, numbers[i]);
        }
        return sb.toString();
    }

    public static String hex(byte[] numbers) {
        int len;
        if (numbers == null || (len = numbers.length) <= 0) return "";
        StringBuilder sb = new StringBuilder(numbers.length << 1);
        for (int i = 0; i < len; i++) {
            appendHex(sb, numbers[i]);
        }
        return sb.toString();
    }

    public static String bin(long number) {
        String h = Long.toBinaryString(number);
        return mask64.substring(0, 64 - h.length()) + h;
    }

    public static String bin(int number) {
        String h = Integer.toBinaryString(number);
        return mask32.substring(0, 32 - h.length()) + h;
    }

    public static String bin(short number) {
        String h = Integer.toBinaryString(number & 0xffff);
        return mask16.substring(0, 16 - h.length()) + h;
    }

    public static String bin(char number) {
        String h = Integer.toBinaryString(number & 0xffff);
        return mask16.substring(0, 16 - h.length()) + h;
    }

    public static String bin(byte number) {
        String h = Integer.toBinaryString(number & 0xff);
        return mask8.substring(0, 8 - h.length()) + h;
    }
    private static final int[] hexCodes = new int[]
            {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
             -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
             -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
              0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1,
             -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1,
             -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
             -1,10,11,12,13,14,15};

    /**
     * Reads in a CharSequence containing only hex digits (only 0-9, a-f, and A-F) with an optional sign at the start
     * and returns the long they represent, reading at most 16 characters (17 if there is a sign) and returning the
     * result if valid, or 0 if nothing could be read. The leading sign can be '+' or '-' if present. This can also
     * represent negative numbers as they are printed by such methods as String.format given a %x in the formatting
     * string, or this class' {@link #hex(long)} method; that is, if the first char of a 16-char (or longer)
     * CharSequence is a hex digit 8 or higher, then the whole number represents a negative number, using two's
     * complement and so on. This means "FFFFFFFFFFFFFFFF" would return the long -1 when passed to this, though you
     * could also simply use "-1 ".
     * 
* Should be fairly close to Java 8's Long.parseUnsignedLong method, which is an odd omission from earlier JDKs. * This doesn't throw on invalid input, though, instead returning 0 if the first char is not a hex digit, or * stopping the parse process early if a non-hex-digit char is read before the end of cs is reached. If the parse is * stopped early, this behaves as you would expect for a number with less digits, and simply doesn't fill the larger * places. * @param cs a CharSequence, such as a String, containing only hex digits with an optional sign (no 0x at the start) * @return the long that cs represents */ public static long longFromHex(final CharSequence cs) { return longFromHex(cs, 0, cs.length()); } /** * Reads in a CharSequence containing only hex digits (only 0-9, a-f, and A-F) with an optional sign at the start * and returns the long they represent, reading at most 16 characters (17 if there is a sign) and returning the * result if valid, or 0 if nothing could be read. The leading sign can be '+' or '-' if present. This can also * represent negative numbers as they are printed by such methods as String.format given a %x in the formatting * string, or this class' {@link #hex(long)} method; that is, if the first char of a 16-char (or longer) * CharSequence is a hex digit 8 or higher, then the whole number represents a negative number, using two's * complement and so on. This means "FFFFFFFFFFFFFFFF" would return the long -1 when passed to this, though you * could also simply use "-1 ". If you use both '-' at the start and have the most significant digit as 8 or higher, * such as with "-FFFFFFFFFFFFFFFF", then both indicate a negative number, but the digits will be processed first * (producing -1) and then the whole thing will be multiplied by -1 to flip the sign again (returning 1). *
* Should be fairly close to Java 8's Long.parseUnsignedLong method, which is an odd omission from earlier JDKs. * This doesn't throw on invalid input, though, instead returning 0 if the first char is not a hex digit, or * stopping the parse process early if a non-hex-digit char is read before end is reached. If the parse is stopped * early, this behaves as you would expect for a number with less digits, and simply doesn't fill the larger places. * @param cs a CharSequence, such as a String, containing only hex digits with an optional sign (no 0x at the start) * @param start the (inclusive) first character position in cs to read * @param end the (exclusive) last character position in cs to read (this stops after 16 characters if end is too large) * @return the long that cs represents */ public static long longFromHex(final CharSequence cs, final int start, int end) { int len, h, lim = 16; if (cs == null || start < 0 || end <= 0 || end - start <= 0 || (len = cs.length()) - start <= 0 || end > len) return 0; char c = cs.charAt(start); if (c == '-') { len = -1; h = 0; lim = 17; } else if (c == '+') { len = 1; h = 0; lim = 17; } else if (c > 102 || (h = hexCodes[c]) < 0) return 0; else { len = 1; } long data = h; for (int i = start + 1; i < end && i < start + lim; i++) { if ((c = cs.charAt(i)) > 102 || (h = hexCodes[c]) < 0) return data * len; data <<= 4; data |= h; } return data * len; } /** * Reads in a char[] containing only hex digits (only 0-9, a-f, and A-F) with an optional sign at the start and * returns the long they represent, reading at most 16 characters (17 if there is a sign) and returning the result * if valid, or 0 if nothing could be read. The leading sign can be '+' or '-' if present. This can also represent * negative numbers as they are printed by such methods as String.format given a %x in the formatting string, or * this class' {@link #hex(long)} method; that is, if the first digit of a 16-char (or longer) char[] is a hex * digit 8 or higher, then the whole number represents a negative number, using two's complement and so on. This * means "FFFFFFFFFFFFFFFF" would return the long -1L when passed to this, though you could also simply use "-1 ". * If you use both '-' at the start and have the most significant digit as 8 or higher, such as with * "-FFFFFFFFFFFFFFFF", then both indicate a negative number, but the digits will be processed first (producing -1) * and then the whole thing will be multiplied by -1 to flip the sign again (returning 1). *
* Should be fairly close to Java 8's Long.parseUnsignedLong method, which is an odd omission from earlier JDKs. * This doesn't throw on invalid input, though, instead returning 0 if the first char is not a hex digit, or * stopping the parse process early if a non-hex-digit char is read before end is reached. If the parse is stopped * early, this behaves as you would expect for a number with less digits, and simply doesn't fill the larger places. * @param cs a char array containing only hex digits with an optional sign (no 0x at the start) * @param start the (inclusive) first character position in cs to read * @param end the (exclusive) last character position in cs to read (this stops after 8 or 9 characters if end is too large, depending on sign) * @return the long that cs represents */ public static long longFromHex(final char[] cs, final int start, int end) { int len, h, lim = 16; if(cs == null || start < 0 || end <=0 || end - start <= 0 || (len = cs.length) - start <= 0 || end > len) return 0; char c = cs[start]; if(c == '-') { len = -1; h = 0; lim = 17; } else if(c == '+') { len = 1; h = 0; lim = 17; } else if(c > 102 || (h = hexCodes[c]) < 0) return 0; else { len = 1; } int data = h; for (int i = start + 1; i < end && i < start + lim; i++) { if((c = cs[i]) > 102 || (h = hexCodes[c]) < 0) return data * len; data <<= 4; data |= h; } return data * len; } /** * Reads in a CharSequence containing only hex digits (only 0-9, a-f, and A-F) with an optional sign at the start * and returns the int they represent, reading at most 8 characters (9 if there is a sign) and returning the result * if valid, or 0 if nothing could be read. The leading sign can be '+' or '-' if present. This can also represent * negative numbers as they are printed by such methods as String.format given a %x in the formatting string, or * this class' {@link #hex(int)} method; that is, if the first digit of an 8-char (or longer) CharSequence is a hex * digit 8 or higher, then the whole number represents a negative number, using two's complement and so on. This * means "FFFFFFFF" would return the int -1 when passed to this, though you could also simply use "-1 ". If you use * both '-' at the start and have the most significant digit as 8 or higher, such as with "-FFFFFFFF", then both * indicate a negative number, but the digits will be processed first (producing -1) and then the whole thing will * be multiplied by -1 to flip the sign again (returning 1). *
* Should be fairly close to Java 8's Integer.parseUnsignedInt method, which is an odd omission from earlier JDKs. * This doesn't throw on invalid input, though, instead returning 0 if the first char is not a hex digit, or * stopping the parse process early if a non-hex-digit char is read before the end of cs is reached. If the parse is * stopped early, this behaves as you would expect for a number with less digits, and simply doesn't fill the larger * places. * @param cs a CharSequence, such as a String, containing only hex digits with an optional sign (no 0x at the start) * @return the int that cs represents */ public static int intFromHex(final CharSequence cs) { return intFromHex(cs, 0, cs.length()); } /** * Reads in a CharSequence containing only hex digits (only 0-9, a-f, and A-F) with an optional sign at the start * and returns the int they represent, reading at most 8 characters (9 if there is a sign) and returning the result * if valid, or 0 if nothing could be read. The leading sign can be '+' or '-' if present. This can also represent * negative numbers as they are printed by such methods as String.format given a %x in the formatting string, or * this class' {@link #hex(int)} method; that is, if the first digit of an 8-char (or longer) CharSequence is a hex * digit 8 or higher, then the whole number represents a negative number, using two's complement and so on. This * means "FFFFFFFF" would return the int -1 when passed to this, though you could also simply use "-1 ". If you use * both '-' at the start and have the most significant digit as 8 or higher, such as with "-FFFFFFFF", then both * indicate a negative number, but the digits will be processed first (producing -1) and then the whole thing will * be multiplied by -1 to flip the sign again (returning 1). *
* Should be fairly close to Java 8's Integer.parseUnsignedInt method, which is an odd omission from earlier JDKs. * This doesn't throw on invalid input, though, instead returning 0 if the first char is not a hex digit, or * stopping the parse process early if a non-hex-digit char is read before end is reached. If the parse is stopped * early, this behaves as you would expect for a number with less digits, and simply doesn't fill the larger places. * @param cs a CharSequence, such as a String, containing only hex digits with an optional sign (no 0x at the start) * @param start the (inclusive) first character position in cs to read * @param end the (exclusive) last character position in cs to read (this stops after 8 or 9 characters if end is too large, depending on sign) * @return the int that cs represents */ public static int intFromHex(final CharSequence cs, final int start, int end) { int len, h, lim = 8; if(cs == null || start < 0 || end <=0 || end - start <= 0 || (len = cs.length()) - start <= 0 || end > len) return 0; char c = cs.charAt(start); if(c == '-') { len = -1; h = 0; lim = 9; } else if(c == '+') { len = 1; h = 0; lim = 9; } else if(c > 102 || (h = hexCodes[c]) < 0) return 0; else { len = 1; } int data = h; for (int i = start + 1; i < end && i < start + lim; i++) { if((c = cs.charAt(i)) > 102 || (h = hexCodes[c]) < 0) return data * len; data <<= 4; data |= h; } return data * len; } /** * Reads in a char[] containing only hex digits (only 0-9, a-f, and A-F) with an optional sign at the start * and returns the int they represent, reading at most 8 characters (9 if there is a sign) and returning the result * if valid, or 0 if nothing could be read. The leading sign can be '+' or '-' if present. This can also represent * negative numbers as they are printed by such methods as String.format given a %x in the formatting string, or * this class' {@link #hex(int)} method; that is, if the first digit of an 8-char (or longer) char[] is a hex * digit 8 or higher, then the whole number represents a negative number, using two's complement and so on. This * means "FFFFFFFF" would return the int -1 when passed to this, though you could also simply use "-1 ". If you use * both '-' at the start and have the most significant digit as 8 or higher, such as with "-FFFFFFFF", then both * indicate a negative number, but the digits will be processed first (producing -1) and then the whole thing will * be multiplied by -1 to flip the sign again (returning 1). *
* Should be fairly close to Java 8's Integer.parseUnsignedInt method, which is an odd omission from earlier JDKs. * This doesn't throw on invalid input, though, instead returning 0 if the first char is not a hex digit, or * stopping the parse process early if a non-hex-digit char is read before end is reached. If the parse is stopped * early, this behaves as you would expect for a number with less digits, and simply doesn't fill the larger places. * @param cs a char array containing only hex digits with an optional sign (no 0x at the start) * @param start the (inclusive) first character position in cs to read * @param end the (exclusive) last character position in cs to read (this stops after 8 or 9 characters if end is too large, depending on sign) * @return the int that cs represents */ public static int intFromHex(final char[] cs, final int start, int end) { int len, h, lim = 8; if(cs == null || start < 0 || end <=0 || end - start <= 0 || (len = cs.length) - start <= 0 || end > len) return 0; char c = cs[start]; if(c == '-') { len = -1; h = 0; lim = 9; } else if(c == '+') { len = 1; h = 0; lim = 9; } else if(c > 102 || (h = hexCodes[c]) < 0) return 0; else { len = 1; } int data = h; for (int i = start + 1; i < end && i < start + lim; i++) { if((c = cs[i]) > 102 || (h = hexCodes[c]) < 0) return data * len; data <<= 4; data |= h; } return data * len; } /** * Reads in a CharSequence containing only decimal digits (0-9) with an optional sign at the start and returns the * long they represent, reading at most 19 characters (20 if there is a sign) and returning the result if valid, or * 0 if nothing could be read. The leading sign can be '+' or '-' if present. Unlike * {@link #intFromDec(CharSequence)}, this can't effectively be used to read unsigned longs as decimal literals, * since anything larger than the highest signed long would be larger than the normal limit for longs as text (it * would be 20 characters without a sign, where we limit it to 19 without a sign to match normal behavior). *
* Should be fairly close to the JDK's Long.parseLong method, but this also supports CharSequence data instead of * just String data, and ignores chars after the number. This doesn't throw on invalid input, either, instead * returning 0 if the first char is not a decimal digit, or stopping the parse process early if a non-decimal-digit * char is read before the end of cs is reached. If the parse is stopped early, this behaves as you would expect for * a number with less digits, and simply doesn't fill the larger places. * @param cs a CharSequence, such as a String, containing only digits 0-9 with an optional sign * @return the long that cs represents */ public static long longFromDec(final CharSequence cs) { return longFromDec(cs,0, cs.length()); } /** * Reads in a CharSequence containing only decimal digits (0-9) with an optional sign at the start and returns the * long they represent between the given positions {@code start} and {@code end}, reading at most 19 characters (20 * if there is a sign) or until end is reached and returning the result if valid, or 0 if nothing could be read. The * leading sign can be '+' or '-' if present. Unlike {@link #intFromDec(CharSequence, int, int)}, this can't * effectively be used to read unsigned longs as decimal literals, since anything larger than the highest signed * long would be larger than the normal limit for longs as text (it would be 20 characters without a sign, where we * limit it to 19 without a sign to match normal behavior). *
* Should be fairly close to the JDK's Long.parseLong method, but this also supports CharSequence data instead of * just String data, and allows specifying a start and end. This doesn't throw on invalid input, either, instead * returning 0 if the first char is not a decimal digit, or stopping the parse process early if a non-decimal-digit * char is read before end is reached. If the parse is stopped early, this behaves as you would expect for a number * with less digits, and simply doesn't fill the larger places. * @param cs a CharSequence, such as a String, containing only digits 0-9 with an optional sign * @param start the (inclusive) first character position in cs to read * @param end the (exclusive) last character position in cs to read (this stops after 19 or 20 characters if end is too large, depending on sign) * @return the long that cs represents */ public static long longFromDec(final CharSequence cs, final int start, int end) { int len, h, lim = 19; long sign = 1L; if(cs == null || start < 0 || end <=0 || end - start <= 0 || (len = cs.length()) - start <= 0 || end > len) return 0L; char c = cs.charAt(start); if(c == '-') { sign = -1L; lim = 20; h = 0; } else if(c == '+') { lim = 20; h = 0; } else if(c > 102 || (h = hexCodes[c]) < 0 || h > 9) return 0L; long data = h; for (int i = start + 1; i < end && i < start + lim; i++) { if((c = cs.charAt(i)) > 102 || (h = hexCodes[c]) < 0 || h > 9) return data * sign; data = data * 10 + h; } return data * sign; } /** * Reads in a CharSequence containing only decimal digits (0-9) with an optional sign at the start and returns the * int they represent, reading at most 10 characters (11 if there is a sign) and returning the result if valid, or 0 * if nothing could be read. The leading sign can be '+' or '-' if present. This can technically be used to handle * unsigned integers in decimal format, but it isn't the intended purpose. If you do use it for handling unsigned * ints, 2147483647 is normally the highest positive int and -2147483648 the lowest negative one, but if you give * this a number between 2147483647 and {@code 2147483647 + 2147483648}, it will interpret it as a negative number * that fits in bounds using the normal rules for converting between signed and unsigned numbers. *
* Should be fairly close to the JDK's Integer.parseInt method, but this also supports CharSequence data instead of * just String data, and ignores chars after the number. This doesn't throw on invalid input, either, instead * returning 0 if the first char is not a decimal digit, or stopping the parse process early if a non-decimal-digit * char is read before the end of cs is reached. If the parse is stopped early, this behaves as you would expect for * a number with less digits, and simply doesn't fill the larger places. * @param cs a CharSequence, such as a String, containing only digits 0-9 with an optional sign * @return the int that cs represents */ public static int intFromDec(final CharSequence cs) { return intFromDec(cs, 0, cs.length()); } /** * Reads in a CharSequence containing only decimal digits (0-9) with an optional sign at the start and returns the * int they represent, reading at most 10 characters (11 if there is a sign) and returning the result if valid, or 0 * if nothing could be read. The leading sign can be '+' or '-' if present. This can technically be used to handle * unsigned integers in decimal format, but it isn't the intended purpose. If you do use it for handling unsigned * ints, 2147483647 is normally the highest positive int and -2147483648 the lowest negative one, but if you give * this a number between 2147483647 and {@code 2147483647 + 2147483648}, it will interpret it as a negative number * that fits in bounds using the normal rules for converting between signed and unsigned numbers. *
* Should be fairly close to the JDK's Integer.parseInt method, but this also supports CharSequence data instead of * just String data, and allows specifying a start and end. This doesn't throw on invalid input, either, instead * returning 0 if the first char is not a decimal digit, or stopping the parse process early if a non-decimal-digit * char is read before end is reached. If the parse is stopped early, this behaves as you would expect for a number * with less digits, and simply doesn't fill the larger places. * @param cs a CharSequence, such as a String, containing only digits 0-9 with an optional sign * @param start the (inclusive) first character position in cs to read * @param end the (exclusive) last character position in cs to read (this stops after 10 or 11 characters if end is too large, depending on sign) * @return the int that cs represents */ public static int intFromDec(final CharSequence cs, final int start, int end) { int len, h, lim = 10; if(cs == null || start < 0 || end <=0 || end - start <= 0 || (len = cs.length()) - start <= 0 || end > len) return 0; char c = cs.charAt(start); if(c == '-') { len = -1; lim = 11; h = 0; } else if(c == '+') { len = 1; lim = 11; h = 0; } else if(c > 102 || (h = hexCodes[c]) < 0 || h > 9) return 0; else { len = 1; } int data = h; for (int i = start + 1; i < end && i < start + lim; i++) { if((c = cs.charAt(i)) > 102 || (h = hexCodes[c]) < 0 || h > 9) return data * len; data = data * 10 + h; } return data * len; } /** * Reads in a CharSequence containing only binary digits (only 0 and 1) and returns the long they represent, * reading at most 64 characters and returning the result if valid or 0 otherwise. The first digit is considered * the sign bit iff cs is 64 chars long. *
* Should be fairly close to Java 8's Long.parseUnsignedLong method, which is a bizarre omission from earlier JDKs. * This doesn't throw on invalid input, though, instead returning 0. * @param cs a CharSequence, such as a String, containing only binary digits (nothing at the start) * @return the long that cs represents */ public static long longFromBin(CharSequence cs) { return longFromBin(cs, 0, cs.length()); } /** * Reads in a CharSequence containing only binary digits (only 0 and 1) and returns the long they represent, * reading at most 64 characters and returning the result if valid or 0 otherwise. The first digit is considered * the sign bit iff cs is 64 chars long. *
* Should be fairly close to Java 8's Long.parseUnsignedLong method, which is a bizarre omission from earlier JDKs. * This doesn't throw on invalid input, though, instead returning 0. * @param cs a CharSequence, such as a String, containing only binary digits (nothing at the start) * @param start the first character position in cs to read from * @param end the last character position in cs to read from (this stops after 64 characters if end is too large) * @return the long that cs represents */ public static long longFromBin(CharSequence cs, final int start, final int end) { int len; if(cs == null || start < 0 || end <=0 || end - start <= 0 || (len = cs.length()) - start <= 0 || end > len) return 0; char c = cs.charAt(start); if(c < '0' || c > '1') return 0; long data = hexCodes[c]; for (int i = start+1; i < end && i < start+64; i++) { if((c = cs.charAt(i)) < '0' || c > '1') return 0; data <<= 1; data |= c - '0'; } return data; } /** * Reads in a CharSequence containing only binary digits (only 0 and 1) and returns the int they represent, * reading at most 32 characters and returning the result if valid or 0 otherwise. The first digit is considered * the sign bit iff cs is 32 chars long. *
* Should be fairly close to Java 8's Integer.parseUnsignedInt method, which is a bizarre omission from earlier * JDKs. This doesn't throw on invalid input, though, instead returning 0. * @param cs a CharSequence, such as a String, containing only binary digits (nothing at the start) * @return the int that cs represents */ public static int intFromBin(CharSequence cs) { return intFromBin(cs, 0, cs.length()); } /** * Reads in a CharSequence containing only binary digits (only 0 and 1) and returns the int they represent, * reading at most 32 characters and returning the result if valid or 0 otherwise. The first digit is considered * the sign bit iff cs is 32 chars long. *
* Should be fairly close to Java 8's Integer.parseUnsignedInt method, which is a bizarre omission from earlier * JDKs. This doesn't throw on invalid input, though, instead returning 0. * @param cs a CharSequence, such as a String, containing only binary digits (nothing at the start) * @param start the first character position in cs to read from * @param end the last character position in cs to read from (this stops after 32 characters if end is too large) * @return the int that cs represents */ public static int intFromBin(CharSequence cs, final int start, final int end) { int len; if(cs == null || start < 0 || end <=0 || end - start <= 0 || (len = cs.length()) - start <= 0 || end > len) return 0; char c = cs.charAt(start); if(c < '0' || c > '1') return 0; int data = hexCodes[c]; for (int i = start+1; i < end && i < start+32; i++) { if((c = cs.charAt(i)) < '0' || c > '1') return 0; data <<= 1; data |= c - '0'; } return data; } /** * Base-64 encodes number and stores that string representation in buf starting at offset; uses 11 chars. * * @param number the long to encode * @param offset the first position to set in buf * @param buf a char array that should be non-null and have length of at least offset + 11 * @return buf, after modifying it in-place */ public static char[] b64Encode(long number, int offset, char[] buf) { if (buf != null && buf.length >= 11 - offset) { buf[offset] = keyBase64[(int) (number >>> 60)]; buf[offset + 1] = keyBase64[(int) (0x3f & number >>> 54)]; buf[offset + 2] = keyBase64[(int) (0x3f & number >>> 48)]; buf[offset + 3] = keyBase64[(int) (0x3f & number >>> 42)]; buf[offset + 4] = keyBase64[(int) (0x3f & number >>> 36)]; buf[offset + 5] = keyBase64[(int) (0x3f & number >>> 30)]; buf[offset + 6] = keyBase64[(int) (0x3f & number >>> 24)]; buf[offset + 7] = keyBase64[(int) (0x3f & number >>> 18)]; buf[offset + 8] = keyBase64[(int) (0x3f & number >>> 12)]; buf[offset + 9] = keyBase64[(int) (0x3f & number >>> 6)]; buf[offset + 10] = keyBase64[(int) (0x3f & number)]; } return buf; } /** * Base-64 encodes number and stores that string representation in buf starting at offset; uses 11 chars. * * @param number the double to encode * @param offset the first position to set in buf * @param buf a char array that should be non-null and have length of at least offset + 11 * @return buf, after modifying it in-place */ public static char[] b64Encode(double number, int offset, char[] buf) { return b64Encode(NumberTools.doubleToLongBits(number), offset, buf); } /** * Base-64 encodes number and stores that string representation in buf starting at offset; uses 6 chars. * * @param number the int to encode * @param offset the first position to set in buf * @param buf a char array that should be non-null and have length of at least offset + 6 * @return buf, after modifying it in-place */ public static char[] b64Encode(int number, int offset, char[] buf) { if (buf != null && buf.length >= 6 - offset) { buf[offset] = keyBase64[number >>> 30]; buf[offset + 1] = keyBase64[0x3f & number >>> 24]; buf[offset + 2] = keyBase64[0x3f & number >>> 18]; buf[offset + 3] = keyBase64[0x3f & number >>> 12]; buf[offset + 4] = keyBase64[0x3f & number >>> 6]; buf[offset + 5] = keyBase64[0x3f & number]; } return buf; } /** * Base-64 encodes number and stores that string representation in buf starting at offset; uses 6 chars. * * @param number the float to encode * @param offset the first position to set in buf * @param buf a char array that should be non-null and have length of at least offset + 6 * @return buf, after modifying it in-place */ public static char[] b64Encode(float number, int offset, char[] buf) { return b64Encode(NumberTools.floatToIntBits(number), offset, buf); } /** * Base-64 encodes number and stores that string representation in buf starting at offset; uses 3 chars. * * @param number the int to encode * @param offset the first position to set in buf * @param buf a char array that should be non-null and have length of at least offset + 3 * @return buf, after modifying it in-place */ public static char[] b64Encode(short number, int offset, char[] buf) { if (buf != null && buf.length >= 3 - offset) { buf[offset] = keyBase64[number >>> 12]; buf[offset + 1] = keyBase64[0x3f & number >>> 6]; buf[offset + 2] = keyBase64[0x3f & number]; } return buf; } /** * Base-64 encodes glyph and stores that string representation in buf starting at offset; uses 3 chars. * * @param glyph the char to encode * @param offset the first position to set in buf * @param buf a char array that should be non-null and have length of at least offset + 3 * @return buf, after modifying it in-place */ public static char[] b64Encode(char glyph, int offset, char[] buf) { if (buf != null && buf.length >= 4 - offset) { buf[offset] = keyBase64[glyph >>> 12]; buf[offset + 1] = keyBase64[0x3f & glyph >>> 6]; buf[offset + 2] = keyBase64[0x3f & glyph]; } return buf; } /** * Base-64 encodes number and stores that string representation in buf starting at offset; uses 2 chars. * * @param number the byte to encode * @param offset the first position to set in buf * @param buf a char array that should be non-null and have length of at least offset + 2 * @return buf, after modifying it in-place */ public static char[] b64Encode(byte number, int offset, char[] buf) { if (buf != null && buf.length >= 2 - offset) { buf[offset] = keyBase64[number >>> 6]; buf[offset + 1] = keyBase64[0x3f & number]; } return buf; } /** * Decodes 11 characters from data starting from offset to get a long encoded as base-64. * @param data a char array that should be have length of at least offset + 11 * @param offset where in data to start reading from * @return the decoded long */ public static long b64DecodeLong(char[] data, int offset) { return (data == null || data.length < 11 + offset) ? 0L : (((long)valBase64[data[offset] & 0x7F]) << 60) | ((0x3fL & valBase64[data[offset + 1 ] & 0x7F]) << 54) | ((0x3fL & valBase64[data[offset + 2 ] & 0x7F]) << 48) | ((0x3fL & valBase64[data[offset + 3 ] & 0x7F]) << 42) | ((0x3fL & valBase64[data[offset + 4 ] & 0x7F]) << 36) | ((0x3fL & valBase64[data[offset + 5 ] & 0x7F]) << 30) | ((0x3fL & valBase64[data[offset + 6 ] & 0x7F]) << 24) | ((0x3fL & valBase64[data[offset + 7 ] & 0x7F]) << 18) | ((0x3fL & valBase64[data[offset + 8 ] & 0x7F]) << 12) | ((0x3fL & valBase64[data[offset + 9 ] & 0x7F]) << 6) | (0x3fL & valBase64[data[offset + 10] & 0x7F]); } /** * Decodes 11 characters from data starting from offset to get a double encoded as base-64. * @param data a char array that should be have length of at least offset + 11 * @param offset where in data to start reading from * @return the decoded double */ public static double b64DecodeDouble(char[] data, int offset) { return (data == null || data.length < 11 + offset) ? 0.0 : NumberTools.longBitsToDouble((((long) valBase64[data[offset] & 0x7F]) << 60) | ((0x3fL & valBase64[data[offset + 1 ] & 0x7F]) << 54) | ((0x3fL & valBase64[data[offset + 2 ] & 0x7F]) << 48) | ((0x3fL & valBase64[data[offset + 3 ] & 0x7F]) << 42) | ((0x3fL & valBase64[data[offset + 4 ] & 0x7F]) << 36) | ((0x3fL & valBase64[data[offset + 5 ] & 0x7F]) << 30) | ((0x3fL & valBase64[data[offset + 6 ] & 0x7F]) << 24) | ((0x3fL & valBase64[data[offset + 7 ] & 0x7F]) << 18) | ((0x3fL & valBase64[data[offset + 8 ] & 0x7F]) << 12) | ((0x3fL & valBase64[data[offset + 9 ] & 0x7F]) << 6) | (0x3fL & valBase64[data[offset + 10] & 0x7F])); } /** * Decodes 6 characters from data starting from offset to get an int encoded as base-64. * @param data a char array that should be have length of at least offset + 6 * @param offset where in data to start reading from * @return the decoded int */ public static int b64DecodeInt(char[] data, int offset) { return (data == null || data.length < 6 + offset) ? 0 : ((valBase64[data[offset] & 0x7F]) << 30) | ((valBase64[data[offset + 1] & 0x7F]) << 24) | ((valBase64[data[offset + 2] & 0x7F]) << 18) | ((valBase64[data[offset + 3] & 0x7F]) << 12) | ((valBase64[data[offset + 4] & 0x7F]) << 6) | (valBase64[data[offset + 5] & 0x7F]); } /** * Decodes 6 characters from data starting from offset to get a float encoded as base-64. * @param data a char array that should be have length of at least offset + 6 * @param offset where in data to start reading from * @return the decoded float */ public static float b64DecodeFloat(char[] data, int offset) { return (data == null || data.length < 6 + offset) ? 0f : NumberTools.intBitsToFloat(((valBase64[data[offset] & 0x7F]) << 30) | ((valBase64[data[offset + 1] & 0x7F]) << 24) | ((valBase64[data[offset + 2] & 0x7F]) << 18) | ((valBase64[data[offset + 3] & 0x7F]) << 12) | ((valBase64[data[offset + 4] & 0x7F]) << 6) | ( valBase64[data[offset + 5] & 0x7F])); } /** * Decodes 3 characters from data starting from offset to get a short encoded as base-64. * @param data a char array that should be have length of at least offset + 3 * @param offset where in data to start reading from * @return the decoded short */ public static short b64DecodeShort(char[] data, int offset) { return (short) ((data == null || data.length < 3 + offset) ? 0 : ((valBase64[data[offset] & 0x7F]) << 12) | ((valBase64[data[offset + 1] & 0x7F]) << 6) | ( valBase64[data[offset + 2] & 0x7F])); } /** * Decodes 3 characters from data starting from offset to get a char encoded as base-64. * @param data a char array that should be have length of at least offset + 3 * @param offset where in data to start reading from * @return the decoded char */ public static char b64DecodeChar(char[] data, int offset) { return (char) ((data == null || data.length < 3 + offset) ? 0 : ((valBase64[data[offset] & 0x7F]) << 12) | ((valBase64[data[offset + 1] & 0x7F]) << 6) | ( valBase64[data[offset + 2] & 0x7F])); } /** * Decodes 2 characters from data starting from offset to get a byte encoded as base-64. * @param data a char array that should be have length of at least offset + 2 * @param offset where in data to start reading from * @return the decoded byte */ public static byte b64DecodeByte(char[] data, int offset) { return (byte) ((data == null || data.length < 2 + offset) ? 0 : ((valBase64[data[offset] & 0x7F]) << 6) | (valBase64[data[offset + 1] & 0x7F])); } public static String hexHash(boolean... array) { return hex(CrossHash.hash64(array)); } public static String hexHash(byte... array) { return hex(CrossHash.hash64(array)); } public static String hexHash(short... array) { return hex(CrossHash.hash64(array)); } public static String hexHash(char... array) { return hex(CrossHash.hash64(array)); } public static String hexHash(int... array) { return hex(CrossHash.hash64(array)); } public static String hexHash(long... array) { return hex(CrossHash.hash64(array)); } /** * If text is shorter than the given minimumLength, returns a String with text padded on the right with spaces until * it reaches that length; otherwise it simply returns text. * @param text the text to pad if necessary * @param minimumLength the minimum length of String to return * @return text, potentially padded with spaces to reach the given minimum length */ public static String padRight(String text, int minimumLength) { if(text.length() < minimumLength) return padRightStrict(text, ' ', minimumLength); return text; } /** * If text is shorter than the given minimumLength, returns a String with text padded on the right with padChar * until it reaches that length; otherwise it simply returns text. * @param text the text to pad if necessary * @param padChar the char to use to pad text, if necessary * @param minimumLength the minimum length of String to return * @return text, potentially padded with padChar to reach the given minimum length */ public static String padRight(String text, char padChar, int minimumLength) { if(text.length() < minimumLength) return padRightStrict(text, padChar, minimumLength); return text; } /** * Constructs a String with exactly the given totalLength by taking text (or a substring of it) and padding it on * its right side with spaces until totalLength is reached. If text is longer than totalLength, this only uses the * portion of text needed to fill totalLength, and no more. * @param text the String to pad if necessary, or truncate if too long * @param totalLength the exact length of String to return * @return a String with exactly totalLength for its length, made from text and possibly extra spaces */ public static String padRightStrict(String text, int totalLength) { return padRightStrict(text, ' ', totalLength); } /** * Constructs a String with exactly the given totalLength by taking text (or a substring of it) and padding it on * its right side with padChar until totalLength is reached. If text is longer than totalLength, this only uses the * portion of text needed to fill totalLength, and no more. * @param text the String to pad if necessary, or truncate if too long * @param padChar the char to use to fill any remaining length * @param totalLength the exact length of String to return * @return a String with exactly totalLength for its length, made from text and possibly padChar */ public static String padRightStrict(String text, char padChar, int totalLength) { char[] c = new char[totalLength]; int len = text.length(); text.getChars(0, Math.min(len, totalLength), c, 0); for (int i = len; i < totalLength; i++) { c[i] = padChar; } return String.valueOf(c); } /** * If text is shorter than the given minimumLength, returns a String with text padded on the left with spaces until * it reaches that length; otherwise it simply returns text. * @param text the text to pad if necessary * @param minimumLength the minimum length of String to return * @return text, potentially padded with spaces to reach the given minimum length */ public static String padLeft(String text, int minimumLength) { if(text.length() < minimumLength) return padLeftStrict(text, ' ', minimumLength); return text; } /** * If text is shorter than the given minimumLength, returns a String with text padded on the left with padChar until * it reaches that length; otherwise it simply returns text. * @param text the text to pad if necessary * @param padChar the char to use to pad text, if necessary * @param minimumLength the minimum length of String to return * @return text, potentially padded with padChar to reach the given minimum length */ public static String padLeft(String text, char padChar, int minimumLength) { if(text.length() < minimumLength) return padLeftStrict(text, padChar, minimumLength); return text; } /** * Constructs a String with exactly the given totalLength by taking text (or a substring of it) and padding it on * its left side with spaces until totalLength is reached. If text is longer than totalLength, this only uses the * portion of text needed to fill totalLength, and no more. * @param text the String to pad if necessary, or truncate if too long * @param totalLength the exact length of String to return * @return a String with exactly totalLength for its length, made from text and possibly extra spaces */ public static String padLeftStrict(String text, int totalLength) { return padLeftStrict(text, ' ', totalLength); } /** * Constructs a String with exactly the given totalLength by taking text (or a substring of it) and padding it on * its left side with padChar until totalLength is reached. If text is longer than totalLength, this only uses the * portion of text needed to fill totalLength, and no more. * @param text the String to pad if necessary, or truncate if too long * @param padChar the char to use to fill any remaining length * @param totalLength the exact length of String to return * @return a String with exactly totalLength for its length, made from text and possibly padChar */ public static String padLeftStrict(String text, char padChar, int totalLength) { char[] c = new char[totalLength]; int len = text.length(); text.getChars(0, Math.min(len, totalLength), c, Math.max(0, totalLength - len)); for (int i = totalLength - len - 1; i >= 0; i--) { c[i] = padChar; } return String.valueOf(c); } /** * Word-wraps the given String (or other CharSequence, such as a StringBuilder) so it is split into zero or more * Strings as lines of text, with the given width as the maximum width for a line. This correctly splits most (all?) * text in European languages on spaces (treating all whitespace characters matched by the regex '\\s' as breaking), * and also uses the English-language rule (probably used in other languages as well) of splitting on hyphens and * other dash characters (Unicode category Pd) in the middle of a word. This means for a phrase like "UN Secretary * General Ban-Ki Moon", if the width was 12, then the Strings in the List returned would be *
*
     * "UN Secretary"
     * "General Ban-"
     * "Ki Moon"
     * 
* Spaces are not preserved if they were used to split something into two lines, but dashes are. * @param longText a probably-large piece of text that needs to be split into multiple lines with a max width * @param width the max width to use for any line, removing trailing whitespace at the end of a line * @return a List of Strings for the lines after word-wrapping */ public static List wrap(CharSequence longText, int width) { if(width <= 0) return new ArrayList<>(0); return wrap(new ArrayList(longText.length() / width + 2), longText, width); } /** * Word-wraps the given String (or other CharSequence, such as a StringBuilder) so it is split into zero or more * Strings as lines of text, with the given width as the maximum width for a line; appends the word-wrapped lines to * the given List of Strings and does not create a new List. This correctly splits most (all?) text in European * languages on spaces (treating all whitespace characters matched by the regex '\\s' as breaking), and also uses * the English-language rule (probably used in other languages as well) of splitting on hyphens and other dash * characters (Unicode category Pd) in the middle of a word. This means for a phrase like "UN Secretary General * Ban-Ki Moon", if the width was 12, then the Strings in the List returned would be *
*
     * "UN Secretary"
     * "General Ban-"
     * "Ki Moon"
     * 
* Spaces are not preserved if they were used to split something into two lines, but dashes are. * @param receiving the List of String to append the word-wrapped lines to * @param longText a probably-large piece of text that needs to be split into multiple lines with a max width * @param width the max width to use for any line, removing trailing whitespace at the end of a line * @return the given {@code receiving} parameter, after appending the lines from word-wrapping */ public static List wrap(List receiving, CharSequence longText, int width) { if(width <= 0 || receiving == null) return receiving; Matcher widthMatcher = Pattern.compile("(?:({=Y}(?!\\s).{1," + width + "})((?<=\\p{Pd})|(\\s+)))|({=Y}\\S{1," + width + "})").matcher(longText + "\n"); while (widthMatcher.find()) { receiving.add(widthMatcher.group("Y")); } return receiving; } /** * Constant storing the 16 hexadecimal digits, as char values, in order. */ public static final char[] hexDigits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; /** * A constant containing only chars that are reasonably likely to be supported by broad fonts and thus display-able. * This assumes the font supports Latin, Greek, and Cyrillic alphabets, with good support for extended Latin (at * least for European languages) but not required to be complete enough to support the very large Vietnamese set of * extensions to Latin, nor to support any International Phonetic Alphabet (IPA) chars. It also assumes box drawing * characters are supported and a handful of common dingbats, such as male and female signs. It does not include * the tab, newline, or carriage return characters, since these don't usually make sense on a grid of chars. */ public static final String PERMISSIBLE_CHARS = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmno"+ "pqrstuvwxyz{|}~¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàá"+ "âãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬĭĮįİı"+ "ĴĵĶķĹĺĻļĽľĿŀŁłŃńŅņŇňŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƒǺǻǼǽǾǿ"+ "ȘșȚțȷˆˇˉˋ˘˙˚˛˜˝΄΅Ά·ΈΉΊΌΎΏΐΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩΪΫάέήίΰαβγδεζηθικλμνξοπρςστυ"+ "φχψωϊϋόύώЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхц"+ "чшщъыьэюяѐёђѓєѕіїјљњћќѝўџѴѵҐґẀẁẂẃẄẅỲỳ–—‘’‚‛“”„†‡•…‰‹›ⁿ₤€№™Ω℮←↑→↓∆−√≈" + "─│┌┐└┘├┤┬┴┼═║╒╓╔╕╖╗╘╙╚╛╜╝╞╟╠╡╢╣╤╥╦╧╨╩╪╫╬■□▲▼○●◦♀♂♠♣♥♦♪"; public static final String BOX_DRAWING_SINGLE = "─│┌┐└┘├┤┬┴┼"; public static final String BOX_DRAWING_DOUBLE = "═║╔╗╚╝╠╣╦╩╬"; public static final String BOX_DRAWING = "─│┌┐└┘├┤┬┴┼═║╒╓╔╕╖╗╘╙╚╛╜╝╞╟╠╡╢╣╤╥╦╧╨╩╪╫╬"; public static final String VISUAL_SYMBOLS = "←↑→↓■□▲▼○●◦♀♂♠♣♥♦♪"; public static final String DIGITS = "0123456789"; public static final String MARKS = "~`^'¨¯°´¸ˆˇˉˋ˘˙˚˛˜˝΄΅‘’‚‛"; /** * Can be used to match an index with one in {@link #GROUPING_SIGNS_CLOSE} to find the closing char (this way only). */ public static final String GROUPING_SIGNS_OPEN = "([{<«‘‛“‹"; /** * An index in {@link #GROUPING_SIGNS_OPEN} can be used here to find the closing char for that opening one. */ public static final String GROUPING_SIGNS_CLOSE = ")]}>»’’”›"; public static final String COMMON_PUNCTUATION = "!\"%&'*+,-./:;<>?•…–—"; public static final String MODERN_PUNCTUATION = "@\\^_`|~¦©®™´№♀♂♪"; public static final String UNCOMMON_PUNCTUATION = "§¶¨ªº¯°·¸¡¿·‚„†‡"; public static final String TECHNICAL_PUNCTUATION = "#%'*+,-./<=>^|¬°µ±¹²³ⁿ¼½¾×÷‰№Ω℮∆−√≈"; public static final String PUNCTUATION = COMMON_PUNCTUATION + MODERN_PUNCTUATION + UNCOMMON_PUNCTUATION + TECHNICAL_PUNCTUATION + GROUPING_SIGNS_OPEN + GROUPING_SIGNS_CLOSE; public static final String CURRENCY = "$¢£¤¥₤€"; public static final String SPACING = " "; public static final String ENGLISH_LETTERS_UPPER = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; public static final String ENGLISH_LETTERS_LOWER = "abcdefghijklmnopqrstuvwxyz"; public static final String ENGLISH_LETTERS = ENGLISH_LETTERS_UPPER + ENGLISH_LETTERS_LOWER; public static final String LATIN_EXTENDED_LETTERS_UPPER = "ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞĀĂĄĆĈĊČĎĐĒĔĖĘĚĜĞĠĢĤĦĨĪĬĮİĴĶĹĻĽĿŁŃŅŇŊŌŎŐŒŔŖŘŚŜŞŠŢŤŨŪŬŮŰŲŴŶŸŹŻŽǺǼǾȘȚẀẂẄỲßSFJ"; public static final String LATIN_EXTENDED_LETTERS_LOWER = "àáâãäåæçèéêëìíîïðñòóôõöøùúûüýþāăąćĉċčďđēĕėęěĝğġģĥħĩīĭįıĵķĺļľŀłńņňŋōŏőœŕŗřśŝşšţťũūŭůűųŵŷÿźżžǻǽǿșțẁẃẅỳßſƒȷ"; public static final String LATIN_EXTENDED_LETTERS = LATIN_EXTENDED_LETTERS_UPPER + LATIN_EXTENDED_LETTERS_LOWER; public static final String LATIN_LETTERS_UPPER = ENGLISH_LETTERS_UPPER + LATIN_EXTENDED_LETTERS_UPPER; public static final String LATIN_LETTERS_LOWER = ENGLISH_LETTERS_LOWER + LATIN_EXTENDED_LETTERS_LOWER; public static final String LATIN_LETTERS = LATIN_LETTERS_UPPER + LATIN_LETTERS_LOWER; /** * Includes the letter Sigma, 'Σ', twice because it has two lower-case forms in {@link #GREEK_LETTERS_LOWER}. This * lets you use one index for both lower and upper case, like with Latin and Cyrillic. */ public static final String GREEK_LETTERS_UPPER = "ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΣΤΥΦΧΨΩΆΈΉΊΌΎΏΪΫΪΫ"; /** * Includes both lower-case forms for Sigma, 'ς' and 'σ', but this matches the two upper-case Sigma in * {@link #GREEK_LETTERS_UPPER}. This lets you use one index for both lower and upper case, like with Latin and * Cyrillic. */ public static final String GREEK_LETTERS_LOWER = "αβγδεζηθικλμνξοπρςστυφχψωάέήίόύώϊϋΐΰ"; public static final String GREEK_LETTERS = GREEK_LETTERS_UPPER + GREEK_LETTERS_LOWER; public static final String CYRILLIC_LETTERS_UPPER = "АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏѴҐ"; public static final String CYRILLIC_LETTERS_LOWER = "абвгдежзийклмнопрстуфхцчшщъыьэюяѐёђѓєѕіїјљњћќѝўџѵґ"; public static final String CYRILLIC_LETTERS = CYRILLIC_LETTERS_UPPER + CYRILLIC_LETTERS_LOWER; public static final String LETTERS_UPPER = LATIN_LETTERS_UPPER + GREEK_LETTERS_UPPER + CYRILLIC_LETTERS_UPPER; public static final String LETTERS_LOWER = LATIN_LETTERS_LOWER + GREEK_LETTERS_LOWER + CYRILLIC_LETTERS_LOWER; public static final String LETTERS = LETTERS_UPPER + LETTERS_LOWER; public static final String LETTERS_AND_NUMBERS = LETTERS + DIGITS; public static String replace(CharSequence text, String before, String after) { if(text instanceof String) { return ((String)text).replace(before, after); } String t = text.toString(); return t.replace(before, after); } public static final Pattern whitespacePattern = Pattern.compile("\\s+"), nonSpacePattern = Pattern.compile("\\S+"); private static final Matcher matcher = new Matcher(whitespacePattern); public static int indexOf(CharSequence text, Pattern regex, int beginIndex) { matcher.setPattern(regex); matcher.setTarget(text); matcher.setPosition(beginIndex); if(!matcher.find()) return -1; return matcher.start(); } public static int indexOf(CharSequence text, String regex, int beginIndex) { matcher.setPattern(Pattern.compile(regex)); matcher.setTarget(text); matcher.setPosition(beginIndex); if(!matcher.find()) return -1; return matcher.start(); } public static int indexOf(CharSequence text, Pattern regex) { matcher.setPattern(regex); matcher.setTarget(text); if(!matcher.find()) return -1; return matcher.start(); } public static int indexOf(CharSequence text, String regex) { matcher.setPattern(Pattern.compile(regex)); matcher.setTarget(text); if(!matcher.find()) return -1; return matcher.start(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy