View Javadoc
1   /*
2    * This file is part of Waarp Project (named also Waarp or GG).
3    *
4    *  Copyright (c) 2019, Waarp SAS, and individual contributors by the @author
5    *  tags. See the COPYRIGHT.txt in the distribution for a full listing of
6    * individual contributors.
7    *
8    *  All Waarp Project is free software: you can redistribute it and/or
9    * modify it under the terms of the GNU General Public License as published by
10   * the Free Software Foundation, either version 3 of the License, or (at your
11   * option) any later version.
12   *
13   * Waarp is distributed in the hope that it will be useful, but WITHOUT ANY
14   * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
15   * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16   *
17   *  You should have received a copy of the GNU General Public License along with
18   * Waarp . If not, see <http://www.gnu.org/licenses/>.
19   */
20  package org.waarp.common.guid;
21  
22  import org.waarp.common.utility.Hexa;
23  import org.waarp.common.utility.StringUtils;
24  
25  import java.util.Arrays;
26  import java.util.concurrent.atomic.AtomicInteger;
27  
28  /**
29   * UUID Generator (also Global UUID Generator) but limited to 1 Long (64 bits)
30   * <br>
31   * <br>
32   * Inspired from com.groupon locality-uuid which used combination of internal
33   * counter value - process id - and
34   * Timestamp. see https://github.com/groupon/locality-uuid.java <br>
35   * <br>
36   * But force sequence and take care of errors and improves some performance
37   * issues
38   */
39  public final class LongUuid {
40    /**
41     * Bits size of Counter
42     */
43    private static final int SIZE_COUNTER = 20;
44    /**
45     * Min Counter value
46     */
47    private static final int MIN_COUNTER = 0;
48    /**
49     * Max Counter value
50     */
51    private static final int MAX_COUNTER = (1 << SIZE_COUNTER) - 1;
52    /**
53     * Counter part
54     */
55    private static final AtomicInteger COUNTER =
56        new AtomicInteger(Math.abs(StringUtils.RANDOM.nextInt(SIZE_COUNTER)));
57    /**
58     * Byte size of UUID
59     */
60    private static final int UUIDSIZE = 8;
61  
62  
63    /**
64     * real UUID
65     */
66    private final byte[] uuid = { 0, 0, 0, 0, 0, 0, 0, 0 };
67  
68    static final synchronized int getCounter() {
69      if (COUNTER.compareAndSet(MAX_COUNTER, MIN_COUNTER)) {
70        return MAX_COUNTER;
71      } else {
72        return COUNTER.getAndIncrement();
73      }
74    }
75  
76    public static final long getLongUuid() {
77      final long time = System.currentTimeMillis();
78      // atomically
79      final int count = getCounter();
80      // Jvmd Id on 4 first bits
81      // Timestamp on 40 bits (2^40 ms = 35 years rolling)
82      // Count on 20 bits => 2^20 (1M / ms)
83      long uuidAsLong = (JvmProcessId.jvmId & 0xF0L) << 56;
84      uuidAsLong |= (time & 0xFFFFFFFFFFL) << 20;
85      uuidAsLong |= count & 0xFFFFFL;
86      return uuidAsLong;
87    }
88  
89    /**
90     * Constructor that generates a new UUID using the current process id and
91     * MAC address, timestamp and a counter
92     */
93    public LongUuid() {
94      this(getLongUuid());
95    }
96  
97    /**
98     * Constructor that takes a byte array as this UUID's content
99     *
100    * @param bytes UUID content
101    */
102   public LongUuid(final byte[] bytes) {
103     if (bytes.length != UUIDSIZE) {
104       throw new RuntimeException(
105           "Attempted to parse malformed UUID: " + Arrays.toString(bytes));
106     }
107     System.arraycopy(bytes, 0, uuid, 0, UUIDSIZE);
108   }
109 
110   public LongUuid(final long value) {
111     uuid[0] = (byte) (value >> 56);
112     uuid[1] = (byte) (value >> 48);
113     uuid[2] = (byte) (value >> 40);
114     uuid[3] = (byte) (value >> 32);
115     uuid[4] = (byte) (value >> 24);
116     uuid[5] = (byte) (value >> 16);
117     uuid[6] = (byte) (value >> 8);
118     uuid[7] = (byte) value;
119   }
120 
121   public LongUuid(final String idsource) {
122     final String id = idsource.trim();
123 
124     if (id.length() != UUIDSIZE * 2) {
125       throw new RuntimeException("Attempted to parse malformed UUID: " + id);
126     }
127     System.arraycopy(Hexa.fromHex(id), 0, uuid, 0, UUIDSIZE);
128   }
129 
130   @Override
131   public String toString() {
132     return Hexa.toHex(uuid);
133   }
134 
135   /**
136    * copy the uuid of this UUID, so that it can't be changed, and return it
137    *
138    * @return raw byte array of UUID
139    */
140   public final byte[] getBytes() {
141     return Arrays.copyOf(uuid, UUIDSIZE);
142   }
143 
144   /**
145    * extract process id from raw UUID bytes and return as int
146    *
147    * @return id of process that generated the UUID
148    */
149   public final int getProcessId() {
150     return (uuid[0] >> 4 & 0x0F);
151   }
152 
153   /**
154    * extract timestamp from raw UUID bytes and return as int
155    *
156    * @return millisecond UTC timestamp from generation of the UUID
157    */
158   public final long getTimestamp() {
159     long time;
160     time = ((long) uuid[0] & 0x0F) << 36;
161     time |= ((long) uuid[1] & 0xFF) << 28;
162     time |= ((long) uuid[2] & 0xFF) << 20;
163     time |= ((long) uuid[3] & 0xFF) << 12;
164     time |= ((long) uuid[4] & 0xFF) << 4;
165     time |= ((long) uuid[5] & 0xF0) >> 4;
166     return time;
167   }
168 
169   @Override
170   public final boolean equals(final Object o) {
171     if (!(o instanceof LongUuid)) {
172       return false;
173     }
174     return this == o || Arrays.equals(uuid, ((LongUuid) o).uuid);
175   }
176 
177   @Override
178   public final int hashCode() {
179     return Arrays.hashCode(uuid);
180   }
181 
182   /**
183    * @return the equivalent UUID as long
184    */
185   public final long getLong() {
186     long value = ((long) uuid[0] & 0xFF) << 56;
187     value |= ((long) uuid[1] & 0xFF) << 48;
188     value |= ((long) uuid[2] & 0xFF) << 40;
189     value |= ((long) uuid[3] & 0xFF) << 32;
190     value |= ((long) uuid[4] & 0xFF) << 24;
191     value |= ((long) uuid[5] & 0xFF) << 16;
192     value |= ((long) uuid[6] & 0xFF) << 8;
193     value |= (long) uuid[7] & 0xFF;
194     return value;
195   }
196 }