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
16static const char* const kHexChars = "0123456789abcdef";
17
18static const size_t kVersionByteIndex = 6;
19static const size_t kVariantByteIndex = 8;
20
21
22static bool
23init_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
40namespace BPrivate {
41
42BUuid::BUuid()
43{
44	memset(fValue, 0, sizeof(fValue));
45}
46
47
48BUuid::BUuid(const BUuid& other)
49{
50	memcpy(fValue, other.fValue, sizeof(fValue));
51}
52
53
54BUuid::~BUuid()
55{
56}
57
58
59bool
60BUuid::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
71BUuid&
72BUuid::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
88BString
89BUuid::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
102int
103BUuid::Compare(const BUuid& other) const
104{
105	return memcmp(fValue, other.fValue, sizeof(fValue));
106}
107
108
109BUuid&
110BUuid::operator=(const BUuid& other)
111{
112	memcpy(fValue, other.fValue, sizeof(fValue));
113
114	return *this;
115}
116
117
118bool
119BUuid::_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
137void
138BUuid::_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
161using BPrivate::BUuid;
162