View Javadoc
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 }