xref: /haiku/src/kits/support/Uuid.cpp (revision 529cd177b573aaba391c8adc9c9f5ad76a14bf81)
1 /*
2  * Copyright 2013 Haiku, Inc. All rights reserved.
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 
36 	return true;
37 }
38 
39 
40 namespace BPrivate {
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 
97 	return BString().SetToFormat("%.8s-%.4s-%.4s-%.4s-%.12s",
98 		buffer, buffer + 8, buffer + 12, buffer + 16, buffer + 20);
99 }
100 
101 
102 int
103 BUuid::Compare(const BUuid& other) const
104 {
105 	return memcmp(fValue, other.fValue, sizeof(fValue));
106 }
107 
108 
109 BUuid&
110 BUuid::operator=(const BUuid& other)
111 {
112 	memcpy(fValue, other.fValue, sizeof(fValue));
113 
114 	return *this;
115 }
116 
117 
118 bool
119 BUuid::_SetToDevRandom()
120 {
121 	// open device
122 	int fd = open("/dev/urandom", O_RDONLY);
123 	if (fd < 0) {
124 		fd = open("/dev/random", O_RDONLY);
125 		if (fd < 0)
126 			return false;
127 	}
128 
129 	// read bytes
130 	ssize_t bytesRead = read(fd, fValue, sizeof(fValue));
131 	close(fd);
132 
133 	return bytesRead == (ssize_t)sizeof(fValue);
134 }
135 
136 
137 void
138 BUuid::_SetToRandomFallback()
139 {
140 	static bool sSeedInitialized = init_random_seed();
141 	(void)sSeedInitialized;
142 
143 	for (int32 i = 0; i < 4; i++) {
144 		uint32 value = random();
145 		fValue[4 * i + 0] = uint8(value >> 24);
146 		fValue[4 * i + 1] = uint8(value >> 16);
147 		fValue[4 * i + 2] = uint8(value >> 8);
148 		fValue[4 * i + 3] = uint8(value);
149 	}
150 
151 	// random() returns 31 bit numbers only, so we move a few bits from where
152 	// we overwrite them with the version anyway.
153 	uint8 bitsToMove = fValue[kVersionByteIndex];
154 	for (int32 i = 0; i < 4; i++)
155 		fValue[4 * i] |= (bitsToMove << i) & 0x80;
156 }
157 
158 }	// namespace BPrivate
159 
160 
161 using BPrivate::BUuid;
162