blob: 3488445f7efa71bbf19c03b73200e028aa5de14d [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with this
* work for additional information regarding copyright ownership. The ASF
* licenses this file to You under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package org.apache.hugegraph.util;
/**
* Utility class for encoding longs in strings based on:
* @see <a href="http://stackoverflow.com/questions/2938482/encode-decode-a-long-to-a-string-using-a-fixed-set-of-letters-in-java">encode decode long to string</a>
*/
public final class LongEncoding {
private static final String B64_SYMBOLS =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~";
private static final String LENGTH_SYMBOLS = "0123456789ABCDEF";
private static final char SORTABLE_NEG = LENGTH_SYMBOLS.charAt(0);
private static final char SIGNED_NEG = '-';
private static final long FULL_LONG = Long.MIN_VALUE;
public static String encodeNumber(Object number) {
Number num = NumericUtil.convertToNumber(number);
long value = NumericUtil.numberToSortableLong(num);
return encodeSortable(value);
}
public static Number decodeNumber(String str, Class<?> clazz) {
long value = decodeSortable(str);
Number number = NumericUtil.sortableLongToNumber(value, clazz);
return number;
}
public static String encodeSortable(long num) {
boolean negative = false;
if (num < 0L) {
negative = true;
num += FULL_LONG;
}
String encoded = encode(num, B64_SYMBOLS);
int length = encoded.length();
E.checkArgument(length <= LENGTH_SYMBOLS.length(),
"Length symbols can't represent encoded number '%s'",
encoded);
StringBuilder sb = new StringBuilder(length + 2);
if (negative) {
sb.append(SORTABLE_NEG);
}
char len = LENGTH_SYMBOLS.charAt(length);
sb.append(len);
sb.append(encoded);
return sb.toString();
}
public static long decodeSortable(String str) {
E.checkArgument(str.length() >= 2,
"Length of sortable encoded string must be >=2");
boolean negative = str.charAt(0) == SORTABLE_NEG;
int lengthPos = 0;
if (negative) {
lengthPos = 1;
}
int length = B64_SYMBOLS.indexOf(str.charAt(lengthPos));
E.checkArgument(length == str.length() - lengthPos - 1,
"Can't decode illegal string '%s' with wrong length",
str);
String encoded = str.substring(lengthPos + 1);
long value = decode(encoded, B64_SYMBOLS);
if (negative) {
value -= FULL_LONG;
}
return value;
}
public static String encodeSignedB64(long value) {
boolean negative = false;
if (value < 0L) {
negative = true;
if (value == FULL_LONG) {
return "-80000000000";
}
value = -value;
}
assert value >= 0L : value;
String encoded = encodeB64(value);
return negative ? SIGNED_NEG + encoded : encoded;
}
public static long decodeSignedB64(String value) {
boolean negative = false;
if (!value.isEmpty() && value.charAt(0) == SIGNED_NEG) {
negative = true;
value = value.substring(1);
}
long decoded = decodeB64(value);
return negative ? -decoded : decoded;
}
public static boolean validB64Char(char c) {
return B64_SYMBOLS.indexOf(c) != -1;
}
public static String encodeB64(long num) {
return encode(num, B64_SYMBOLS);
}
public static long decodeB64(String str) {
return decode(str, B64_SYMBOLS);
}
public static long decode(String encoded, String symbols) {
final int B = symbols.length();
E.checkArgument(B > 0, "The symbols parameter can't be empty");
long num = 0L;
for (char ch : encoded.toCharArray()) {
num *= B;
int pos = symbols.indexOf(ch);
if (pos < 0)
throw new NumberFormatException(String.format(
"Can't decode symbol '%s' in string '%s'",
ch, encoded));
num += pos;
}
return num;
}
public static String encode(long num, String symbols) {
final int B = symbols.length();
E.checkArgument(num >= 0L, "Expected non-negative number: %s", num);
E.checkArgument(B > 0, "The symbols parameter can't be empty");
StringBuilder sb = new StringBuilder();
do {
sb.append(symbols.charAt((int) (num % B)));
num /= B;
} while (num != 0L);
return sb.reverse().toString();
}
}