xref: /haiku/src/kits/support/Uuid.cpp (revision 4b7e219688450694efc9d1890f83f816758c16d3)
1 /*
2  * Copyright 2013, Haiku, Inc.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include <Uuid.h>
8 
9 #include <fcntl.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <time.h>
13 #include <unistd.h>
14 
15 
16 static const char* const kHexChars = "0123456789abcdef";
17 
18 static const size_t kVersionByteIndex = 6;
19 static const size_t kVariantByteIndex = 8;
20 
21 
22 static bool
23 init_random_seed()
24 {
25 	// set a time-based seed
26 	timespec time;
27 	clock_gettime(CLOCK_REALTIME, &time);
28 	uint32 seed = (uint32)time.tv_sec ^ (uint32)time.tv_nsec;
29 
30 	// factor in a stack address -- with address space layout randomization
31 	// that adds a bit of additional randomness
32 	seed ^= (uint32)(addr_t)&time;
33 
34 	srandom(seed);
35 	return true;
36 }
37 
38 
39 namespace BPrivate {
40 
41 
42 BUuid::BUuid()
43 {
44 	memset(fValue, 0, sizeof(fValue));
45 }
46 
47 
48 BUuid::BUuid(const BUuid& other)
49 {
50 	memcpy(fValue, other.fValue, sizeof(fValue));
51 }
52 
53 
54 BUuid::~BUuid()
55 {
56 }
57 
58 
59 bool
60 BUuid::IsNil() const
61 {
62 	for (size_t i = 0; i < sizeof(fValue); i++) {
63 		if (fValue[i] != 0)
64 			return false;
65 	}
66 
67 	return true;
68 }
69 
70 
71 BUuid&
72 BUuid::SetToRandom()
73 {
74 	if (!BUuid::_SetToDevRandom())
75 		BUuid::_SetToRandomFallback();
76 
77 	// set variant and version
78 	fValue[kVariantByteIndex] &= 0x3f;
79 	fValue[kVariantByteIndex] |= 0x80;
80 	fValue[kVersionByteIndex] &= 0x0f;
81 	fValue[kVersionByteIndex] |= 4 << 4;
82 		// version 4
83 
84 	return *this;
85 }
86 
87 
88 BString
89 BUuid::ToString() const
90 {
91 	char buffer[32];
92 	for (size_t i = 0; i < 16; i++) {
93 		buffer[2 * i] = kHexChars[fValue[i] >> 4];
94 		buffer[2 * i + 1] = kHexChars[fValue[i] & 0xf];
95 	}
96 	return BString().SetToFormat("%.8s-%.4s-%.4s-%.4s-%.12s",
97 		buffer, buffer + 8, buffer + 12, buffer + 16, buffer + 20);
98 }
99 
100 
101 int
102 BUuid::Compare(const BUuid& other) const
103 {
104 	return memcmp(fValue, other.fValue, sizeof(fValue));
105 }
106 
107 
108 BUuid&
109 BUuid::operator=(const BUuid& other)
110 {
111 	memcpy(fValue, other.fValue, sizeof(fValue));
112 	return *this;
113 }
114 
115 
116 bool
117 BUuid::_SetToDevRandom()
118 {
119 	// open device
120 	int fd = open("/dev/urandom", O_RDONLY);
121 	if (fd < 0) {
122 		fd = open("/dev/random", O_RDONLY);
123 		if (fd < 0)
124 			return false;
125 	}
126 
127 	// read bytes
128 	ssize_t bytesRead = read(fd, fValue, sizeof(fValue));
129 	close(fd);
130 
131 	return bytesRead == (ssize_t)sizeof(fValue);
132 }
133 
134 
135 void
136 BUuid::_SetToRandomFallback()
137 {
138 	static bool sSeedInitialized = init_random_seed();
139 	(void)sSeedInitialized;
140 
141 	for (int32 i = 0; i < 4; i++) {
142 		uint32 value = random();
143 		fValue[4 * i + 0] = uint8(value >> 24);
144 		fValue[4 * i + 1] = uint8(value >> 16);
145 		fValue[4 * i + 2] = uint8(value >> 8);
146 		fValue[4 * i + 3] = uint8(value);
147 	}
148 
149 	// random() returns 31 bit numbers only, so we move a few bits from where
150 	// we overwrite them with the version anyway.
151 	uint8 bitsToMove = fValue[kVersionByteIndex];
152 	for (int32 i = 0; i < 4; i++)
153 		fValue[4 * i] |= (bitsToMove << i) & 0x80;
154 }
155 
156 
157 }	// namespace BPrivate
158 
159 
160 using BPrivate::BUuid;
161