Skip to content

Commit f43636c

Browse files
committed
update
1 parent b416fc3 commit f43636c

File tree

14 files changed

+206
-37
lines changed

14 files changed

+206
-37
lines changed

src/main/java/io/github/honhimw/uuid/ClockSequence.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,11 @@ default Instant now() {
2929
return Instant.now();
3030
}
3131

32+
static Instant fastNow() {
33+
long millis = System.currentTimeMillis();
34+
long seconds = Maths.div1000(millis);
35+
int nanos = (int) (millis - (seconds * Maths.ONE_THOUSAND)) * 1_000_000;
36+
return Instant.ofEpochSecond(seconds, nanos);
37+
}
38+
3239
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package io.github.honhimw.uuid;
2+
3+
import java.math.BigDecimal;
4+
import java.math.BigInteger;
5+
import java.math.RoundingMode;
6+
7+
/// Prevent from using division. Make it as fast as possible.
8+
///
9+
/// @author honhimW
10+
/// @since 2025-12-30
11+
public class Maths {
12+
13+
public static final long ONE_THOUSAND = 1_000L;
14+
public static final long ONE_MILLION = 1_000_000L;
15+
/// 1 << 10 == 1024 greater than 1000, so we just take highest 10-bits in i64, 64 - 10 = 54
16+
public static final int NANOS_TO_MILLIS_SHIFT = 54;
17+
public static final long NANOS_TO_MILLIS_MULTIPLIER = ((1L) << NANOS_TO_MILLIS_SHIFT) / ONE_MILLION;
18+
19+
public static final long DIV_1000_MULTIPLIER = new BigDecimal(BigInteger.ONE.shiftLeft(64)).divide(BigDecimal.valueOf(125), RoundingMode.CEILING).longValueExact();
20+
21+
/// Optimize division by one million
22+
///
23+
/// @param nanos range 1_000_000 - 1_000_000_000
24+
/// @return millis seconds
25+
public static int ns2ms(int nanos) {
26+
return (int) (((nanos + 1) * NANOS_TO_MILLIS_MULTIPLIER) >>> NANOS_TO_MILLIS_SHIFT);
27+
}
28+
29+
/// Optimize remainder of one million
30+
///
31+
/// @param nanos range 1_000_000 - 1_000_000_000
32+
/// @return lower nano seconds
33+
public static int nsMod(int nanos) {
34+
int ms = ns2ms(nanos);
35+
return nanos - (int) (ms * ONE_MILLION);
36+
}
37+
38+
/// Optimize division by one thousand.
39+
///
40+
/// 1000 == 8 * 125
41+
/// 1. right shift 3(>>> 3) for division by 8
42+
/// 2. divide by 125 using the "magic number" method. For odd divisors, we can compute [#DIV_1000_MULTIPLIER]
43+
/// as the multiplier constant.
44+
/// 3. use [#multiplyHigh(long, long)] to compute the high 64 bits of the 128-bit product.
45+
/// This effectively performs floor(x / 125) for all 64‑bit inputs.
46+
///
47+
/// @param l long value
48+
/// @return result of division by 1000
49+
public static long div1000(long l) {
50+
l = l >>> 3; // division by 8
51+
return multiplyHigh(l, DIV_1000_MULTIPLIER);
52+
}
53+
54+
/// Convert the lowest 3 decimal digits of milliseconds into nanoseconds.
55+
///
56+
/// @param millis milli seconds
57+
/// @return nano seconds
58+
public static int ms2ns(long millis) {
59+
long l = div1000(millis);
60+
l = millis - (l * ONE_THOUSAND);
61+
return (int) (l * ONE_MILLION);
62+
}
63+
64+
/// Returns as a `long` the most significant 64 bits of the 128-bit product of two 64-bit factors.
65+
///
66+
/// Copied from JDK9+
67+
///
68+
/// @param x the first value
69+
/// @param y the second value
70+
/// @return the result
71+
public static long multiplyHigh(long x, long y) {
72+
// Use technique from section 8-2 of Henry S. Warren, Jr.,
73+
// Hacker's Delight (2nd ed.) (Addison Wesley, 2013), 173-174.
74+
long x1 = x >> 32;
75+
long x2 = x & 0xFFFFFFFFL;
76+
long y1 = y >> 32;
77+
long y2 = y & 0xFFFFFFFFL;
78+
79+
long z2 = x2 * y2;
80+
long t = x1 * y2 + (z2 >>> 32);
81+
long z1 = t & 0xFFFFFFFFL;
82+
long z0 = t >> 32;
83+
z1 += x2 * y1;
84+
85+
return x1 * y1 + z0 + (z1 >> 32);
86+
}
87+
88+
}

src/main/java/io/github/honhimw/uuid/Timestamp.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public Timestamp(long seconds, int nanos, long counter, int usableCounterBits) {
3333

3434
public long asEpochMillis() {
3535
long millis = seconds * 1_000L;
36-
return millis + (nanos / 1_000_000);
36+
return millis + Maths.ns2ms(nanos);
3737
}
3838

3939
public Instant asInstant() {

src/main/java/io/github/honhimw/uuid/UUIDs.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,7 @@ public class UUIDs {
5858
V7 v7 = new V7(Context.builder().clock(new RandomSequence(random) {
5959
@Override
6060
public Instant now() {
61-
long millis = System.currentTimeMillis();
62-
long seconds = millis / 1000;
63-
int nanos = (int) (millis % 1000) * 1_000_000;
64-
return Instant.ofEpochSecond(seconds, nanos);
61+
return ClockSequence.fastNow();
6562
}
6663
}).random(random).build());
6764
FAST = new Generators(v1, v3, v4, v5, v6, v7);

src/main/java/io/github/honhimw/uuid/Uuid.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,8 +119,8 @@ public Optional<Timestamp> timestamp() {
119119
millis |= (Byte.toUnsignedLong(bb.get(4))) << 8;
120120
millis |= Byte.toUnsignedLong(bb.get(5));
121121

122-
long seconds = millis / 1000;
123-
int nanos = (int) (millis % 1000) * 1_000_000;
122+
long seconds = Maths.div1000(millis);
123+
int nanos = (int) (millis - (seconds * Maths.ONE_THOUSAND)) * 1_000_000;
124124

125125
long counter = (Byte.toUnsignedLong(bb.get(6)) & 0xF) << 38;
126126
counter |= (Byte.toUnsignedLong(bb.get(7))) << 30;

src/main/java/io/github/honhimw/uuid/UuidBuilder.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,18 @@ private UuidBuilder() {
1414
this.bytes = new Bytes(new byte[16]);
1515
}
1616

17+
/// set variant field
18+
///
19+
/// @param variant uuid variant
20+
/// @return self
1721
public UuidBuilder variant(int variant) {
1822
return variant(Variant.of(variant));
1923
}
2024

25+
/// set variant field
26+
///
27+
/// @param variant uuid variant
28+
/// @return self
2129
public UuidBuilder variant(Variant variant) {
2230
byte b = this.bytes.get(8);
2331
switch (variant) {
@@ -40,10 +48,18 @@ public UuidBuilder variant(Variant variant) {
4048
return this;
4149
}
4250

51+
/// set version field
52+
///
53+
/// @param version uuid version
54+
/// @return self
4355
public UuidBuilder version(Version version) {
4456
return version(version.value());
4557
}
4658

59+
/// set version field
60+
///
61+
/// @param version uuid version
62+
/// @return self
4763
public UuidBuilder version(int version) {
4864
byte b = this.bytes.get(6);
4965
b &= 0x0F;
@@ -56,6 +72,9 @@ public UUID build() {
5672
return new UUID(this.bytes.getLong(0), this.bytes.getLong(Long.BYTES));
5773
}
5874

75+
/// create an empty uuid builder with [UUIDs#NIL] as default.
76+
///
77+
/// @return new empty builder
5978
public static UuidBuilder empty() {
6079
return new UuidBuilder();
6180
}

src/main/java/io/github/honhimw/uuid/gen/V1.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44

55
import java.util.UUID;
66

7-
/// [Version 1](https://www.rfc-editor.org/rfc/rfc9562.html#name-uuid-version-1)
7+
/// UUID [Version 1](https://www.rfc-editor.org/rfc/rfc9562.html#name-uuid-version-1) Generator, aka Time-Based UUID.
8+
///
89
/// ```text
910
/// 0 1 2 3
1011
/// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
@@ -17,7 +18,7 @@
1718
/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1819
/// | node |
1920
/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
20-
/// ```
21+
///```
2122
///
2223
/// - time_low: The least significant 32 bits of the 60-bit starting timestamp. Occupies bits 0 through 31 (octets 0-3).
2324
/// - time_mid: The middle 16 bits of the 60-bit starting timestamp. Occupies bits 32 through 47 (octets 4-5).

src/main/java/io/github/honhimw/uuid/gen/V3.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
import java.security.MessageDigest;
99
import java.util.UUID;
1010

11-
/// [Version 3](https://www.rfc-editor.org/rfc/rfc9562.html#name-uuid-version-3)
11+
/// UUID [Version 3](https://www.rfc-editor.org/rfc/rfc9562.html#name-uuid-version-3) Generator, aka Named-Based(MD5) UUID.
12+
///
1213
/// ```text
1314
/// 0 1 2 3
1415
/// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1

src/main/java/io/github/honhimw/uuid/gen/V4.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
import java.util.Random;
66
import java.util.UUID;
77

8-
/// [Version 4](https://www.rfc-editor.org/rfc/rfc9562.html#name-uuid-version-4)
8+
/// UUID [Version 4](https://www.rfc-editor.org/rfc/rfc9562.html#name-uuid-version-4) generator, aka Random-Based UUID.
9+
///
910
/// ```text
1011
/// 0 1 2 3
1112
/// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1

src/main/java/io/github/honhimw/uuid/gen/V5.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
import java.security.MessageDigest;
1010
import java.util.UUID;
1111

12-
/// [Version 5](https://www.rfc-editor.org/rfc/rfc9562.html#name-uuid-version-5)
12+
/// UUID [Version 5](https://www.rfc-editor.org/rfc/rfc9562.html#name-uuid-version-5) generator, aka Name-Based(SHA1) UUID.
13+
///
1314
/// ```text
1415
/// 0 1 2 3
1516
/// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1

0 commit comments

Comments
 (0)