aboutsummaryrefslogtreecommitdiffstats
path: root/src/uuid/uuid.c
blob: 0dfcfbd55670fa17c23de0569b9c87b8783b4028 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#include "uuid.h"

#include <inttypes.h>
#include <stdio.h>
#include <string.h>
#include <sys/random.h> /* not very portable but idc */

bool l2__str_to_uint64(uint64_t *out, const char *str);

void l2_uuid__set_version(uuid_t *id, unsigned version)
{
  version &= 0x0F;

  id->uuid_ms &= ~(UINT64_C(0x0F) << 12);
  id->uuid_ms |= (uint64_t)version << 12;
}

void l2_uuid__set_variant(uuid_t *id, unsigned variant, unsigned bits)
{
  uint64_t mask = (UINT64_C(1) << (64 - bits)) - 1;
  uint64_t lvariant = (uint64_t)variant << (64 - bits);
  lvariant &= ~mask;

  id->uuid_ls &= mask;
  id->uuid_ls |= lvariant;
}

void l2_uuid_random(uuid_t *id)
{
  getentropy(id, sizeof(uuid_t));
  l2_uuid__set_version(id, 4);
  l2_uuid__set_variant(id, 2, 2);
}

void l2_uuid_to_string(const uuid_t *id, char *out)
{
  snprintf(out, UUID_STRLEN + 1, "%08" PRIx64 "-%04" PRIx64 "-%04" PRIx64 "-%04" PRIx64 "-%012" PRIx64,
           id->uuid_ms >> 32,                       /* time-low */
           id->uuid_ms >> 16 & 0xffff,              /* time-mid */
           id->uuid_ms & 0xffff,                    /* time-high-and-version */
           id->uuid_ls >> 48,                       /* clock-seq-and-reserved, clock-seq-low */
           id->uuid_ls & UINT64_C(0xffffffffffff)); /* node */
}

void l2_uuid_to_string_short(const uuid_t *id, char *out)
{
  snprintf(out, UUID_STRLEN_SHORT + 1, "%08" PRIx64 "%08" PRIx64, id->halves[1], id->halves[0]);
}

bool l2_uuid_from_string(uuid_t *id, const char *str)
{
  size_t slen = strlen(str);
  char rbuf[UUID_STRLEN_SHORT + 1];

  if (slen != UUID_STRLEN) return false;

  memcpy(rbuf, str, 8);
  memcpy(rbuf + 8, str + 9, 4);
  memcpy(rbuf + 12, str + 14, 4);
  memcpy(rbuf + 16, str + 19, 4);
  memcpy(rbuf + 20, str + 24, 12);
  rbuf[UUID_STRLEN_SHORT] = '\0';

  return l2_uuid_from_string_short(id, rbuf);
}

bool l2_uuid_from_string_short(uuid_t *id, const char *str)
{
  size_t slen = strlen(str);
  uuid_t outid;

  if (slen != UUID_STRLEN_SHORT) return false;

  if (!l2__str_to_uint64(outid.halves + 1, str)) return false;
  if (!l2__str_to_uint64(outid.halves, str + 16)) return false;

  memcpy(id, &outid, sizeof(uuid_t));
  return true;
}

int l2_uuid_compare(const uuid_t *c1, const uuid_t *c2)
{
  return memcmp(c1, c2, sizeof(uuid_t));
}

/* This function exists because there's not a portable way to do this.
 * Don't suggest strtoull, because uint64_t may not be a ulonglong. */
bool l2__str_to_uint64(uint64_t *out, const char *str)
{
  *out = 0;
  str += 15;
  for (unsigned i = 0; i < 16; ++i, --str) {
    switch (*str) {
      case '0':
      case '1':
      case '2':
      case '3':
      case '4':
      case '5':
      case '6':
      case '7':
      case '8':
      case '9':
        *out |= (uint64_t)(*str - '0') << (i * 4);
        break;
      case 'a':
      case 'b':
      case 'c':
      case 'd':
      case 'e':
      case 'f':
        *out |= (uint64_t)(*str - 'a' + 10) << (i * 4);
        break;
      case 'A':
      case 'B':
      case 'C':
      case 'D':
      case 'E':
      case 'F':
        *out |= (uint64_t)(*str - 'A' + 10) << (i * 4);
        break;
      default:
        return false;
    }
  }

  return true;
}