GnuCash  5.6-150-g038405b370+
guid.cpp
1 /********************************************************************\
2  * guid.c -- globally unique ID implementation *
3  * Copyright (C) 2000 Dave Peticolas <peticola@cs.ucdavis.edu> *
4  * Copyright (C) 2014 Aaron Laws <dartmetrash@gmail.com> *
5  * *
6  * This program is free software; you can redistribute it and/or *
7  * modify it under the terms of the GNU General Public License as *
8  * published by the Free Software Foundation; either version 2 of *
9  * the License, or (at your option) any later version. *
10  * *
11  * This program is distributed in the hope that it will be useful, *
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14  * GNU General Public License for more details. *
15  * *
16  * You should have received a copy of the GNU General Public License*
17  * along with this program; if not, contact: *
18  * *
19  * Free Software Foundation Voice: +1-617-542-5942 *
20  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
21  * Boston, MA 02110-1301, USA gnu@gnu.org *
22  * *
23 \********************************************************************/
24 
25 #include "guid.hpp"
26 #include "guid.h"
27 
28 #ifdef HAVE_CONFIG_H
29 # include <config.h>
30 #endif
31 
32 #ifdef HAVE_SYS_TYPES_H
33 # include <sys/types.h>
34 #endif
35 #include <ctype.h>
36 #include <stdint.h>
37 #ifdef HAVE_DIRENT_H
38 # include <dirent.h>
39 #endif
40 #include <glib.h>
41 #include <glib/gstdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <sys/stat.h>
45 #ifdef HAVE_SYS_TIMES_H
46 # include <sys/times.h>
47 #endif
48 #include <time.h>
49 #ifdef HAVE_UNISTD_H
50 # include <unistd.h>
51 #endif
52 #include "qof.h"
53 
54 #include <boost/uuid/uuid.hpp>
55 #include <boost/uuid/uuid_generators.hpp>
56 #include <boost/uuid/uuid_io.hpp>
57 #include <sstream>
58 #include <string>
59 #include <algorithm>
60 
61 /* This static indicates the debugging module that this .o belongs to. */
62 static QofLogModule log_module = QOF_MOD_ENGINE;
63 
71 const GncGUID*
72 gnc_value_get_guid (const GValue *value)
73 {
74  if (!value) return nullptr;
75  GncGUID *val;
76 
77  g_return_val_if_fail (value && G_IS_VALUE (value), nullptr);
78  g_return_val_if_fail (GNC_VALUE_HOLDS_GUID (value), nullptr);
79 
80  val = (GncGUID*) g_value_get_boxed (value);
81 
82  return val;
83 }
84 
85 GncGUID * guid_convert_create (gnc::GUID const &);
86 
87 static gnc::GUID s_null_guid {boost::uuids::uuid { {0}}};
88 static GncGUID * s_null_gncguid {guid_convert_create (s_null_guid)};
89 
90 static inline int
91 char_to_num (unsigned char c) noexcept
92 {
93  unsigned int digit = c - '0';
94  unsigned int alpha = (c | 0x20) - 'a';
95  return digit <= 9 ? digit : alpha <= 5 ? alpha + 10 : -1;
96 }
97 
98 static inline bool
99 fast_string_to_guid (const char* s, uint8_t* out) noexcept
100 {
101  if (strnlen (s, GUID_ENCODING_LENGTH + 1) != GUID_ENCODING_LENGTH) return false;
102  bool all_ok = true;
103  for (int i = 0; i < GUID_DATA_SIZE; i++)
104  {
105  int hi = char_to_num (*s++);
106  int lo = char_to_num (*s++);
107  all_ok &= (hi >= 0 && lo >= 0);
108  out[i] = (unsigned char)(((unsigned)hi << 4) | (unsigned)lo);
109  }
110  return all_ok;
111 }
112 
113 static inline void
114 fast_guid_to_string (const uint8_t* src, char* dest) noexcept
115 {
116  static constexpr char hex[] = "0123456789abcdef";
117  for (size_t i = 0; i < 16; i++)
118  {
119  uint8_t b = src[i];
120  *dest++ = hex[b >> 4];
121  *dest++ = hex[b & 0x0F];
122  }
123 }
124 
125 /* Memory management routines ***************************************/
126 
131 GncGUID *
132 guid_convert_create (gnc::GUID const & guid)
133 {
134  GncGUID temp = guid;
135  return guid_copy (&temp);
136 }
137 
138 GncGUID *
140 {
141  return new GncGUID;
142 }
143 
144 void
145 guid_free (GncGUID *guid)
146 {
147  if (!guid) return;
148  if (guid == s_null_gncguid)
149  /* Don't delete that! */
150  return;
151  delete guid;
152 }
153 
154 GncGUID *
155 guid_copy (const GncGUID *guid)
156 {
157  if (!guid) return nullptr;
158  auto ret = guid_malloc ();
159  *ret = *guid;
160  return ret;
161 }
162 
163 /*It looks like we are expected to provide the same pointer every time from this function*/
164 const GncGUID *
165 guid_null (void)
166 {
167  return s_null_gncguid;
168 }
169 
170 static void
171 guid_assign (GncGUID & target, gnc::GUID const & source)
172 {
173  std::copy (source.begin(), source.end(), target.reserved);
174 }
175 
176 /*Takes an allocated guid pointer and constructs it in place*/
177 void
179 {
180  if (!guid) return;
181  gnc::GUID temp_random {gnc::GUID::create_random ()};
182  guid_assign (*guid, temp_random);
183 }
184 
185 GncGUID *
186 guid_new (void)
187 {
188  auto ret = guid_new_return ();
189  return guid_copy (&ret);
190 }
191 
192 GncGUID
194 {
195  return gnc::GUID::create_random ();
196 }
197 
198 gchar *
199 guid_to_string (const GncGUID * guid)
200 {
201  if (!guid) return nullptr;
202  char* buffer = g_new (char, GUID_ENCODING_LENGTH + 1);
203  guid_to_string_buff (guid, buffer);
204  return buffer;
205 }
206 
207 gchar *
208 guid_to_string_buff (const GncGUID * guid, gchar *str)
209 {
210  if (!str || !guid) return nullptr;
211  fast_guid_to_string (guid->reserved, str);
212  str[GUID_ENCODING_LENGTH] = '\0';
213  return str;
214 }
215 
216 gboolean
217 string_to_guid (const char * str, GncGUID * guid)
218 {
219  if (!guid || !str || !*str) return false;
220 
221  if (fast_string_to_guid (str, guid->reserved))
222  return true;
223 
224  try
225  {
226  guid_assign (*guid, gnc::GUID::from_string (str));
227  }
228  catch (...)
229  {
230  PINFO("Failed to construct a GUID from %s", str);
231  return false;
232  }
233  return true;
234 }
235 
236 gboolean
237 guid_equal (const GncGUID *guid_1, const GncGUID *guid_2)
238 {
239  return guid_compare (guid_1, guid_2) == 0;
240 }
241 
242 gint
243 guid_compare (const GncGUID *guid_1, const GncGUID *guid_2)
244 {
245  if (guid_1 == guid_2) return 0;
246  if (!guid_1) return -1;
247  if (!guid_2) return 1;
248  return std::memcmp (guid_1->reserved, guid_2->reserved, GUID_DATA_SIZE);
249 }
250 
251 // returns a 32-bit hash from 32-byte guid. since guid are generated
252 // randomly, this is not expected to cause hash collisions. use memcpy
253 // to avoid alignment issues; memcpy likely to be optimised away.
254 guint
255 guid_hash_to_guint (gconstpointer ptr)
256 {
257  if (!ptr)
258  {
259  PERR ("received nullptr guid pointer.");
260  return 0;
261  }
262  const GncGUID* g = static_cast<const GncGUID*>(ptr);
263  guint rv;
264  memcpy (&rv, &g->reserved[12], sizeof (guint));
265  return rv;
266 }
267 
268 gint
269 guid_g_hash_table_equal (gconstpointer guid_a, gconstpointer guid_b)
270 {
271  return guid_equal (reinterpret_cast<const GncGUID*> (guid_a),
272  reinterpret_cast<const GncGUID*> (guid_b));
273 }
274 
275 GHashTable *
277 {
278  return g_hash_table_new (guid_hash_to_guint, guid_g_hash_table_equal);
279 }
280 
281 /***************************/
282 static void
283 gnc_string_to_guid (const GValue *src, GValue *dest)
284 {
285  /* FIXME: add more checks*/
286  GncGUID *guid;
287  const gchar *as_string;
288 
289  g_return_if_fail (G_VALUE_HOLDS_STRING (src) &&
290  GNC_VALUE_HOLDS_GUID (dest));
291 
292  as_string = g_value_get_string (src);
293 
294  guid = g_new0 (GncGUID, 1);
295  string_to_guid (as_string, guid);
296 
297  g_value_take_boxed (dest, guid);
298 }
299 
300 static void
301 gnc_guid_to_string (const GValue *src, GValue *dest)
302 {
303  const gchar *str;
304 
305  g_return_if_fail (G_VALUE_HOLDS_STRING (dest) &&
306  GNC_VALUE_HOLDS_GUID (src));
307 
308  str = guid_to_string (gnc_value_get_guid (src));
309 
310  g_value_set_string (dest, str);
311 }
312 
313 G_DEFINE_BOXED_TYPE_WITH_CODE (GncGUID, gnc_guid, guid_copy, guid_free,
314  g_value_register_transform_func (G_TYPE_STRING,
315  g_define_type_id,
316  gnc_string_to_guid);
317 
318  g_value_register_transform_func (g_define_type_id,
319  G_TYPE_STRING,
320  gnc_guid_to_string);
321  )
322 
323 namespace gnc
324 {
325 
326 GUID
327 GUID::create_random () noexcept
328 {
329  static boost::uuids::random_generator gen;
330  return {gen ()};
331 }
332 
333 GUID::GUID (boost::uuids::uuid const & other) noexcept
334  : implementation (other)
335 {
336 }
337 
338 GUID const &
339 GUID::null_guid () noexcept
340 {
341  return s_null_guid;
342 }
343 
344 std::string
345 GUID::to_string () const noexcept
346 {
347  std::string out;
348  out.resize (implementation.size() * 2);
349  fast_guid_to_string (implementation.data, out.data());
350  return out;
351 }
352 
353 GUID
354 GUID::from_string (const char* str)
355 {
356  if (!str)
357  throw guid_syntax_exception {};
358 
359  if (boost::uuids::uuid u; fast_string_to_guid(str, u.data))
360  return u;
361  try
362  {
363  static boost::uuids::string_generator strgen;
364  return strgen (str);
365  }
366  catch (...)
367  {
368  throw guid_syntax_exception {};
369  }
370 }
371 
372 bool
373 GUID::is_valid_guid (const char* str)
374 {
375  uint8_t bytes[16];
376  if (fast_string_to_guid(str, bytes))
377  return true;
378  try
379  {
380  static boost::uuids::string_generator strgen;
381  strgen (str);
382  return true;
383  }
384  catch (...)
385  {
386  return false;
387  }
388 }
389 
390 guid_syntax_exception::guid_syntax_exception () noexcept
391  : invalid_argument {"Invalid syntax for guid."}
392 {
393 }
394 
395 GUID::GUID (GncGUID const & other) noexcept
396 : implementation {{other.reserved[0] , other.reserved[1]
397  , other.reserved[2], other.reserved[3]
398  , other.reserved[4], other.reserved[5]
399  , other.reserved[6], other.reserved[7]
400  , other.reserved[8], other.reserved[9]
401  , other.reserved[10], other.reserved[11]
402  , other.reserved[12], other.reserved[13]
403  , other.reserved[14], other.reserved[15]}
404  }
405 {
406 
407 }
408 
409 auto
410 GUID::end () const noexcept -> decltype (implementation.end ())
411 {
412  return implementation.end ();
413 }
414 
415 auto
416 GUID::begin () const noexcept -> decltype (implementation.begin ())
417 {
418  return implementation.begin ();
419 }
420 
421 bool
422 GUID::operator < (GUID const & other) noexcept
423 {
424  return implementation < other.implementation;
425 }
426 
427 bool operator == (GUID const & lhs, GncGUID const & rhs) noexcept
428 {
429  return lhs.implementation == GUID(rhs).implementation;
430 }
431 
432 bool
433 operator != (GUID const & one, GUID const & two) noexcept
434 {
435  return one.implementation != two.implementation;
436 }
437 
438 GUID & GUID::operator = (GUID && other) noexcept
439 {
440  boost::uuids::swap (other.implementation, implementation);
441  return *this;
442 }
443 
444 GUID::operator GncGUID () const noexcept
445 {
446  GncGUID ret;
447  guid_assign (ret, *this);
448  return ret;
449 }
450 
451 } // namespace gnc
452 
453 bool
454 operator==(const GncGUID& lhs, const GncGUID& rhs)
455 {
456  return gnc::GUID{lhs} == gnc::GUID{rhs};
457 }
void guid_replace(GncGUID *guid)
Generate a new guid.
Definition: guid.cpp:178
#define PINFO(format, args...)
Print an informational note.
Definition: qoflog.h:256
GncGUID guid_new_return(void)
Generate a new id.
Definition: guid.cpp:193
GncGUID * guid_copy(const GncGUID *guid)
Returns a newly allocated GncGUID that matches the passed-in GUID.
Definition: guid.cpp:155
gboolean string_to_guid(const gchar *string, GncGUID *guid)
Given a string, replace the given guid with the parsed one unless the given value is null...
GncGUID * guid_new(void)
Allocate and construct a new GUID.
Definition: guid.cpp:186
globally unique ID User API
GHashTable * guid_hash_table_new(void)
Returns a GHashTable with <GUID*> as key and a <gpointer> as value and no destructor functions for ke...
Definition: guid.cpp:276
gchar * guid_to_string_buff(const GncGUID *guid, gchar *str)
The guid_to_string_buff() routine puts a null-terminated string encoding of the id into the memory po...
Definition: guid.cpp:208
guint guid_hash_to_guint(gconstpointer ptr)
Hash function for a GUID.
Definition: guid.cpp:255
const GncGUID * gnc_value_get_guid(const GValue *value)
gnc_value_get_guid
Definition: guid.cpp:72
#define PERR(format, args...)
Log a serious error.
Definition: qoflog.h:244
GncGUID * guid_malloc(void)
Allocate memory for a GUID.
Definition: guid.cpp:139
gboolean guid_equal(const GncGUID *guid_1, const GncGUID *guid_2)
Given two GUIDs, return TRUE if they are non-NULL and equal.
Definition: guid.cpp:237
#define GUID_ENCODING_LENGTH
Number of characters needed to encode a guid as a string not including the null terminator.
Definition: guid.h:84
gchar * guid_to_string(const GncGUID *guid)
The guid_to_string() routine returns a null-terminated string encoding of the id. ...
Definition: guid.cpp:199
const GncGUID * guid_null(void)
Returns a GncGUID which is guaranteed to never reference any entity.
Definition: guid.cpp:165
The type used to store guids in C.
Definition: guid.h:75
gint guid_g_hash_table_equal(gconstpointer guid_a, gconstpointer guid_b)
Equality function for two GUIDs in a GHashTable.
Definition: guid.cpp:269