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 <= 6 ? 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)((hi << 4) | 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  if (guid_1 == guid_2) return true;
240  if (!guid_1 || !guid_2) return false;
241  gnc::GUID temp1 {*guid_1};
242  gnc::GUID temp2 {*guid_2};
243  return temp1 == temp2;
244 }
245 
246 gint
247 guid_compare (const GncGUID *guid_1, const GncGUID *guid_2)
248 {
249  if (guid_1 == guid_2) return 0;
250  if (!guid_1) return -1;
251  if (!guid_2) return 1;
252  gnc::GUID temp1 {*guid_1};
253  gnc::GUID temp2 {*guid_2};
254  if (temp1 < temp2)
255  return -1;
256  if (temp1 == temp2)
257  return 0;
258  return 1;
259 }
260 
261 guint
262 guid_hash_to_guint (gconstpointer ptr)
263 {
264  if (!ptr)
265  {
266  PERR ("received nullptr guid pointer.");
267  return 0;
268  }
269  GncGUID const & guid = * reinterpret_cast <GncGUID const *> (ptr);
270  gnc::GUID const & temp {guid};
271 
272  guint hash {0};
273  std::for_each (temp.begin (), temp.end (), [&hash] (unsigned char a) {
274  hash <<=4;
275  hash |= a;
276  });
277  return hash;
278 }
279 
280 gint
281 guid_g_hash_table_equal (gconstpointer guid_a, gconstpointer guid_b)
282 {
283  return guid_equal (reinterpret_cast<const GncGUID*> (guid_a),
284  reinterpret_cast<const GncGUID*> (guid_b));
285 }
286 
287 GHashTable *
289 {
290  return g_hash_table_new (guid_hash_to_guint, guid_g_hash_table_equal);
291 }
292 
293 /***************************/
294 static void
295 gnc_string_to_guid (const GValue *src, GValue *dest)
296 {
297  /* FIXME: add more checks*/
298  GncGUID *guid;
299  const gchar *as_string;
300 
301  g_return_if_fail (G_VALUE_HOLDS_STRING (src) &&
302  GNC_VALUE_HOLDS_GUID (dest));
303 
304  as_string = g_value_get_string (src);
305 
306  guid = g_new0 (GncGUID, 1);
307  string_to_guid (as_string, guid);
308 
309  g_value_take_boxed (dest, guid);
310 }
311 
312 static void
313 gnc_guid_to_string (const GValue *src, GValue *dest)
314 {
315  const gchar *str;
316 
317  g_return_if_fail (G_VALUE_HOLDS_STRING (dest) &&
318  GNC_VALUE_HOLDS_GUID (src));
319 
320  str = guid_to_string (gnc_value_get_guid (src));
321 
322  g_value_set_string (dest, str);
323 }
324 
325 G_DEFINE_BOXED_TYPE_WITH_CODE (GncGUID, gnc_guid, guid_copy, guid_free,
326  g_value_register_transform_func (G_TYPE_STRING,
327  g_define_type_id,
328  gnc_string_to_guid);
329 
330  g_value_register_transform_func (g_define_type_id,
331  G_TYPE_STRING,
332  gnc_guid_to_string);
333  )
334 
335 namespace gnc
336 {
337 
338 GUID
339 GUID::create_random () noexcept
340 {
341  static boost::uuids::random_generator gen;
342  return {gen ()};
343 }
344 
345 GUID::GUID (boost::uuids::uuid const & other) noexcept
346  : implementation (other)
347 {
348 }
349 
350 GUID const &
351 GUID::null_guid () noexcept
352 {
353  return s_null_guid;
354 }
355 
356 std::string
357 GUID::to_string () const noexcept
358 {
359  std::string out;
360  out.resize (implementation.size() * 2);
361  fast_guid_to_string (implementation.data, out.data());
362  return out;
363 }
364 
365 GUID
366 GUID::from_string (const char* str)
367 {
368  if (!str)
369  throw guid_syntax_exception {};
370 
371  uint8_t bytes[16];
372  if (fast_string_to_guid(str, bytes))
373  {
374  boost::uuids::uuid u;
375  std::memcpy(u.data, bytes, 16);
376  return GUID{u};
377  }
378  try
379  {
380  static boost::uuids::string_generator strgen;
381  return strgen (str);
382  }
383  catch (...)
384  {
385  throw guid_syntax_exception {};
386  }
387 }
388 
389 bool
390 GUID::is_valid_guid (const char* str)
391 {
392  uint8_t bytes[16];
393  if (fast_string_to_guid(str, bytes))
394  return true;
395  try
396  {
397  static boost::uuids::string_generator strgen;
398  strgen (str);
399  return true;
400  }
401  catch (...)
402  {
403  return false;
404  }
405 }
406 
407 guid_syntax_exception::guid_syntax_exception () noexcept
408  : invalid_argument {"Invalid syntax for guid."}
409 {
410 }
411 
412 GUID::GUID (GncGUID const & other) noexcept
413 : implementation {{other.reserved[0] , other.reserved[1]
414  , other.reserved[2], other.reserved[3]
415  , other.reserved[4], other.reserved[5]
416  , other.reserved[6], other.reserved[7]
417  , other.reserved[8], other.reserved[9]
418  , other.reserved[10], other.reserved[11]
419  , other.reserved[12], other.reserved[13]
420  , other.reserved[14], other.reserved[15]}
421  }
422 {
423 
424 }
425 
426 auto
427 GUID::end () const noexcept -> decltype (implementation.end ())
428 {
429  return implementation.end ();
430 }
431 
432 auto
433 GUID::begin () const noexcept -> decltype (implementation.begin ())
434 {
435  return implementation.begin ();
436 }
437 
438 bool
439 GUID::operator < (GUID const & other) noexcept
440 {
441  return implementation < other.implementation;
442 }
443 
444 bool operator == (GUID const & lhs, GncGUID const & rhs) noexcept
445 {
446  return lhs.implementation == GUID(rhs).implementation;
447 }
448 
449 bool
450 operator != (GUID const & one, GUID const & two) noexcept
451 {
452  return one.implementation != two.implementation;
453 }
454 
455 GUID & GUID::operator = (GUID && other) noexcept
456 {
457  boost::uuids::swap (other.implementation, implementation);
458  return *this;
459 }
460 
461 GUID::operator GncGUID () const noexcept
462 {
463  GncGUID ret;
464  guid_assign (ret, *this);
465  return ret;
466 }
467 
468 } // namespace gnc
469 
470 bool
471 operator==(const GncGUID& lhs, const GncGUID& rhs)
472 {
473  return gnc::GUID{lhs} == gnc::GUID{rhs};
474 }
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:288
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:262
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:281