1 package com.buralotech.oss.identifier.uuid;
2
3 import com.fasterxml.uuid.Generators;
4 import com.fasterxml.uuid.NoArgGenerator;
5 import com.fasterxml.uuid.impl.UUIDUtil;
6
7 import java.nio.ByteBuffer;
8 import java.nio.ByteOrder;
9 import java.time.Instant;
10 import java.util.regex.Pattern;
11
12 /**
13 * Encapsulates the logic that is specific to the modified type 1 UUID format.
14 *
15 * @deprecated Use {@link }UUIDVersion6Delegate} for new developments.
16 */
17 @Deprecated(since = "1.3.0")
18 public class UUIDVersion1Delegate implements UUIDVersionDelegate {
19
20 /**
21 * The regular expression pattern used to validate identifiers.
22 */
23 private static final Pattern PATTERN = Pattern.compile("[3456][0-9a-zA-Z_-]{9}[159DHLPTXaeimquy][0-9a-zA-Z_-]{10}[FVk-]");
24
25 /**
26 * Mask to remove the UUID version.
27 */
28 private static final long MASK = 0x0fffffffffffffffL;
29
30 /**
31 * Number of ticks per second. TheUUID thick is 100 nanoseconds.
32 */
33 private static final long TICKS_PER_SECOND = 10000000L;
34
35 /**
36 * The number of nanoseconds in a UUID tick.
37 */
38 private static final int TICK_NANOS = 100;
39
40 /**
41 * The adjustment to apply to convert UUID epoch to Unix Epoch. The UUID epoch 15 October 1582, while the Unix
42 * epoch is 1 January 1970.
43 */
44 private static final long EPOCH_ADJ = 122192928000000000L;
45
46 /**
47 * Used to generate time based UUIDs.
48 */
49 private final NoArgGenerator generator = Generators.timeBasedGenerator();
50
51 /**
52 * Generate an identifier using an underlying UUID generator.
53 *
54 * @return The generated identifier as a byte array.
55 */
56 @Override
57 public byte[] generate() {
58 final var uuid = generator.generate();
59 final var binary = UUIDUtil.asByteArray(uuid);
60 swap(binary, 4);
61 swap(binary, 2);
62 return binary;
63 }
64
65 /**
66 * Swap the first {@code len} bytes of the array with the subsequent {@code len} bytes.
67 *
68 * @param bytes The byte array.
69 * @param len The number of bytes to swap.
70 */
71 private void swap(final byte[] bytes,
72 final int len) {
73 for (int i = 0; i < len; i++) {
74 bytes[i] ^= bytes[len + i];
75 bytes[len + i] ^= bytes[i];
76 bytes[i] ^= bytes[len + i];
77 }
78 }
79
80 /**
81 * Check that the binary representation is valid. The service has already checked that it is non-null and a valid length.
82 *
83 * @param binary The binary representation.
84 * @return {@code true} if the binary representation is valid. Otherwise, {@code false}.
85 */
86 @Override
87 public boolean isValidBinary(final byte[] binary) {
88 return ((binary[0] & 0xf0) >> 4 == 1) && ((binary[8] & 0xc0) >> 6 == 2);
89 }
90
91 /**
92 * Check that the text representation is valid. The service has already checked that it is non-null and a valid length.
93 *
94 * @param text The binary representation.
95 * @return {@code true} if the text representation is valid. Otherwise, {@code false}.
96 */
97 @Override
98 public boolean isValidText(final String text) {
99 return PATTERN.matcher(text).matches();
100 }
101
102 /**
103 * Extract the timestamp from the UUID.
104 *
105 * @param binary The binary representation of the UUID.
106 * @return The timestamp as an Instant.
107 */
108 @Override
109 public Instant toInstant(final byte[] binary) {
110 final var buf = ByteBuffer.wrap(binary);
111 buf.order(ByteOrder.BIG_ENDIAN);
112 final var ticks = (buf.getLong(0) & MASK) - EPOCH_ADJ;
113 return Instant.ofEpochSecond(ticks / TICKS_PER_SECOND, (ticks % TICKS_PER_SECOND) * TICK_NANOS);
114 }
115
116 /**
117 * Create a UUID as a byte array from a timestamp.
118 *
119 * @param ticks The timestamp in 100 nanoseconds.
120 * @param suffix The second portion of the UUID.
121 * @return The UUID as a byte array.
122 */
123 @Override
124 public byte[] fromTicks(final long ticks, final long suffix) {
125 final var bytes = new byte[16];
126 final var buffer = ByteBuffer.wrap(bytes);
127 buffer.order(ByteOrder.BIG_ENDIAN);
128 buffer.putLong(ticks | 0x1000000000000000L);
129 buffer.putLong(suffix);
130 return bytes;
131 }
132 }