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 Integer (32
30   * bits) <br>
31   * <br>
32   * Inspired from com.groupon locality-uuid which used combination of internal
33   * counter value - process id -
34   * and 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.<br>
38   * <br>
39   * Limit is about 4000000/s UUID
40   */
41  public final class IntegerUuid {
42    /**
43     * Counter part
44     */
45    private static final AtomicInteger COUNTER =
46        new AtomicInteger(StringUtils.RANDOM.nextInt());
47    /**
48     * Byte size of UUID
49     */
50    private static final int UUIDSIZE = 4;
51  
52    /**
53     * real UUID
54     */
55    private final byte[] uuid = { 0, 0, 0, 0 };
56  
57    static final synchronized int getCounter() {
58      if (COUNTER.compareAndSet(Integer.MAX_VALUE, Integer.MIN_VALUE)) {
59        return Integer.MAX_VALUE;
60      } else {
61        return COUNTER.getAndIncrement();
62      }
63    }
64  
65    /**
66     * Constructor that generates a new UUID using the current process id, MAC
67     * address, and timestamp
68     */
69    public IntegerUuid() {
70      // atomically
71      final int count = getCounter();
72      uuid[0] = (byte) (count >> 24);
73      uuid[1] = (byte) (count >> 16);
74      uuid[2] = (byte) (count >> 8);
75      uuid[3] = (byte) count;
76    }
77  
78    /**
79     * Constructor that takes a byte array as this UUID's content
80     *
81     * @param bytes UUID content
82     */
83    public IntegerUuid(final byte[] bytes) {
84      if (bytes.length != UUIDSIZE) {
85        throw new RuntimeException(
86            "Attempted to parse malformed UUID: " + Arrays.toString(bytes));
87      }
88      System.arraycopy(bytes, 0, uuid, 0, UUIDSIZE);
89    }
90  
91    public IntegerUuid(final int value) {
92      uuid[0] = (byte) (value >> 24);
93      uuid[1] = (byte) (value >> 16);
94      uuid[2] = (byte) (value >> 8);
95      uuid[3] = (byte) value;
96    }
97  
98    public IntegerUuid(final String idsource) {
99      final String id = idsource.trim();
100 
101     if (id.length() != UUIDSIZE * 2) {
102       throw new IllegalArgumentException(
103           "Attempted to parse malformed UUID: " + id);
104     }
105     System.arraycopy(Hexa.fromHex(id), 0, uuid, 0, UUIDSIZE);
106   }
107 
108   @Override
109   public String toString() {
110     return Hexa.toHex(uuid);
111   }
112 
113   /**
114    * copy the uuid of this UUID, so that it can't be changed, and return it
115    *
116    * @return raw byte array of UUID
117    */
118   public final byte[] getBytes() {
119     return Arrays.copyOf(uuid, UUIDSIZE);
120   }
121 
122   /**
123    * extract timestamp from raw UUID bytes and return as int
124    *
125    * @return millisecond UTC timestamp from generation of the UUID
126    */
127   public final long getTimestamp() {
128     long time;
129     time = ((long) uuid[0] & 0xFF) << 24;
130     time |= ((long) uuid[2] & 0xFF) << 16;
131     time |= ((long) uuid[2] & 0xFF) << 8;
132     time |= (long) uuid[3] & 0xFF;
133     return time;
134   }
135 
136   @Override
137   public final boolean equals(final Object o) {
138     if (!(o instanceof IntegerUuid)) {
139       return false;
140     }
141     return this == o || Arrays.equals(uuid, ((IntegerUuid) o).uuid);
142   }
143 
144   @Override
145   public final int hashCode() {
146     return Arrays.hashCode(uuid);
147   }
148 
149   /**
150    * @return the equivalent UUID as int
151    */
152   public final int getInt() {
153     int value = ((int) uuid[0] & 0xFF) << 24;
154     value |= ((long) uuid[1] & 0xFF) << 16;
155     value |= ((long) uuid[2] & 0xFF) << 8;
156     value |= (long) uuid[3] & 0xFF;
157     return value;
158   }
159 }