Optimization of the ULID string validation
It's not needed to parse the whole ULID timestamp. All is needed is to check the first two bits. They are extra bits added by the base-32 encoding. Both bits must be ZERO.
This commit is contained in:
parent
1b47994150
commit
1bee3ba042
|
@ -33,7 +33,7 @@ Add these lines to your `pom.xml`.
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.github.f4b6a3</groupId>
|
<groupId>com.github.f4b6a3</groupId>
|
||||||
<artifactId>ulid-creator</artifactId>
|
<artifactId>ulid-creator</artifactId>
|
||||||
<version>2.3.1</version>
|
<version>2.3.2</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
```
|
```
|
||||||
See more options in [maven.org](https://search.maven.org/artifact/com.github.f4b6a3/ulid-creator).
|
See more options in [maven.org](https://search.maven.org/artifact/com.github.f4b6a3/ulid-creator).
|
||||||
|
|
|
@ -30,9 +30,6 @@ import static com.github.f4b6a3.ulid.util.internal.UlidStruct.BASE32_VALUES;
|
||||||
|
|
||||||
public final class UlidValidator {
|
public final class UlidValidator {
|
||||||
|
|
||||||
// Date: 10889-08-02T05:31:50.655Z: 281474976710655 (2^48-1)
|
|
||||||
private static final long TIMESTAMP_MAX = 0xffffffffffffL;
|
|
||||||
|
|
||||||
protected static final int ULID_LENGTH = 26;
|
protected static final int ULID_LENGTH = 26;
|
||||||
|
|
||||||
private UlidValidator() {
|
private UlidValidator() {
|
||||||
|
@ -58,11 +55,7 @@ public final class UlidValidator {
|
||||||
* @return boolean true if valid
|
* @return boolean true if valid
|
||||||
*/
|
*/
|
||||||
public static boolean isValid(String ulid) {
|
public static boolean isValid(String ulid) {
|
||||||
if (ulid == null) {
|
return (ulid != null && ulid.length() != 0 && isValidString(ulid.toCharArray()));
|
||||||
return false;
|
|
||||||
}
|
|
||||||
char[] chars = ulid.toCharArray();
|
|
||||||
return isValidString(chars) && isValidTimestamp(chars);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -74,13 +67,9 @@ public final class UlidValidator {
|
||||||
* @throws InvalidUlidException if invalid
|
* @throws InvalidUlidException if invalid
|
||||||
*/
|
*/
|
||||||
public static void validate(String ulid) {
|
public static void validate(String ulid) {
|
||||||
if(ulid != null) {
|
if (ulid == null || ulid.length() == 0 || !isValidString(ulid.toCharArray())) {
|
||||||
final char[] chars = ulid.toCharArray();
|
throw new InvalidUlidException(String.format("Invalid ULID: %s.", ulid));
|
||||||
if(isValidString(chars) && isValidTimestamp(chars)) {
|
|
||||||
return; // valid
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
throw new InvalidUlidException(String.format("Invalid ULID: %s.", ulid));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -101,6 +90,12 @@ public final class UlidValidator {
|
||||||
* @return boolean true if valid
|
* @return boolean true if valid
|
||||||
*/
|
*/
|
||||||
protected static boolean isValidString(final char[] c) {
|
protected static boolean isValidString(final char[] c) {
|
||||||
|
|
||||||
|
// the two extra bits added by base-32 encoding must be zero
|
||||||
|
if ((BASE32_VALUES[c[0]] & 0b11000) != 0) {
|
||||||
|
return false; // overflow
|
||||||
|
}
|
||||||
|
|
||||||
int hyphen = 0;
|
int hyphen = 0;
|
||||||
for (int i = 0; i < c.length; i++) {
|
for (int i = 0; i < c.length; i++) {
|
||||||
if (c[i] == '-') {
|
if (c[i] == '-') {
|
||||||
|
@ -117,28 +112,4 @@ public final class UlidValidator {
|
||||||
}
|
}
|
||||||
return (c.length - hyphen) == ULID_LENGTH;
|
return (c.length - hyphen) == ULID_LENGTH;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the timestamp is between 0 and 2^48-1
|
|
||||||
*
|
|
||||||
* @param chars a char array
|
|
||||||
* @return false if invalid.
|
|
||||||
*/
|
|
||||||
protected static boolean isValidTimestamp(char[] chars) {
|
|
||||||
|
|
||||||
long time = 0;
|
|
||||||
|
|
||||||
time |= BASE32_VALUES[chars[0x00]] << 45;
|
|
||||||
time |= BASE32_VALUES[chars[0x01]] << 40;
|
|
||||||
time |= BASE32_VALUES[chars[0x02]] << 35;
|
|
||||||
time |= BASE32_VALUES[chars[0x03]] << 30;
|
|
||||||
time |= BASE32_VALUES[chars[0x04]] << 25;
|
|
||||||
time |= BASE32_VALUES[chars[0x05]] << 20;
|
|
||||||
time |= BASE32_VALUES[chars[0x06]] << 15;
|
|
||||||
time |= BASE32_VALUES[chars[0x07]] << 10;
|
|
||||||
time |= BASE32_VALUES[chars[0x08]] << 5;
|
|
||||||
time |= BASE32_VALUES[chars[0x09]];
|
|
||||||
|
|
||||||
return time >= 0 && time <= TIMESTAMP_MAX;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,17 @@ public class UlidUtilTest {
|
||||||
assertEquals(TIMESTAMP_MAX, milliseconds);
|
assertEquals(TIMESTAMP_MAX, milliseconds);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ulid = "8ZZZZZZZZZ" + EXAMPLE_RANDOMNESS;
|
// Test the first extra bit added by the base32 encoding
|
||||||
|
ulid = "G0000000000000000000000000";
|
||||||
|
extractUnixMilliseconds(ulid);
|
||||||
|
fail("Should throw an InvalidUlidException");
|
||||||
|
} catch (InvalidUlidException e) {
|
||||||
|
// success
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Test the second extra bit added by the base32 encoding
|
||||||
|
ulid = "80000000000000000000000000";
|
||||||
extractUnixMilliseconds(ulid);
|
extractUnixMilliseconds(ulid);
|
||||||
fail("Should throw an InvalidUlidException");
|
fail("Should throw an InvalidUlidException");
|
||||||
} catch (InvalidUlidException e) {
|
} catch (InvalidUlidException e) {
|
||||||
|
@ -77,14 +87,6 @@ public class UlidUtilTest {
|
||||||
ulid = UlidConverter.fromString(string);
|
ulid = UlidConverter.fromString(string);
|
||||||
milliseconds = extractUnixMilliseconds(ulid);
|
milliseconds = extractUnixMilliseconds(ulid);
|
||||||
assertEquals(TIMESTAMP_MAX, milliseconds);
|
assertEquals(TIMESTAMP_MAX, milliseconds);
|
||||||
|
|
||||||
try {
|
|
||||||
string = "8ZZZZZZZZZ" + EXAMPLE_RANDOMNESS;
|
|
||||||
ulid = UlidConverter.fromString(string);
|
|
||||||
fail("Should throw an InvalidUlidException");
|
|
||||||
} catch (InvalidUlidException e) {
|
|
||||||
// success
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
Loading…
Reference in New Issue