Branch data 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 : 331008 : char_to_num (unsigned char c) noexcept
92 : : {
93 : 331008 : unsigned int digit = c - '0';
94 : 331008 : unsigned int alpha = (c | 0x20) - 'a';
95 : 331008 : return digit <= 9 ? digit : alpha <= 5 ? alpha + 10 : -1;
96 : : }
97 : :
98 : : static inline bool
99 : 10350 : fast_string_to_guid (const char* s, uint8_t* out) noexcept
100 : : {
101 : 10350 : if (strnlen (s, GUID_ENCODING_LENGTH + 1) != GUID_ENCODING_LENGTH) return false;
102 : 10344 : bool all_ok = true;
103 : 175848 : for (int i = 0; i < GUID_DATA_SIZE; i++)
104 : : {
105 : 165504 : int hi = char_to_num (*s++);
106 : 165504 : int lo = char_to_num (*s++);
107 : 165504 : all_ok &= (hi >= 0 && lo >= 0);
108 : 165504 : out[i] = (unsigned char)(((unsigned)hi << 4) | (unsigned)lo);
109 : : }
110 : 10344 : return all_ok;
111 : : }
112 : :
113 : : static inline void
114 : 33142 : fast_guid_to_string (const uint8_t* src, char* dest) noexcept
115 : : {
116 : : static constexpr char hex[] = "0123456789abcdef";
117 : 563414 : for (size_t i = 0; i < 16; i++)
118 : : {
119 : 530272 : uint8_t b = src[i];
120 : 530272 : *dest++ = hex[b >> 4];
121 : 530272 : *dest++ = hex[b & 0x0F];
122 : : }
123 : 33142 : }
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 : 115 : guid_convert_create (gnc::GUID const & guid)
133 : : {
134 : 115 : GncGUID temp = guid;
135 : 230 : return guid_copy (&temp);
136 : : }
137 : :
138 : : GncGUID *
139 : 44171 : guid_malloc (void)
140 : : {
141 : 44171 : return new GncGUID;
142 : : }
143 : :
144 : : void
145 : 43985 : guid_free (GncGUID *guid)
146 : : {
147 : 43985 : if (!guid) return;
148 : 42963 : if (guid == s_null_gncguid)
149 : : /* Don't delete that! */
150 : 0 : return;
151 : 42963 : delete guid;
152 : : }
153 : :
154 : : GncGUID *
155 : 9103 : guid_copy (const GncGUID *guid)
156 : : {
157 : 9103 : if (!guid) return nullptr;
158 : 9103 : auto ret = guid_malloc ();
159 : 9103 : *ret = *guid;
160 : 9103 : 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 : 144253 : guid_null (void)
166 : : {
167 : 144253 : return s_null_gncguid;
168 : : }
169 : :
170 : : static void
171 : 139762 : guid_assign (GncGUID & target, gnc::GUID const & source)
172 : : {
173 : 139762 : std::copy (source.begin(), source.end(), target.reserved);
174 : 139762 : }
175 : :
176 : : /*Takes an allocated guid pointer and constructs it in place*/
177 : : void
178 : 134568 : guid_replace (GncGUID *guid)
179 : : {
180 : 134568 : if (!guid) return;
181 : 134568 : gnc::GUID temp_random {gnc::GUID::create_random ()};
182 : 134568 : guid_assign (*guid, temp_random);
183 : : }
184 : :
185 : : GncGUID *
186 : 4990 : guid_new (void)
187 : : {
188 : 4990 : auto ret = guid_new_return ();
189 : 9980 : return guid_copy (&ret);
190 : : }
191 : :
192 : : GncGUID
193 : 5055 : guid_new_return (void)
194 : : {
195 : 5055 : return gnc::GUID::create_random ();
196 : : }
197 : :
198 : : gchar *
199 : 395 : guid_to_string (const GncGUID * guid)
200 : : {
201 : 395 : if (!guid) return nullptr;
202 : 395 : char* buffer = g_new (char, GUID_ENCODING_LENGTH + 1);
203 : 395 : guid_to_string_buff (guid, buffer);
204 : 395 : return buffer;
205 : : }
206 : :
207 : : gchar *
208 : 32339 : guid_to_string_buff (const GncGUID * guid, gchar *str)
209 : : {
210 : 32339 : if (!str || !guid) return nullptr;
211 : 32339 : fast_guid_to_string (guid->reserved, str);
212 : 32339 : str[GUID_ENCODING_LENGTH] = '\0';
213 : 32339 : return str;
214 : : }
215 : :
216 : : gboolean
217 : 10430 : string_to_guid (const char * str, GncGUID * guid)
218 : : {
219 : 10430 : if (!guid || !str || !*str) return false;
220 : :
221 : 10329 : if (fast_string_to_guid (str, guid->reserved))
222 : 10329 : 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 : 468871 : guid_equal (const GncGUID *guid_1, const GncGUID *guid_2)
238 : : {
239 : 468871 : return guid_compare (guid_1, guid_2) == 0;
240 : : }
241 : :
242 : : gint
243 : 478529 : guid_compare (const GncGUID *guid_1, const GncGUID *guid_2)
244 : : {
245 : 478529 : if (guid_1 == guid_2) return 0;
246 : 373337 : if (!guid_1) return -1;
247 : 373337 : if (!guid_2) return 1;
248 : 373266 : 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 : 519866 : guid_hash_to_guint (gconstpointer ptr)
256 : : {
257 : 519866 : if (!ptr)
258 : : {
259 : 0 : PERR ("received nullptr guid pointer.");
260 : 0 : return 0;
261 : : }
262 : 519866 : const GncGUID* g = static_cast<const GncGUID*>(ptr);
263 : : guint rv;
264 : 519866 : memcpy (&rv, &g->reserved[12], sizeof (guint));
265 : 519866 : return rv;
266 : : }
267 : :
268 : : gint
269 : 161571 : guid_g_hash_table_equal (gconstpointer guid_a, gconstpointer guid_b)
270 : : {
271 : 161571 : return guid_equal (reinterpret_cast<const GncGUID*> (guid_a),
272 : 161571 : reinterpret_cast<const GncGUID*> (guid_b));
273 : : }
274 : :
275 : : GHashTable *
276 : 5958 : guid_hash_table_new (void)
277 : : {
278 : 5958 : 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 : : const 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_set_string (dest, str);
311 : : }
312 : :
313 : 1175 : 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 : 139623 : GUID::create_random () noexcept
328 : : {
329 : : static boost::uuids::random_generator gen;
330 : 139623 : return {gen ()};
331 : : }
332 : :
333 : 139752 : GUID::GUID (boost::uuids::uuid const & other) noexcept
334 : 139752 : : implementation (other)
335 : : {
336 : 139752 : }
337 : :
338 : : GUID const &
339 : 0 : GUID::null_guid () noexcept
340 : : {
341 : 0 : return s_null_guid;
342 : : }
343 : :
344 : : std::string
345 : 803 : GUID::to_string () const noexcept
346 : : {
347 : 803 : std::string out;
348 : 803 : out.resize (implementation.size() * 2);
349 : 803 : fast_guid_to_string (implementation.data, out.data());
350 : 803 : 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 : 857 : GUID::GUID (GncGUID const & other) noexcept
396 : 857 : : implementation {{other.reserved[0] , other.reserved[1]
397 : 857 : , other.reserved[2], other.reserved[3]
398 : 857 : , other.reserved[4], other.reserved[5]
399 : 857 : , other.reserved[6], other.reserved[7]
400 : 857 : , other.reserved[8], other.reserved[9]
401 : 857 : , other.reserved[10], other.reserved[11]
402 : 857 : , other.reserved[12], other.reserved[13]
403 : 857 : , other.reserved[14], other.reserved[15]}
404 : : }
405 : : {
406 : :
407 : 857 : }
408 : :
409 : : auto
410 : 139762 : GUID::end () const noexcept -> decltype (implementation.end ())
411 : : {
412 : 139762 : return implementation.end ();
413 : : }
414 : :
415 : : auto
416 : 139762 : GUID::begin () const noexcept -> decltype (implementation.begin ())
417 : : {
418 : 139762 : 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 : 18 : bool operator == (GUID const & lhs, GncGUID const & rhs) noexcept
428 : : {
429 : 18 : 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 : 5194 : GUID::operator GncGUID () const noexcept
445 : : {
446 : : GncGUID ret;
447 : 5194 : guid_assign (ret, *this);
448 : 5194 : return ret;
449 : : }
450 : :
451 : : } // namespace gnc
452 : :
453 : : bool
454 : 18 : operator==(const GncGUID& lhs, const GncGUID& rhs)
455 : : {
456 : 18 : return gnc::GUID{lhs} == gnc::GUID{rhs};
457 : : }
|