summaryrefslogtreecommitdiffstats
path: root/src/plugins/utilities/uuid/UUIDGeneration.cpp
blob: bb03fc6a61165d2298e819d9968ccaf6e7ad42bb (plain)
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
129
130
131
132
133
134
135
136
137
/*
 * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 *     http://aws.amazon.com/apache2.0/
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */
// This code is copied from https://github.com/alexa/avs-device-sdk/
// Minor modifications are made to the original file.

#include <random>
#include <chrono>
#include <sstream>
#include <iomanip>
#include <mutex>
#include <climits>
#include <algorithm>
#include <functional>

#include "utilities/uuid/UUIDGeneration.h"

namespace vshl {
namespace utilities {
namespace uuid {

/// String to identify log entries originating from this file.
static const std::string TAG("UUIDGeneration");

/// The UUID version (Version 4), shifted into the correct position in the byte.
static const uint8_t UUID_VERSION_VALUE = 4 << 4;

/// The UUID variant (Variant 1), shifted into the correct position in the byte.
static const size_t UUID_VARIANT_VALUE = 2 << 6;

/// Separator used between UUID fields.
static const std::string SEPARATOR("-");

/// Number of bits in the replacement value.
static const size_t MAX_NUM_REPLACEMENT_BITS = CHAR_BIT;

/// Number of bits in a hex digit.
static const size_t BITS_IN_HEX_DIGIT = 4;

/**
 * Randomly generate a string of hex digits. Before the conversion of hex to string,
 * the function allows replacement of the bits of the first two generated hex digits.
 * Replacement happens starting at the most significant, to the least significant bit.
 *
 * @param ibe A random number generator.
 * @param numDigits The number of hex digits [0-9],[a-f] to generate.
 * @param replacementBits The replacement value for up to the first two digits generated.
 * @param numReplacementBits The number of bits of @c replacementBits to use.
 *
 * @return A hex string of length @c numDigits.
 */
static const std::string generateHexWithReplacement(
    std::independent_bits_engine<std::default_random_engine, CHAR_BIT, uint8_t>& ibe,
    unsigned int numDigits,
    uint8_t replacementBits,
    unsigned short numReplacementBits) {
    if (numReplacementBits > MAX_NUM_REPLACEMENT_BITS) {
        return "";
    }

    if (numReplacementBits > (numDigits * BITS_IN_HEX_DIGIT)) {
        return "";
    }

    // Makes assumption that 1 digit = 4 bits.
    std::vector<uint8_t> bytes(ceil(numDigits / 2.0));
    std::generate(bytes.begin(), bytes.end(), std::ref(ibe));

    // Replace the specified number of bits from the first byte.
    bytes.at(0) &= (0xff >> numReplacementBits);
    replacementBits &= (0xff << (MAX_NUM_REPLACEMENT_BITS - numReplacementBits));
    bytes.at(0) |= replacementBits;

    std::ostringstream oss;
    for (const auto& byte : bytes) {
        oss << std::hex << std::setfill('0') << std::setw(2) << static_cast<int>(byte);
    }

    std::string bytesText = oss.str();
    // Remove the last digit for odd numDigits case.
    bytesText.resize(numDigits);

    return bytesText;
}

/**
 * Randomly generate a string of hex digits.
 *
 * @param ibe A random number generator.
 * @param numDigits The number of hex digits [0-9],[a-f] to generate.
 *
 * @return A hex string of length @c numDigits.
 */
static const std::string generateHex(
    std::independent_bits_engine<std::default_random_engine, CHAR_BIT, uint8_t>& ibe,
    unsigned int numDigits) {
    return generateHexWithReplacement(ibe, numDigits, 0, 0);
}

const std::string generateUUID() {
    static bool seeded = false;
    static std::independent_bits_engine<std::default_random_engine, CHAR_BIT, uint8_t> ibe;
    static std::mutex mutex;
    std::unique_lock<std::mutex> lock(mutex);
    if (!seeded) {
        std::random_device rd;
        ibe.seed(
            rd() +
            std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now().time_since_epoch())
                .count());
        seeded = true;
    }

    std::ostringstream uuidText;
    uuidText << generateHex(ibe, 8) << SEPARATOR << generateHex(ibe, 4) << SEPARATOR
             << generateHexWithReplacement(ibe, 4, UUID_VERSION_VALUE, 4) << SEPARATOR
             << generateHexWithReplacement(ibe, 4, UUID_VARIANT_VALUE, 2) << SEPARATOR << generateHex(ibe, 12);

    lock.unlock();

    return uuidText.str();
}

}  // namespace uuid
}  // namespace utilities
}  // namespace vshl