GnuCash c935c2f+
Loading...
Searching...
No Matches
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. */
62static QofLogModule log_module = QOF_MOD_ENGINE;
63
71const GncGUID*
72gnc_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
85GncGUID * guid_convert_create (gnc::GUID const &);
86
87static gnc::GUID s_null_guid {boost::uuids::uuid { {0}}};
88static GncGUID * s_null_gncguid {guid_convert_create (s_null_guid)};
89
90static inline int
91char_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
98static inline bool
99fast_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
113static inline void
114fast_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
131GncGUID *
132guid_convert_create (gnc::GUID const & guid)
133{
134 GncGUID temp = guid;
135 return guid_copy (&temp);
136}
137
138GncGUID *
140{
141 return new GncGUID;
142}
143
144void
145guid_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
154GncGUID *
155guid_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*/
164const GncGUID *
166{
167 return s_null_gncguid;
168}
169
170static void
171guid_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*/
177void
179{
180 if (!guid) return;
181 gnc::GUID temp_random {gnc::GUID::create_random ()};
182 guid_assign (*guid, temp_random);
183}
184
185GncGUID *
187{
188 auto ret = guid_new_return ();
189 return guid_copy (&ret);
190}
191
194{
195 return gnc::GUID::create_random ();
196}
197
198gchar *
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
207gchar *
208guid_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
216gboolean
217string_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
236gboolean
237guid_equal (const GncGUID *guid_1, const GncGUID *guid_2)
238{
239 return guid_compare (guid_1, guid_2) == 0;
240}
241
242gint
243guid_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.
254guint
255guid_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
268gint
269guid_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
275GHashTable *
277{
278 return g_hash_table_new (guid_hash_to_guint, guid_g_hash_table_equal);
279}
280
281/***************************/
282static void
283gnc_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
300static void
301gnc_guid_to_string (const GValue *src, GValue *dest)
302{
303 gchar *str;
304
305 g_return_if_fail (G_VALUE_HOLDS_STRING (dest) &&
306 GNC_VALUE_HOLDS_GUID (src));
307
309
310 g_value_take_string (dest, str);
311}
312
313G_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
323namespace gnc
324{
325
326GUID
327GUID::create_random () noexcept
328{
329 static boost::uuids::random_generator gen;
330 return {gen ()};
331}
332
333GUID::GUID (boost::uuids::uuid const & other) noexcept
334 : implementation (other)
335{
336}
337
338GUID const &
339GUID::null_guid () noexcept
340{
341 return s_null_guid;
342}
343
344std::string
345GUID::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
353GUID
354GUID::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
372bool
373GUID::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
390guid_syntax_exception::guid_syntax_exception () noexcept
391 : invalid_argument {"Invalid syntax for guid."}
392{
393}
394
395GUID::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
409auto
410GUID::end () const noexcept -> decltype (implementation.end ())
411{
412 return implementation.end ();
413}
414
415auto
416GUID::begin () const noexcept -> decltype (implementation.begin ())
417{
418 return implementation.begin ();
419}
420
421bool
422GUID::operator < (GUID const & other) noexcept
423{
424 return implementation < other.implementation;
425}
426
427bool operator == (GUID const & lhs, GncGUID const & rhs) noexcept
428{
429 return lhs.implementation == GUID(rhs).implementation;
430}
431
432bool
433operator != (GUID const & one, GUID const & two) noexcept
434{
435 return one.implementation != two.implementation;
436}
437
438GUID & GUID::operator = (GUID && other) noexcept
439{
440 boost::uuids::swap (other.implementation, implementation);
441 return *this;
442}
443
444GUID::operator GncGUID () const noexcept
445{
446 GncGUID ret;
447 guid_assign (ret, *this);
448 return ret;
449}
450
451} // namespace gnc
452
453bool
454operator==(const GncGUID& lhs, const GncGUID& rhs)
455{
456 return gnc::GUID{lhs} == gnc::GUID{rhs};
457}
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(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
#define GUID_ENCODING_LENGTH
Number of characters needed to encode a guid as a string not including the null terminator.
Definition guid.h:84
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
guint guid_hash_to_guint(gconstpointer ptr)
Hash function for a GUID.
Definition guid.cpp:255
void guid_replace(GncGUID *guid)
Generate a new guid.
Definition guid.cpp:178
GncGUID * guid_copy(const GncGUID *guid)
Returns a newly allocated GncGUID that matches the passed-in GUID.
Definition guid.cpp:155
const GncGUID * gnc_value_get_guid(const GValue *value)
gnc_value_get_guid
Definition guid.cpp:72
gint guid_g_hash_table_equal(gconstpointer guid_a, gconstpointer guid_b)
Equality function for two GUIDs in a GHashTable.
Definition guid.cpp:269
GncGUID guid_new_return(void)
Generate a new id.
Definition guid.cpp:193
GncGUID * guid_malloc(void)
Allocate memory for a GUID.
Definition guid.cpp:139
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
GncGUID * guid_new(void)
Allocate and construct a new GUID.
Definition guid.cpp:186
#define PINFO(format, args...)
Print an informational note.
Definition qoflog.h:256
#define PERR(format, args...)
Log a serious error.
Definition qoflog.h:244
globally unique ID User API
The type used to store guids in C.
Definition guid.h:75