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 /* Memory management routines ***************************************/
91 
96 GncGUID *
97 guid_convert_create (gnc::GUID const & guid)
98 {
99  GncGUID temp = guid;
100  return guid_copy (&temp);
101 }
102 
103 GncGUID *
105 {
106  return new GncGUID;
107 }
108 
109 void
110 guid_free (GncGUID *guid)
111 {
112  if (!guid) return;
113  if (guid == s_null_gncguid)
114  /* Don't delete that! */
115  return;
116  delete guid;
117 }
118 
119 GncGUID *
120 guid_copy (const GncGUID *guid)
121 {
122  if (!guid) return nullptr;
123  auto ret = guid_malloc ();
124  *ret = *guid;
125  return ret;
126 }
127 
128 /*It looks like we are expected to provide the same pointer every time from this function*/
129 const GncGUID *
130 guid_null (void)
131 {
132  return s_null_gncguid;
133 }
134 
135 static void
136 guid_assign (GncGUID & target, gnc::GUID const & source)
137 {
138  std::copy (source.begin(), source.end(), target.reserved);
139 }
140 
141 /*Takes an allocated guid pointer and constructs it in place*/
142 void
144 {
145  if (!guid) return;
146  gnc::GUID temp_random {gnc::GUID::create_random ()};
147  guid_assign (*guid, temp_random);
148 }
149 
150 GncGUID *
151 guid_new (void)
152 {
153  auto ret = guid_new_return ();
154  return guid_copy (&ret);
155 }
156 
157 GncGUID
159 {
160  return gnc::GUID::create_random ();
161 }
162 
163 gchar *
164 guid_to_string (const GncGUID * guid)
165 {
166  if (!guid) return nullptr;
167  gnc::GUID temp {*guid};
168  auto temp_str = temp.to_string ();
169  return g_strdup (temp_str.c_str ());
170 }
171 
172 gchar *
173 guid_to_string_buff (const GncGUID * guid, gchar *str)
174 {
175  if (!str || !guid) return nullptr;
176 
177  gnc::GUID temp {*guid};
178  auto val = temp.to_string ();
179  /*We need to be sure to copy the terminating null character.
180  * The standard guarantees that std::basic_string::c_str ()
181  * returns with a terminating null character, too.*/
182  std::copy (val.c_str (), val.c_str () + val.size () + 1, str);
183  return str + val.size ();
184 }
185 
186 gboolean
187 string_to_guid (const char * str, GncGUID * guid)
188 {
189  if (!guid || !str || !*str) return false;
190 
191  try
192  {
193  guid_assign (*guid, gnc::GUID::from_string (str));
194  }
195  catch (...)
196  {
197  PINFO("Failed to construct a GUID from %s", str);
198  return false;
199  }
200  return true;
201 }
202 
203 gboolean
204 guid_equal (const GncGUID *guid_1, const GncGUID *guid_2)
205 {
206  if (!guid_1 || !guid_2)
207  return !guid_1 && !guid_2;
208  gnc::GUID temp1 {*guid_1};
209  gnc::GUID temp2 {*guid_2};
210  return temp1 == temp2;
211 }
212 
213 gint
214 guid_compare (const GncGUID *guid_1, const GncGUID *guid_2)
215 {
216  if (!guid_1 || !guid_2)
217  return !guid_1 && !guid_2;
218  gnc::GUID temp1 {*guid_1};
219  gnc::GUID temp2 {*guid_2};
220  if (temp1 < temp2)
221  return -1;
222  if (temp1 == temp2)
223  return 0;
224  return 1;
225 }
226 
227 guint
228 guid_hash_to_guint (gconstpointer ptr)
229 {
230  if (!ptr)
231  {
232  PERR ("received nullptr guid pointer.");
233  return 0;
234  }
235  GncGUID const & guid = * reinterpret_cast <GncGUID const *> (ptr);
236  gnc::GUID const & temp {guid};
237 
238  guint hash {0};
239  std::for_each (temp.begin (), temp.end (), [&hash] (unsigned char a) {
240  hash <<=4;
241  hash |= a;
242  });
243  return hash;
244 }
245 
246 gint
247 guid_g_hash_table_equal (gconstpointer guid_a, gconstpointer guid_b)
248 {
249  return guid_equal (reinterpret_cast<const GncGUID*> (guid_a),
250  reinterpret_cast<const GncGUID*> (guid_b));
251 }
252 
253 GHashTable *
255 {
256  return g_hash_table_new (guid_hash_to_guint, guid_g_hash_table_equal);
257 }
258 
259 /***************************/
260 static void
261 gnc_string_to_guid (const GValue *src, GValue *dest)
262 {
263  /* FIXME: add more checks*/
264  GncGUID *guid;
265  const gchar *as_string;
266 
267  g_return_if_fail (G_VALUE_HOLDS_STRING (src) &&
268  GNC_VALUE_HOLDS_GUID (dest));
269 
270  as_string = g_value_get_string (src);
271 
272  guid = g_new0 (GncGUID, 1);
273  string_to_guid (as_string, guid);
274 
275  g_value_take_boxed (dest, guid);
276 }
277 
278 static void
279 gnc_guid_to_string (const GValue *src, GValue *dest)
280 {
281  const gchar *str;
282 
283  g_return_if_fail (G_VALUE_HOLDS_STRING (dest) &&
284  GNC_VALUE_HOLDS_GUID (src));
285 
286  str = guid_to_string (gnc_value_get_guid (src));
287 
288  g_value_set_string (dest, str);
289 }
290 
291 G_DEFINE_BOXED_TYPE_WITH_CODE (GncGUID, gnc_guid, guid_copy, guid_free,
292  g_value_register_transform_func (G_TYPE_STRING,
293  g_define_type_id,
294  gnc_string_to_guid);
295 
296  g_value_register_transform_func (g_define_type_id,
297  G_TYPE_STRING,
298  gnc_guid_to_string);
299  )
300 
301 namespace gnc
302 {
303 
304 GUID
305 GUID::create_random () noexcept
306 {
307  static boost::uuids::random_generator gen;
308  return {gen ()};
309 }
310 
311 GUID::GUID (boost::uuids::uuid const & other) noexcept
312  : implementation (other)
313 {
314 }
315 
316 GUID const &
317 GUID::null_guid () noexcept
318 {
319  return s_null_guid;
320 }
321 
322 std::string
323 GUID::to_string () const noexcept
324 {
325  auto const & val = boost::uuids::to_string (implementation);
326  std::string ret;
327  std::for_each (val.begin (), val.end (), [&ret] (char a) {
328  if (a != '-') ret.push_back (a);
329  });
330  return ret;
331 }
332 
333 GUID
334 GUID::from_string (const char* str)
335 {
336  if (!str)
337  throw guid_syntax_exception {};
338  try
339  {
340  static boost::uuids::string_generator strgen;
341  return strgen (str);
342  }
343  catch (...)
344  {
345  throw guid_syntax_exception {};
346  }
347 }
348 
349 bool
350 GUID::is_valid_guid (const char* str)
351 {
352  try
353  {
354  static boost::uuids::string_generator strgen;
355  strgen (str);
356  return true;
357  }
358  catch (...)
359  {
360  return false;
361  }
362 }
363 
364 guid_syntax_exception::guid_syntax_exception () noexcept
365  : invalid_argument {"Invalid syntax for guid."}
366 {
367 }
368 
369 GUID::GUID (GncGUID const & other) noexcept
370 : implementation {{other.reserved[0] , other.reserved[1]
371  , other.reserved[2], other.reserved[3]
372  , other.reserved[4], other.reserved[5]
373  , other.reserved[6], other.reserved[7]
374  , other.reserved[8], other.reserved[9]
375  , other.reserved[10], other.reserved[11]
376  , other.reserved[12], other.reserved[13]
377  , other.reserved[14], other.reserved[15]}
378  }
379 {
380 
381 }
382 
383 auto
384 GUID::end () const noexcept -> decltype (implementation.end ())
385 {
386  return implementation.end ();
387 }
388 
389 auto
390 GUID::begin () const noexcept -> decltype (implementation.begin ())
391 {
392  return implementation.begin ();
393 }
394 
395 bool
396 GUID::operator < (GUID const & other) noexcept
397 {
398  return implementation < other.implementation;
399 }
400 
401 bool operator == (GUID const & lhs, GncGUID const & rhs) noexcept
402 {
403  return lhs.implementation == GUID(rhs).implementation;
404 }
405 
406 bool
407 operator != (GUID const & one, GUID const & two) noexcept
408 {
409  return one.implementation != two.implementation;
410 }
411 
412 GUID & GUID::operator = (GUID && other) noexcept
413 {
414  boost::uuids::swap (other.implementation, implementation);
415  return *this;
416 }
417 
418 GUID::operator GncGUID () const noexcept
419 {
420  GncGUID ret;
421  guid_assign (ret, *this);
422  return ret;
423 }
424 
425 } // namespace gnc
426 
427 bool
428 operator==(const GncGUID& lhs, const GncGUID& rhs)
429 {
430  return gnc::GUID{lhs} == gnc::GUID{rhs};
431 }
void guid_replace(GncGUID *guid)
Generate a new guid.
Definition: guid.cpp:143
#define PINFO(format, args...)
Print an informational note.
Definition: qoflog.h:256
GncGUID guid_new_return(void)
Generate a new id.
Definition: guid.cpp:158
GncGUID * guid_copy(const GncGUID *guid)
Returns a newly allocated GncGUID that matches the passed-in GUID.
Definition: guid.cpp:120
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:151
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:254
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:173
guint guid_hash_to_guint(gconstpointer ptr)
Hash function for a GUID.
Definition: guid.cpp:228
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:104
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:204
gchar * guid_to_string(const GncGUID *guid)
The guid_to_string() routine returns a null-terminated string encoding of the id. ...
Definition: guid.cpp:164
const GncGUID * guid_null(void)
Returns a GncGUID which is guaranteed to never reference any entity.
Definition: guid.cpp:130
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:247