From 215c9b7951d9bab08c276bb24b9e631bb22374ab Mon Sep 17 00:00:00 2001 From: Bastien Nocera Date: Mon, 23 Jan 2017 04:17:11 +0100 Subject: [PATCH] guuid: Add UUID helper functions to GLib MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Many UUID users will just need a random string, which can be generated simply by calling the function g_uuid_string_random(). Based on original patch by Marc-André Lureau https://bugzilla.gnome.org/show_bug.cgi?id=639078 --- docs/reference/glib/glib-docs.xml | 1 + docs/reference/glib/glib-sections.txt | 7 + glib/Makefile.am | 2 + glib/glib.h | 1 + glib/guuid.c | 231 ++++++++++++++++++++++++++ glib/guuid.h | 42 +++++ glib/tests/.gitignore | 1 + glib/tests/Makefile.am | 1 + glib/tests/guuid.c | 71 ++++++++ po/POTFILES.in | 1 + 10 files changed, 358 insertions(+) create mode 100644 glib/guuid.c create mode 100644 glib/guuid.h create mode 100644 glib/tests/guuid.c diff --git a/docs/reference/glib/glib-docs.xml b/docs/reference/glib/glib-docs.xml index f30e44b05..4f6795a75 100644 --- a/docs/reference/glib/glib-docs.xml +++ b/docs/reference/glib/glib-docs.xml @@ -94,6 +94,7 @@ + diff --git a/docs/reference/glib/glib-sections.txt b/docs/reference/glib/glib-sections.txt index 4745446d0..924d58f09 100644 --- a/docs/reference/glib/glib-sections.txt +++ b/docs/reference/glib/glib-sections.txt @@ -3403,3 +3403,10 @@ g_hostname_is_ascii_encoded g_hostname_is_ip_address + +
+uuid +GUuid +g_uuid_string_is_valid +g_uuid_string_random +
diff --git a/glib/Makefile.am b/glib/Makefile.am index 2aeaddd1b..87cca42f4 100644 --- a/glib/Makefile.am +++ b/glib/Makefile.am @@ -188,6 +188,7 @@ libglib_2_0_la_SOURCES = \ gunicodeprivate.h \ gurifuncs.c \ gutils.c \ + guuid.c \ gvariant.h \ gvariant.c \ gvariant-core.h \ @@ -309,6 +310,7 @@ glibsubinclude_HEADERS = \ gunicode.h \ gurifuncs.h \ gutils.h \ + guuid.h \ gvarianttype.h \ gvariant.h \ gversion.h \ diff --git a/glib/glib.h b/glib/glib.h index 1212d139f..9fd22298a 100644 --- a/glib/glib.h +++ b/glib/glib.h @@ -90,6 +90,7 @@ #include #include #include +#include #include #include #include diff --git a/glib/guuid.c b/glib/guuid.c new file mode 100644 index 000000000..0f48bfbe3 --- /dev/null +++ b/glib/guuid.c @@ -0,0 +1,231 @@ +/* guuid.c - UUID functions + * + * Copyright (C) 2013-2015, 2017 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * licence, or (at your option) any later version. + * + * This is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 + * USA. + * + * Authors: Marc-André Lureau + */ + +#include "config.h" +#include + +#include "gi18n.h" +#include "gstrfuncs.h" +#include "grand.h" +#include "gmessages.h" +#include "gchecksum.h" + +#include "guuid.h" + +typedef struct { + guint8 bytes[16]; +} GUuid; + +/** + * SECTION:uuid + * @title: GUuid + * @short_description: a universally unique identifier + * + * A UUID, or Universally unique identifier, is intended to uniquely + * identify information in a distributed environment. For the + * definition of UUID, see [RFC 4122](https://tools.ietf.org/html/rfc4122.html). + * + * The creation of UUIDs does not require a centralized authority. + * + * UUIDs are of relatively small size (128 bits, or 16 bytes). The + * common string representation (ex: + * 1d6c0810-2bd6-45f3-9890-0268422a6f14) needs 37 bytes. + * + * The UUID specification defines 5 versions, and calling + * g_uuid_string_random() will generate a unique (or rather random) + * UUID of the most common version, version 4. + * + * Since: 2.52 + */ + +/* + * g_uuid_to_string: + * @uuid: a #GUuid + * + * Creates a string representation of @uuid, of the form + * 06e023d5-86d8-420e-8103-383e4566087a (no braces nor urn:uuid: + * prefix). + * + * Returns: (transfer full): A string that should be freed with g_free(). + * Since: STATIC + */ +static gchar * +g_uuid_to_string (const GUuid *uuid) +{ + const guint8 *bytes; + + g_return_val_if_fail (uuid != NULL, NULL); + + bytes = uuid->bytes; + + return g_strdup_printf ("%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x" + "-%02x%02x%02x%02x%02x%02x", + bytes[0], bytes[1], bytes[2], bytes[3], + bytes[4], bytes[5], bytes[6], bytes[7], + bytes[8], bytes[9], bytes[10], bytes[11], + bytes[12], bytes[13], bytes[14], bytes[15]); +} + +static gboolean +uuid_parse_string (const gchar *str, + GUuid *uuid) +{ + GUuid tmp; + guint8 *bytes = tmp.bytes; + gint i, j, hi, lo; + guint expected_len = 36; + + if (g_str_has_prefix (str, "urn:uuid:")) + str += 9; + else if (g_str_has_prefix (str, "{urn:uuid:")) + expected_len += 11; + else if (str[0] == '{') + expected_len += 2; + + if (strlen (str) != expected_len) + return FALSE; + + if (str[0] == '{') + { + if (str[expected_len - 1] != '}') + return FALSE; + + str++; + } + + if (g_str_has_prefix (str, "urn:uuid:")) + str += 9; + + for (i = 0, j = 0; i < 16;) + { + if (j == 8 || j == 13 || j == 18 || j == 23) + { + if (str[j++] != '-') + return FALSE; + + continue; + } + + hi = g_ascii_xdigit_value (str[j++]); + lo = g_ascii_xdigit_value (str[j++]); + + if (hi == -1 || lo == -1) + return FALSE; + + bytes[i++] = hi << 8 | lo; + } + + if (uuid != NULL) + *uuid = tmp; + + return TRUE; +} + +/** + * g_uuid_string_is_valid: + * @str: a string representing a UUID + * + * Parses the string @str and verify if it is a UUID. + * + * The function accepts the following syntaxes: + * + * - simple forms (e.g. `f81d4fae-7dec-11d0-a765-00a0c91e6bf6`) + * - simple forms with curly braces (e.g. + * `{urn:uuid:f81d4fae-7dec-11d0-a765-00a0c91e6bf6}`) + * - URN (e.g. `urn:uuid:f81d4fae-7dec-11d0-a765-00a0c91e6bf6`) + * + * Note that hyphens are required within the UUID string itself, + * as per the aforementioned RFC. + * + * Returns: %TRUE if @str is a valid UUID, %FALSE otherwise. + * Since: 2.52 + */ +gboolean +g_uuid_string_is_valid (const gchar *str) +{ + g_return_val_if_fail (str != NULL, FALSE); + + return uuid_parse_string (str, NULL); +} + +static void +uuid_set_version (GUuid *uuid, guint version) +{ + guint8 *bytes = uuid->bytes; + + /* + * Set the four most significant bits (bits 12 through 15) of the + * time_hi_and_version field to the 4-bit version number from + * Section 4.1.3. + */ + bytes[6] &= 0x0f; + bytes[6] |= version << 4; + /* + * Set the two most significant bits (bits 6 and 7) of the + * clock_seq_hi_and_reserved to zero and one, respectively. + */ + bytes[8] &= 0x3f; + bytes[8] |= 0x80; +} + +/* + * g_uuid_generate_v4: + * @uuid: a #GUuid + * + * Generates a random UUID (RFC 4122 version 4). + * Since: STATIC + */ +static void +g_uuid_generate_v4 (GUuid *uuid) +{ + int i; + guint8 *bytes; + guint32 *ints; + + g_return_if_fail (uuid != NULL); + + bytes = uuid->bytes; + ints = (guint32 *) bytes; + for (i = 0; i < 4; i++) + ints[i] = g_random_int (); + + uuid_set_version (uuid, 4); +} + +/** + * g_uuid_string_random: + * + * Generates a random UUID (RFC 4122 version 4) as a string. + * + * Returns: (transfer full): A string that should be freed with g_free(). + * Since: 2.52 + */ +gchar * +g_uuid_string_random (void) +{ + GUuid uuid; + + g_uuid_generate_v4 (&uuid); + + return g_uuid_to_string (&uuid); +} + diff --git a/glib/guuid.h b/glib/guuid.h new file mode 100644 index 000000000..c653188a0 --- /dev/null +++ b/glib/guuid.h @@ -0,0 +1,42 @@ +/* guuid.h - UUID functions + * + * Copyright (C) 2013-2015, 2017 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * licence, or (at your option) any later version. + * + * This is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 + * USA. + * + * Authors: Marc-André Lureau + */ + +#ifndef __G_UUID_H__ +#define __G_UUID_H__ + +#if !defined (__GLIB_H_INSIDE__) && !defined (GLIB_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +G_BEGIN_DECLS + +GLIB_AVAILABLE_IN_2_52 +gboolean g_uuid_string_is_valid (const gchar *str); + +GLIB_AVAILABLE_IN_2_52 +gchar * g_uuid_string_random (void); + +G_END_DECLS + +#endif /* __G_UUID_H__ */ diff --git a/glib/tests/.gitignore b/glib/tests/.gitignore index 03aee70d7..9e4c147d3 100644 --- a/glib/tests/.gitignore +++ b/glib/tests/.gitignore @@ -22,6 +22,7 @@ environment error fileutils gdatetime +guuid gvariant gwakeup gwakeup-fallback diff --git a/glib/tests/Makefile.am b/glib/tests/Makefile.am index a6bcef0e9..43b11f05d 100644 --- a/glib/tests/Makefile.am +++ b/glib/tests/Makefile.am @@ -59,6 +59,7 @@ test_programs = \ error \ fileutils \ gdatetime \ + guuid \ gvariant \ hash \ hmac \ diff --git a/glib/tests/guuid.c b/glib/tests/guuid.c new file mode 100644 index 000000000..437749f0e --- /dev/null +++ b/glib/tests/guuid.c @@ -0,0 +1,71 @@ +/* guuid.c + * + * Copyright (C) 2013-2015, 2017 Red Hat, Inc. + * + * This is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" + +#undef G_DISABLE_ASSERT + +#include +#include + +static void +test_guuid_string (void) +{ + g_assert_false (g_uuid_string_is_valid ("00010203-0405-0607-0809")); + g_assert_false (g_uuid_string_is_valid ("zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz")); + g_assert_false (g_uuid_string_is_valid ("000102030405060708090a0b0c0d0e0f")); + + g_assert_true (g_uuid_string_is_valid ("00010203-0405-0607-0809-0a0b0c0d0e0f")); + g_assert_true (g_uuid_string_is_valid ("7d444840-9dc0-11d1-b245-5ffdce74fad2")); + g_assert_true (g_uuid_string_is_valid ("e902893a-9d22-3c7e-a7b8-d6e313b71d9f")); + g_assert_true (g_uuid_string_is_valid ("6ba7b810-9dad-11d1-80b4-00c04fd430c8")); + g_assert_true (g_uuid_string_is_valid ("{urn:uuid:f81d4fae-7dec-11d0-a765-00a0c91e6bf6}")); + g_assert_true (g_uuid_string_is_valid ("urn:uuid:f81d4fae-7dec-11d0-a765-00a0c91e6bf6")); +} + +static void +test_guuid_random (void) +{ + gchar *str1, *str2; + + str1 = g_uuid_string_random (); + g_assert_cmpuint (strlen (str1), ==, 36); + g_assert_true (g_uuid_string_is_valid (str1)); + + str2 = g_uuid_string_random (); + g_assert_cmpuint (strlen (str2), ==, 36); + g_assert_true (g_uuid_string_is_valid (str2)); + g_assert_cmpstr (str1, !=, str2); + + g_free (str1); + g_free (str2); +} + +int +main (int argc, char **argv) +{ + g_test_init (&argc, &argv, NULL); + g_test_bug_base ("http://bugzilla.gnome.org/"); + + /* GUuid Tests */ + g_test_add_func ("/uuid/string", test_guuid_string); + g_test_add_func ("/uuid/random", test_guuid_random); + + return g_test_run (); +} diff --git a/po/POTFILES.in b/po/POTFILES.in index a549f0546..4cb4150c5 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -195,4 +195,5 @@ glib/gspawn.c glib/gspawn-win32.c glib/gutf8.c glib/gutils.c +glib/guuid.c gobject/gbinding.c