Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for sortable base64 encoding #12

Merged
merged 1 commit into from
Dec 25, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>com.baidu.hugegraph</groupId>
<artifactId>hugegraph-common</artifactId>
<version>1.5.5</version>
<version>1.5.6</version>

<name>hugegraph-common</name>
<url>https://github.com/hugegraph/hugegraph-common</url>
Expand Down Expand Up @@ -198,7 +198,7 @@
<manifestEntries>
<!-- Must be on one line, otherwise the automatic
upgrade script cannot replace the version number -->
<Implementation-Version>1.5.5.0</Implementation-Version>
<Implementation-Version>1.5.6.0</Implementation-Version>
</manifestEntries>
</archive>
</configuration>
Expand Down
76 changes: 68 additions & 8 deletions src/main/java/com/baidu/hugegraph/util/LongEncoding.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,77 @@
public final class LongEncoding {

private static final String BASE_SYMBOLS =
"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-~";
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~";

public static long decode(String s) {
return decode(s, BASE_SYMBOLS);
private static final String LENGTH_SYMBOLS = "0123456789ABCDEF";
private static final char NEG = LENGTH_SYMBOLS.charAt(0);

private static final long FULL_LONG = 0x8000000000000000L;

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, BASE_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(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) == NEG;
int prefix = 1;
if (negative) {
prefix = 2;
}
String encoded = str.substring(prefix);
long value = decode(encoded);
if (negative) {
value -= FULL_LONG;
}
return value;
}

public static String encode(long num) {
return encode(num, BASE_SYMBOLS);
}

public static long decode(String s, String symbols) {
public static long decode(String str) {
return decode(str, BASE_SYMBOLS);
}

public static long decode(String str, String symbols) {
final int B = symbols.length();
E.checkArgument(B > 0, "The symbols parameter can't be empty");
long num = 0;
for (char ch : s.toCharArray()) {
for (char ch : str.toCharArray()) {
num *= B;
int pos = symbols.indexOf(ch);
if (pos < 0)
Expand All @@ -49,13 +106,16 @@ public static long decode(String s, String symbols) {
}

public static String encode(long num, String symbols) {
E.checkArgument(num >= 0, "Expected non-negative number: %s", num);
final int B = symbols.length();
E.checkArgument(num >= 0, "Expected non-negative number: %s", num);
E.checkArgument(B > 0, "The symbols parameter can't be empty");

StringBuilder sb = new StringBuilder();
while (num != 0) {
do {
sb.append(symbols.charAt((int) (num % B)));
num /= B;
}
} while (num != 0);

return sb.reverse().toString();
}
}
66 changes: 59 additions & 7 deletions src/main/java/com/baidu/hugegraph/util/NumericUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,48 @@ public static int sortableFloatBits(int bits) {
return bits ^ (bits >> 31) & 0x7fffffff;
}

public static long numberToSortableLong(Number number) {
if (number instanceof Double) {
return doubleToSortableLong(number.doubleValue());
} else if (number instanceof Float) {
return floatToSortableInt(number.floatValue());
} else if (number instanceof Long || number instanceof Integer ||
number instanceof Short || number instanceof Byte) {
return number.longValue();
} else if (number instanceof BigDecimal) {
BigDecimal bd = (BigDecimal) number;
boolean intNumber = bd.stripTrailingZeros().scale() <= 0;
return intNumber ? bd.longValueExact() :
doubleToSortableLong(bd.doubleValue());
}
// TODO: support other number types
throw new IllegalArgumentException(String.format(
"Unsupported number type: %s(%s)",
number.getClass().getSimpleName(), number));
}

public static Number sortableLongToNumber(long value, Class<?> clazz) {
assert NumericUtil.isNumber(clazz);

if (clazz == Double.class) {
return sortableLongToDouble(value);
} else if (clazz == Float.class) {
return sortableIntToFloat((int) value);
} else if (clazz == Long.class) {
return value;
} else if (clazz == Integer.class) {
return (int) value;
} else if (clazz == Short.class) {
return (short) value;
} else if (clazz == Byte.class) {
return (byte) value;
}

// TODO: support other number types
throw new IllegalArgumentException(String.format(
"Unsupported number type: %s", clazz.getSimpleName()));
}

public static byte[] numberToSortableBytes(Number number) {
if (number instanceof Long) {
return longToBytes(number.longValue());
Expand All @@ -117,7 +159,9 @@ public static byte[] numberToSortableBytes(Number number) {
}

// TODO: support other number types
return null;
throw new IllegalArgumentException(String.format(
"Unsupported number type: %s(%s)",
number.getClass().getSimpleName(), number));
}

public static Number sortableBytesToNumber(byte[] bytes, Class<?> clazz) {
Expand All @@ -138,7 +182,8 @@ public static Number sortableBytesToNumber(byte[] bytes, Class<?> clazz) {
}

// TODO: support other number types
return null;
throw new IllegalArgumentException(String.format(
"Unsupported number type: %s", clazz.getSimpleName()));
}

public static byte[] longToBytes(long value) {
Expand Down Expand Up @@ -174,16 +219,23 @@ public static boolean isNumber(Class<?> clazz) {
return Number.class.isAssignableFrom(clazz);
}

public static Object convertToNumber(Object value) {
if (!isNumber(value) && value != null) {
public static Number convertToNumber(Object value) {
if (value == null) {
return null;
}

Number number;
if (isNumber(value)) {
number = (Number) value;
} else {
if (value instanceof Date) {
value = ((Date) value).getTime();
number = ((Date) value).getTime();
} else {
// TODO: add some more types to convert
value = new BigDecimal(value.toString());
number = new BigDecimal(value.toString());
}
}
return value;
return number;
}

/**
Expand Down
11 changes: 11 additions & 0 deletions src/main/java/com/baidu/hugegraph/util/VersionUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,17 @@ public static boolean match(Version version, String begin, String end) {
version.compareTo(new Version(end)) < 0;
}

/**
* Compare if a version is greater than the other one (inclusive)
* @param version The version to be compared
* @param begin The lower bound of the range
* @return true if it's greater than the other, otherwise false
*/
public static boolean gte(String version, String other) {
E.checkArgumentNotNull(version, "The version to match is null");
return new Version(version).compareTo(new Version(other)) >= 0;
}

/**
* Check whether a component version is matched expected range,
* throw an exception if it's not matched.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,5 @@ public class CommonVersion {

// The second parameter of Version.of() is for all-in-one JAR
public static final Version VERSION = Version.of(CommonVersion.class,
"1.5.5");
"1.5.6");
}
6 changes: 5 additions & 1 deletion src/test/java/com/baidu/hugegraph/unit/UnitTestSuite.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,13 @@
import com.baidu.hugegraph.unit.util.CollectionUtilTest;
import com.baidu.hugegraph.unit.util.HashUtilTest;
import com.baidu.hugegraph.unit.util.InsertionOrderUtilTest;
import com.baidu.hugegraph.unit.util.LongEncodingTest;
import com.baidu.hugegraph.unit.util.VersionUtilTest;
import com.baidu.hugegraph.unit.version.VersionTest;

@RunWith(Suite.class)
@Suite.SuiteClasses({
VersionTest.class,
EventHubTest.class,

ExtendableIteratorTest.class,
Expand All @@ -48,7 +51,8 @@
CollectionUtilTest.class,
HashUtilTest.class,
InsertionOrderUtilTest.class,
VersionUtilTest.class
VersionUtilTest.class,
LongEncodingTest.class
})
public class UnitTestSuite {
}
12 changes: 6 additions & 6 deletions src/test/java/com/baidu/hugegraph/unit/event/EventHubTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -461,24 +461,24 @@ public Object event(Event event) {

event.checkArgs(Integer.class);
int i = (int) event.args()[0];
if (i % 100000 == 0) {
if (i % 10000 == 0) {
System.out.println("On event '" + notify + "': " + i);
}
return null;
}
};

Thread listenerUpdateThread = new Thread(() -> {
// This will cost about 10s
for (int i = 0; i < 100; i++) {
// This will cost about 1s
for (int i = 0; i < 10; i++) {
this.eventHub.listen(notify, listener1);
if (!this.eventHub.listeners(notify).contains(listener2)) {
this.eventHub.listen(notify, listener2);
}

this.wait100ms();

if (i % 10 == 0) {
if (i % 2 == 0) {
this.eventHub.unlisten(notify);
} else {
this.eventHub.unlisten(notify, listener1);
Expand All @@ -488,8 +488,8 @@ public Object event(Event event) {
listenerUpdateThread.start();

runWithThreads(THREADS_NUM, () -> {
// This will cost about 10s ~ 20s
for (int i = 0; i < 10000 * 100; i++) {
// This will cost about 1s ~ 2s
for (int i = 0; i < 10000 * 10; i++) {
this.eventHub.notify(notify, i);
Thread.yield();
}
Expand Down
Loading