Line data Source code
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 :
64 : /**
65 : * gnc_value_get_guid
66 : *
67 : * @param value a @c GValue whose value we want to get.
68 : *
69 : * @return the value stored in @a value
70 : */
71 : const GncGUID*
72 0 : gnc_value_get_guid (const GValue *value)
73 : {
74 0 : if (!value) return nullptr;
75 : GncGUID *val;
76 :
77 0 : g_return_val_if_fail (value && G_IS_VALUE (value), nullptr);
78 0 : g_return_val_if_fail (GNC_VALUE_HOLDS_GUID (value), nullptr);
79 :
80 0 : val = (GncGUID*) g_value_get_boxed (value);
81 :
82 0 : 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 334080 : char_to_num (unsigned char c) noexcept
92 : {
93 334080 : unsigned int digit = c - '0';
94 334080 : unsigned int alpha = (c | 0x20) - 'a';
95 334080 : return digit <= 9 ? digit : alpha <= 5 ? alpha + 10 : -1;
96 : }
97 :
98 : static inline bool
99 10446 : fast_string_to_guid (const char* s, uint8_t* out) noexcept
100 : {
101 10446 : if (strnlen (s, GUID_ENCODING_LENGTH + 1) != GUID_ENCODING_LENGTH) return false;
102 10440 : bool all_ok = true;
103 177480 : for (int i = 0; i < GUID_DATA_SIZE; i++)
104 : {
105 167040 : int hi = char_to_num (*s++);
106 167040 : int lo = char_to_num (*s++);
107 167040 : all_ok &= (hi >= 0 && lo >= 0);
108 167040 : out[i] = (unsigned char)(((unsigned)hi << 4) | (unsigned)lo);
109 : }
110 10440 : return all_ok;
111 : }
112 :
113 : static inline void
114 33553 : fast_guid_to_string (const uint8_t* src, char* dest) noexcept
115 : {
116 : static constexpr char hex[] = "0123456789abcdef";
117 570401 : for (size_t i = 0; i < 16; i++)
118 : {
119 536848 : uint8_t b = src[i];
120 536848 : *dest++ = hex[b >> 4];
121 536848 : *dest++ = hex[b & 0x0F];
122 : }
123 33553 : }
124 :
125 : /* Memory management routines ***************************************/
126 :
127 : /**
128 : * Allocates and returns a new GncGUID containing the same value as the
129 : * gnc::GUID passed in.
130 : */
131 : GncGUID *
132 118 : guid_convert_create (gnc::GUID const & guid)
133 : {
134 118 : GncGUID temp = guid;
135 236 : return guid_copy (&temp);
136 : }
137 :
138 : GncGUID *
139 41744 : guid_malloc (void)
140 : {
141 41744 : return new GncGUID;
142 : }
143 :
144 : void
145 41705 : guid_free (GncGUID *guid)
146 : {
147 41705 : if (!guid) return;
148 40500 : if (guid == s_null_gncguid)
149 : /* Don't delete that! */
150 0 : return;
151 40500 : delete guid;
152 : }
153 :
154 : GncGUID *
155 9211 : guid_copy (const GncGUID *guid)
156 : {
157 9211 : if (!guid) return nullptr;
158 9211 : auto ret = guid_malloc ();
159 9211 : *ret = *guid;
160 9211 : 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 155793 : guid_null (void)
166 : {
167 155793 : return s_null_gncguid;
168 : }
169 :
170 : static void
171 151185 : guid_assign (GncGUID & target, gnc::GUID const & source)
172 : {
173 151185 : std::copy (source.begin(), source.end(), target.reserved);
174 151185 : }
175 :
176 : /*Takes an allocated guid pointer and constructs it in place*/
177 : void
178 145997 : guid_replace (GncGUID *guid)
179 : {
180 145997 : if (!guid) return;
181 145997 : gnc::GUID temp_random {gnc::GUID::create_random ()};
182 145997 : guid_assign (*guid, temp_random);
183 : }
184 :
185 : GncGUID *
186 4991 : guid_new (void)
187 : {
188 4991 : auto ret = guid_new_return ();
189 9982 : return guid_copy (&ret);
190 : }
191 :
192 : GncGUID
193 5056 : guid_new_return (void)
194 : {
195 5056 : return gnc::GUID::create_random ();
196 : }
197 :
198 : gchar *
199 450 : guid_to_string (const GncGUID * guid)
200 : {
201 450 : if (!guid) return nullptr;
202 450 : char* buffer = g_new (char, GUID_ENCODING_LENGTH + 1);
203 450 : guid_to_string_buff (guid, buffer);
204 450 : return buffer;
205 : }
206 :
207 : gchar *
208 32747 : guid_to_string_buff (const GncGUID * guid, gchar *str)
209 : {
210 32747 : if (!str || !guid) return nullptr;
211 32747 : fast_guid_to_string (guid->reserved, str);
212 32747 : str[GUID_ENCODING_LENGTH] = '\0';
213 32747 : return str;
214 : }
215 :
216 : gboolean
217 10554 : string_to_guid (const char * str, GncGUID * guid)
218 : {
219 10554 : if (!guid || !str || !*str) return false;
220 :
221 10425 : if (fast_string_to_guid (str, guid->reserved))
222 10425 : return true;
223 :
224 : try
225 : {
226 0 : guid_assign (*guid, gnc::GUID::from_string (str));
227 : }
228 0 : catch (...)
229 : {
230 0 : PINFO("Failed to construct a GUID from %s", str);
231 0 : return false;
232 0 : }
233 0 : return true;
234 : }
235 :
236 : gboolean
237 445429 : guid_equal (const GncGUID *guid_1, const GncGUID *guid_2)
238 : {
239 445429 : return guid_compare (guid_1, guid_2) == 0;
240 : }
241 :
242 : gint
243 453384 : guid_compare (const GncGUID *guid_1, const GncGUID *guid_2)
244 : {
245 453384 : if (guid_1 == guid_2) return 0;
246 342052 : if (!guid_1) return -1;
247 342052 : if (!guid_2) return 1;
248 341981 : 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 560249 : guid_hash_to_guint (gconstpointer ptr)
256 : {
257 560249 : if (!ptr)
258 : {
259 0 : PERR ("received nullptr guid pointer.");
260 0 : return 0;
261 : }
262 560249 : const GncGUID* g = static_cast<const GncGUID*>(ptr);
263 : guint rv;
264 560249 : memcpy (&rv, &g->reserved[12], sizeof (guint));
265 560249 : return rv;
266 : }
267 :
268 : gint
269 167605 : guid_g_hash_table_equal (gconstpointer guid_a, gconstpointer guid_b)
270 : {
271 167605 : return guid_equal (reinterpret_cast<const GncGUID*> (guid_a),
272 167605 : reinterpret_cast<const GncGUID*> (guid_b));
273 : }
274 :
275 : GHashTable *
276 6729 : guid_hash_table_new (void)
277 : {
278 6729 : return g_hash_table_new (guid_hash_to_guint, guid_g_hash_table_equal);
279 : }
280 :
281 : /***************************/
282 : static void
283 0 : 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 0 : g_return_if_fail (G_VALUE_HOLDS_STRING (src) &&
290 : GNC_VALUE_HOLDS_GUID (dest));
291 :
292 0 : as_string = g_value_get_string (src);
293 :
294 0 : guid = g_new0 (GncGUID, 1);
295 0 : string_to_guid (as_string, guid);
296 :
297 0 : g_value_take_boxed (dest, guid);
298 : }
299 :
300 : static void
301 0 : gnc_guid_to_string (const GValue *src, GValue *dest)
302 : {
303 : gchar *str;
304 :
305 0 : g_return_if_fail (G_VALUE_HOLDS_STRING (dest) &&
306 : GNC_VALUE_HOLDS_GUID (src));
307 :
308 0 : str = guid_to_string (gnc_value_get_guid (src));
309 :
310 0 : g_value_take_string (dest, str);
311 : }
312 :
313 1241 : 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 151053 : GUID::create_random () noexcept
328 : {
329 151053 : static boost::uuids::random_generator gen;
330 151053 : return {gen ()};
331 : }
332 :
333 151185 : GUID::GUID (boost::uuids::uuid const & other) noexcept
334 151185 : : implementation (other)
335 : {
336 151185 : }
337 :
338 : GUID const &
339 0 : GUID::null_guid () noexcept
340 : {
341 0 : return s_null_guid;
342 : }
343 :
344 : std::string
345 806 : GUID::to_string () const noexcept
346 : {
347 806 : std::string out;
348 806 : out.resize (implementation.size() * 2);
349 806 : fast_guid_to_string (implementation.data, out.data());
350 806 : return out;
351 : }
352 :
353 : GUID
354 14 : GUID::from_string (const char* str)
355 : {
356 14 : if (!str)
357 0 : throw guid_syntax_exception {};
358 :
359 14 : if (boost::uuids::uuid u; fast_string_to_guid(str, u.data))
360 14 : return u;
361 : try
362 : {
363 : static boost::uuids::string_generator strgen;
364 0 : return strgen (str);
365 : }
366 0 : catch (...)
367 : {
368 0 : throw guid_syntax_exception {};
369 0 : }
370 : }
371 :
372 : bool
373 7 : GUID::is_valid_guid (const char* str)
374 : {
375 : uint8_t bytes[16];
376 7 : if (fast_string_to_guid(str, bytes))
377 1 : return true;
378 : try
379 : {
380 : static boost::uuids::string_generator strgen;
381 6 : strgen (str);
382 0 : return true;
383 : }
384 6 : catch (...)
385 : {
386 6 : return false;
387 6 : }
388 : }
389 :
390 0 : guid_syntax_exception::guid_syntax_exception () noexcept
391 0 : : invalid_argument {"Invalid syntax for guid."}
392 : {
393 0 : }
394 :
395 830 : GUID::GUID (GncGUID const & other) noexcept
396 1660 : : implementation {{other.reserved[0] , other.reserved[1]
397 830 : , other.reserved[2], other.reserved[3]
398 830 : , other.reserved[4], other.reserved[5]
399 830 : , other.reserved[6], other.reserved[7]
400 830 : , other.reserved[8], other.reserved[9]
401 830 : , other.reserved[10], other.reserved[11]
402 830 : , other.reserved[12], other.reserved[13]
403 830 : , other.reserved[14], other.reserved[15]}
404 : }
405 : {
406 :
407 830 : }
408 :
409 : auto
410 151185 : GUID::end () const noexcept -> decltype (implementation.end ())
411 : {
412 151185 : return implementation.end ();
413 : }
414 :
415 : auto
416 151185 : GUID::begin () const noexcept -> decltype (implementation.begin ())
417 : {
418 151185 : return implementation.begin ();
419 : }
420 :
421 : bool
422 0 : GUID::operator < (GUID const & other) noexcept
423 : {
424 0 : return implementation < other.implementation;
425 : }
426 :
427 8 : bool operator == (GUID const & lhs, GncGUID const & rhs) noexcept
428 : {
429 8 : return lhs.implementation == GUID(rhs).implementation;
430 : }
431 :
432 : bool
433 0 : operator != (GUID const & one, GUID const & two) noexcept
434 : {
435 0 : return one.implementation != two.implementation;
436 : }
437 :
438 8 : GUID & GUID::operator = (GUID && other) noexcept
439 : {
440 8 : boost::uuids::swap (other.implementation, implementation);
441 8 : return *this;
442 : }
443 :
444 5188 : GUID::operator GncGUID () const noexcept
445 : {
446 : GncGUID ret;
447 5188 : guid_assign (ret, *this);
448 5188 : return ret;
449 : }
450 :
451 : } // namespace gnc
452 :
453 : bool
454 8 : operator==(const GncGUID& lhs, const GncGUID& rhs)
455 : {
456 8 : return gnc::GUID{lhs} == gnc::GUID{rhs};
457 : }
|