1 | /* |
2 | * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0, |
3 | * and the EPL 1.0 (http://h2database.com/html/license.html). |
4 | * Initial Developer: H2 Group |
5 | */ |
6 | package org.h2.value; |
7 | |
8 | import java.sql.PreparedStatement; |
9 | import java.sql.SQLException; |
10 | import java.util.UUID; |
11 | |
12 | import org.h2.api.ErrorCode; |
13 | import org.h2.message.DbException; |
14 | import org.h2.util.Utils; |
15 | import org.h2.util.MathUtils; |
16 | import org.h2.util.StringUtils; |
17 | |
18 | /** |
19 | * Implementation of the UUID data type. |
20 | */ |
21 | public class ValueUuid extends Value { |
22 | |
23 | /** |
24 | * The precision of this value in number of bytes. |
25 | */ |
26 | private static final int PRECISION = 16; |
27 | |
28 | /** |
29 | * The display size of the textual representation of a UUID. |
30 | * Example: cd38d882-7ada-4589-b5fb-7da0ca559d9a |
31 | */ |
32 | private static final int DISPLAY_SIZE = 36; |
33 | |
34 | private final long high, low; |
35 | |
36 | private ValueUuid(long high, long low) { |
37 | this.high = high; |
38 | this.low = low; |
39 | } |
40 | |
41 | @Override |
42 | public int hashCode() { |
43 | return (int) ((high >>> 32) ^ high ^ (low >>> 32) ^ low); |
44 | } |
45 | |
46 | /** |
47 | * Create a new UUID using the pseudo random number generator. |
48 | * |
49 | * @return the new UUID |
50 | */ |
51 | public static ValueUuid getNewRandom() { |
52 | long high = MathUtils.secureRandomLong(); |
53 | long low = MathUtils.secureRandomLong(); |
54 | // version 4 (random) |
55 | high = (high & (~0xf000L)) | 0x4000L; |
56 | // variant (Leach-Salz) |
57 | low = (low & 0x3fffffffffffffffL) | 0x8000000000000000L; |
58 | return new ValueUuid(high, low); |
59 | } |
60 | |
61 | /** |
62 | * Get or create a UUID for the given 16 bytes. |
63 | * |
64 | * @param binary the byte array (must be at least 16 bytes long) |
65 | * @return the UUID |
66 | */ |
67 | public static ValueUuid get(byte[] binary) { |
68 | if (binary.length < 16) { |
69 | return get(StringUtils.convertBytesToHex(binary)); |
70 | } |
71 | long high = Utils.readLong(binary, 0); |
72 | long low = Utils.readLong(binary, 8); |
73 | return (ValueUuid) Value.cache(new ValueUuid(high, low)); |
74 | } |
75 | |
76 | /** |
77 | * Get or create a UUID for the given high and low order values. |
78 | * |
79 | * @param high the most significant bits |
80 | * @param low the least significant bits |
81 | * @return the UUID |
82 | */ |
83 | public static ValueUuid get(long high, long low) { |
84 | return (ValueUuid) Value.cache(new ValueUuid(high, low)); |
85 | } |
86 | |
87 | /** |
88 | * Get or create a UUID for the given text representation. |
89 | * |
90 | * @param s the text representation of the UUID |
91 | * @return the UUID |
92 | */ |
93 | public static ValueUuid get(String s) { |
94 | long low = 0, high = 0; |
95 | for (int i = 0, j = 0, length = s.length(); i < length; i++) { |
96 | char c = s.charAt(i); |
97 | if (c >= '0' && c <= '9') { |
98 | low = (low << 4) | (c - '0'); |
99 | } else if (c >= 'a' && c <= 'f') { |
100 | low = (low << 4) | (c - 'a' + 0xa); |
101 | } else if (c == '-') { |
102 | continue; |
103 | } else if (c >= 'A' && c <= 'F') { |
104 | low = (low << 4) | (c - 'A' + 0xa); |
105 | } else if (c <= ' ') { |
106 | continue; |
107 | } else { |
108 | throw DbException.get(ErrorCode.DATA_CONVERSION_ERROR_1, s); |
109 | } |
110 | if (j++ == 15) { |
111 | high = low; |
112 | low = 0; |
113 | } |
114 | } |
115 | return (ValueUuid) Value.cache(new ValueUuid(high, low)); |
116 | } |
117 | |
118 | @Override |
119 | public String getSQL() { |
120 | return StringUtils.quoteStringSQL(getString()); |
121 | } |
122 | |
123 | @Override |
124 | public int getType() { |
125 | return Value.UUID; |
126 | } |
127 | |
128 | @Override |
129 | public long getPrecision() { |
130 | return PRECISION; |
131 | } |
132 | |
133 | private static void appendHex(StringBuilder buff, long x, int bytes) { |
134 | for (int i = bytes * 8 - 4; i >= 0; i -= 8) { |
135 | buff.append(Integer.toHexString((int) (x >> i) & 0xf)). |
136 | append(Integer.toHexString((int) (x >> (i - 4)) & 0xf)); |
137 | } |
138 | } |
139 | |
140 | @Override |
141 | public String getString() { |
142 | StringBuilder buff = new StringBuilder(36); |
143 | appendHex(buff, high >> 32, 4); |
144 | buff.append('-'); |
145 | appendHex(buff, high >> 16, 2); |
146 | buff.append('-'); |
147 | appendHex(buff, high, 2); |
148 | buff.append('-'); |
149 | appendHex(buff, low >> 48, 2); |
150 | buff.append('-'); |
151 | appendHex(buff, low, 6); |
152 | return buff.toString(); |
153 | } |
154 | |
155 | @Override |
156 | protected int compareSecure(Value o, CompareMode mode) { |
157 | if (o == this) { |
158 | return 0; |
159 | } |
160 | ValueUuid v = (ValueUuid) o; |
161 | if (high == v.high) { |
162 | return MathUtils.compareLong(low, v.low); |
163 | } |
164 | return high > v.high ? 1 : -1; |
165 | } |
166 | |
167 | @Override |
168 | public boolean equals(Object other) { |
169 | return other instanceof ValueUuid && compareSecure((Value) other, null) == 0; |
170 | } |
171 | |
172 | @Override |
173 | public Object getObject() { |
174 | return new UUID(high, low); |
175 | } |
176 | |
177 | @Override |
178 | public byte[] getBytes() { |
179 | byte[] buff = new byte[16]; |
180 | for (int i = 0; i < 8; i++) { |
181 | buff[i] = (byte) ((high >> (8 * (7 - i))) & 255); |
182 | buff[8 + i] = (byte) ((low >> (8 * (7 - i))) & 255); |
183 | } |
184 | return buff; |
185 | } |
186 | |
187 | @Override |
188 | public void set(PreparedStatement prep, int parameterIndex) |
189 | throws SQLException { |
190 | prep.setBytes(parameterIndex, getBytes()); |
191 | } |
192 | |
193 | /** |
194 | * Get the most significant 64 bits of this UUID. |
195 | * |
196 | * @return the high order bits |
197 | */ |
198 | public long getHigh() { |
199 | return high; |
200 | } |
201 | |
202 | /** |
203 | * Get the least significant 64 bits of this UUID. |
204 | * |
205 | * @return the low order bits |
206 | */ |
207 | public long getLow() { |
208 | return low; |
209 | } |
210 | |
211 | @Override |
212 | public int getDisplaySize() { |
213 | return DISPLAY_SIZE; |
214 | } |
215 | |
216 | } |