GnuCashew ~ GnuCash Enabled Web
GCW
guid.cpp
Go to the documentation of this file.
1 /*
2 The MIT License (MIT)
3 
4 Copyright (c) 2014 Graeme Hill (http://graemehill.ca)
5 
6 Permission is hereby granted, free of charge, to any person obtaining a copy
7 of this software and associated documentation files (the "Software"), to deal
8 in the Software without restriction, including without limitation the rights
9 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 copies of the Software, and to permit persons to whom the Software is
11 furnished to do so, subject to the following conditions:
12 
13 The above copyright notice and this permission notice shall be included in
14 all copies or substantial portions of the Software.
15 
16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 THE SOFTWARE.
23 */
24 
25 #include <cstring>
26 #include "guid.hpp"
27 
28 #ifdef GUID_LIBUUID
29 #include <uuid/uuid.h>
30 #endif
31 
32 #ifdef GUID_CFUUID
33 #include <CoreFoundation/CFUUID.h>
34 #endif
35 
36 #ifdef GUID_WINDOWS
37 #include <objbase.h>
38 #endif
39 
40 #ifdef GUID_ANDROID
41 #include <jni.h>
42 #include <cassert>
43 #endif
44 
45 BEGIN_XG_NAMESPACE
46 
47 #ifdef GUID_ANDROID
48 AndroidGuidInfo androidInfo;
49 
50 AndroidGuidInfo AndroidGuidInfo::fromJniEnv(JNIEnv *env)
51 {
52  AndroidGuidInfo info;
53  info.env = env;
54  auto localUuidClass = env->FindClass("java/util/UUID");
55  info.uuidClass = (jclass)env->NewGlobalRef(localUuidClass);
56  env->DeleteLocalRef(localUuidClass);
57  info.newGuidMethod = env->GetStaticMethodID(
58  info.uuidClass, "randomUUID", "()Ljava/util/UUID;");
59  info.mostSignificantBitsMethod = env->GetMethodID(
60  info.uuidClass, "getMostSignificantBits", "()J");
61  info.leastSignificantBitsMethod = env->GetMethodID(
62  info.uuidClass, "getLeastSignificantBits", "()J");
63  info.initThreadId = std::this_thread::get_id();
64  return info;
65 }
66 
67 void initJni(JNIEnv *env)
68 {
69  androidInfo = AndroidGuidInfo::fromJniEnv(env);
70 }
71 #endif
72 
73 // overload << so that it's easy to convert to a string
74 std::ostream &operator<<(std::ostream &s, const Guid &guid)
75 {
76  std::ios_base::fmtflags f(s.flags()); // politely don't leave the ostream in hex mode
77  s << std::hex << std::setfill('0')
78  << std::setw(2) << (int)guid._bytes[0]
79  << std::setw(2) << (int)guid._bytes[1]
80  << std::setw(2) << (int)guid._bytes[2]
81  << std::setw(2) << (int)guid._bytes[3]
82  << "-"
83  << std::setw(2) << (int)guid._bytes[4]
84  << std::setw(2) << (int)guid._bytes[5]
85  << "-"
86  << std::setw(2) << (int)guid._bytes[6]
87  << std::setw(2) << (int)guid._bytes[7]
88  << "-"
89  << std::setw(2) << (int)guid._bytes[8]
90  << std::setw(2) << (int)guid._bytes[9]
91  << "-"
92  << std::setw(2) << (int)guid._bytes[10]
93  << std::setw(2) << (int)guid._bytes[11]
94  << std::setw(2) << (int)guid._bytes[12]
95  << std::setw(2) << (int)guid._bytes[13]
96  << std::setw(2) << (int)guid._bytes[14]
97  << std::setw(2) << (int)guid._bytes[15];
98  s.flags(f);
99  return s;
100 }
101 
102 bool operator<(const xg::Guid &lhs, const xg::Guid &rhs)
103 {
104  return lhs.bytes() < rhs.bytes();
105 }
106 
107 bool Guid::isValid() const
108 {
109  xg::Guid empty;
110  return *this != empty;
111 }
112 
113 // convert to string using std::snprintf() and std::string
114 std::string Guid::str() const
115 {
116  char one[10], two[6], three[6], four[6], five[14];
117 
118  snprintf(one, 10, "%02x%02x%02x%02x",
119  _bytes[0], _bytes[1], _bytes[2], _bytes[3]);
120  snprintf(two, 6, "%02x%02x",
121  _bytes[4], _bytes[5]);
122  snprintf(three, 6, "%02x%02x",
123  _bytes[6], _bytes[7]);
124  snprintf(four, 6, "%02x%02x",
125  _bytes[8], _bytes[9]);
126  snprintf(five, 14, "%02x%02x%02x%02x%02x%02x",
127  _bytes[10], _bytes[11], _bytes[12], _bytes[13], _bytes[14], _bytes[15]);
128  const std::string sep("-");
129  std::string out(one);
130 
131  out += sep + two;
132  out += sep + three;
133  out += sep + four;
134  out += sep + five;
135 
136  return out;
137 }
138 
139 // conversion operator for std::string
140 Guid::operator std::string() const
141 {
142  return str();
143 }
144 
145 // Access underlying bytes
146 const std::array<unsigned char, 16>& Guid::bytes() const
147 {
148  return _bytes;
149 }
150 
151 // create a guid from vector of bytes
152 Guid::Guid(const std::array<unsigned char, 16> &bytes) : _bytes(bytes)
153 { }
154 
155 // create a guid from vector of bytes
156 Guid::Guid(std::array<unsigned char, 16> &&bytes) : _bytes(std::move(bytes))
157 { }
158 
159 // converts a single hex char to a number (0 - 15)
160 unsigned char hexDigitToChar(char ch)
161 {
162  // 0-9
163  if (ch > 47 && ch < 58)
164  return ch - 48;
165 
166  // a-f
167  if (ch > 96 && ch < 103)
168  return ch - 87;
169 
170  // A-F
171  if (ch > 64 && ch < 71)
172  return ch - 55;
173 
174  return 0;
175 }
176 
177 bool isValidHexChar(char ch)
178 {
179  // 0-9
180  if (ch > 47 && ch < 58)
181  return true;
182 
183  // a-f
184  if (ch > 96 && ch < 103)
185  return true;
186 
187  // A-F
188  if (ch > 64 && ch < 71)
189  return true;
190 
191  return false;
192 }
193 
194 // converts the two hexadecimal characters to an unsigned char (a byte)
195 unsigned char hexPairToChar(char a, char b)
196 {
197  return hexDigitToChar(a) * 16 + hexDigitToChar(b);
198 }
199 
200 // create a guid from string
201 Guid::Guid(std::string_view fromString)
202 {
203  char charOne = '\0';
204  char charTwo = '\0';
205  bool lookingForFirstChar = true;
206  unsigned nextByte = 0;
207 
208  for (const char &ch : fromString)
209  {
210  if (ch == '-')
211  continue;
212 
213  if (nextByte >= 16 || !isValidHexChar(ch))
214  {
215  // Invalid string so bail
216  zeroify();
217  return;
218  }
219 
220  if (lookingForFirstChar)
221  {
222  charOne = ch;
223  lookingForFirstChar = false;
224  }
225  else
226  {
227  charTwo = ch;
228  auto byte = hexPairToChar(charOne, charTwo);
229  _bytes[nextByte++] = byte;
230  lookingForFirstChar = true;
231  }
232  }
233 
234  // if there were fewer than 16 bytes in the string then guid is bad
235  if (nextByte < 16)
236  {
237  zeroify();
238  return;
239  }
240 }
241 
242 // create empty guid
243 Guid::Guid() : _bytes{ {0} }
244 { }
245 
246 // set all bytes to zero
247 void Guid::zeroify()
248 {
249  std::fill(_bytes.begin(), _bytes.end(), static_cast<unsigned char>(0));
250 }
251 
252 // overload equality operator
253 bool Guid::operator==(const Guid &other) const
254 {
255  return _bytes == other._bytes;
256 }
257 
258 // overload inequality operator
259 bool Guid::operator!=(const Guid &other) const
260 {
261  return !((*this) == other);
262 }
263 
264 // member swap function
265 void Guid::swap(Guid &other)
266 {
267  _bytes.swap(other._bytes);
268 }
269 
270 // This is the linux friendly implementation, but it could work on other
271 // systems that have libuuid available
272 #ifdef GUID_LIBUUID
273 Guid newGuid()
274 {
275  std::array<unsigned char, 16> data;
276  static_assert(std::is_same<unsigned char[16], uuid_t>::value, "Wrong type!");
277  uuid_generate(data.data());
278  return Guid{std::move(data)};
279 }
280 #endif
281 
282 // this is the mac and ios version
283 #ifdef GUID_CFUUID
284 Guid newGuid()
285 {
286  auto newId = CFUUIDCreate(NULL);
287  auto bytes = CFUUIDGetUUIDBytes(newId);
288  CFRelease(newId);
289 
290  std::array<unsigned char, 16> byteArray =
291  {{
292  bytes.byte0,
293  bytes.byte1,
294  bytes.byte2,
295  bytes.byte3,
296  bytes.byte4,
297  bytes.byte5,
298  bytes.byte6,
299  bytes.byte7,
300  bytes.byte8,
301  bytes.byte9,
302  bytes.byte10,
303  bytes.byte11,
304  bytes.byte12,
305  bytes.byte13,
306  bytes.byte14,
307  bytes.byte15
308  }};
309  return Guid{std::move(byteArray)};
310 }
311 #endif
312 
313 // obviously this is the windows version
314 #ifdef GUID_WINDOWS
315 Guid newGuid()
316 {
317  GUID newId;
318  CoCreateGuid(&newId);
319 
320  std::array<unsigned char, 16> bytes =
321  {
322  (unsigned char)((newId.Data1 >> 24) & 0xFF),
323  (unsigned char)((newId.Data1 >> 16) & 0xFF),
324  (unsigned char)((newId.Data1 >> 8) & 0xFF),
325  (unsigned char)((newId.Data1) & 0xff),
326 
327  (unsigned char)((newId.Data2 >> 8) & 0xFF),
328  (unsigned char)((newId.Data2) & 0xff),
329 
330  (unsigned char)((newId.Data3 >> 8) & 0xFF),
331  (unsigned char)((newId.Data3) & 0xFF),
332 
333  (unsigned char)newId.Data4[0],
334  (unsigned char)newId.Data4[1],
335  (unsigned char)newId.Data4[2],
336  (unsigned char)newId.Data4[3],
337  (unsigned char)newId.Data4[4],
338  (unsigned char)newId.Data4[5],
339  (unsigned char)newId.Data4[6],
340  (unsigned char)newId.Data4[7]
341  };
342 
343  return Guid{std::move(bytes)};
344 }
345 #endif
346 
347 // android version that uses a call to a java api
348 #ifdef GUID_ANDROID
349 Guid newGuid(JNIEnv *env)
350 {
351  assert(env != androidInfo.env || std::this_thread::get_id() == androidInfo.initThreadId);
352 
353  jobject javaUuid = env->CallStaticObjectMethod(
354  androidInfo.uuidClass, androidInfo.newGuidMethod);
355  jlong mostSignificant = env->CallLongMethod(javaUuid,
356  androidInfo.mostSignificantBitsMethod);
357  jlong leastSignificant = env->CallLongMethod(javaUuid,
358  androidInfo.leastSignificantBitsMethod);
359 
360  std::array<unsigned char, 16> bytes =
361  {
362  (unsigned char)((mostSignificant >> 56) & 0xFF),
363  (unsigned char)((mostSignificant >> 48) & 0xFF),
364  (unsigned char)((mostSignificant >> 40) & 0xFF),
365  (unsigned char)((mostSignificant >> 32) & 0xFF),
366  (unsigned char)((mostSignificant >> 24) & 0xFF),
367  (unsigned char)((mostSignificant >> 16) & 0xFF),
368  (unsigned char)((mostSignificant >> 8) & 0xFF),
369  (unsigned char)((mostSignificant) & 0xFF),
370  (unsigned char)((leastSignificant >> 56) & 0xFF),
371  (unsigned char)((leastSignificant >> 48) & 0xFF),
372  (unsigned char)((leastSignificant >> 40) & 0xFF),
373  (unsigned char)((leastSignificant >> 32) & 0xFF),
374  (unsigned char)((leastSignificant >> 24) & 0xFF),
375  (unsigned char)((leastSignificant >> 16) & 0xFF),
376  (unsigned char)((leastSignificant >> 8) & 0xFF),
377  (unsigned char)((leastSignificant) & 0xFF)
378  };
379 
380  env->DeleteLocalRef(javaUuid);
381 
382  return Guid{std::move(bytes)};
383 }
384 
385 Guid newGuid()
386 {
387  return newGuid(androidInfo.env);
388 }
389 #endif
390 
391 
392 END_XG_NAMESPACE
393 
394 // Specialization for std::swap<Guid>() --
395 // call member swap function of lhs, passing rhs
396 namespace std
397 {
398  template <>
399  void swap(xg::Guid &lhs, xg::Guid &rhs) noexcept
400  {
401  lhs.swap(rhs);
402  }
403 }
BEGIN_XG_NAMESPACE std::ostream & operator<<(std::ostream &s, const Guid &guid)
Definition: guid.cpp:74
bool operator<(const xg::Guid &lhs, const xg::Guid &rhs)
Definition: guid.cpp:102
bool isValidHexChar(char ch)
Definition: guid.cpp:177
unsigned char hexPairToChar(char a, char b)
Definition: guid.cpp:195
unsigned char hexDigitToChar(char ch)
Definition: guid.cpp:160
auto newGuid() -> std::string
Generate new GUID string value.
Definition: Core.cpp:235
const Wt::WFormModel::Field guid
Definition: Accounts.cpp:46
Definition: guid.cpp:397
void swap(xg::Guid &lhs, xg::Guid &rhs) noexcept
Definition: guid.cpp:399