summaryrefslogtreecommitdiffstats
path: root/libnetdata/uuid/uuid.c
blob: 55b66db9b3b06f16489092a4f310ef1cb6dca604 (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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
// SPDX-License-Identifier: GPL-3.0-or-later

#include "../libnetdata.h"

void uuid_unparse_lower_compact(const uuid_t uuid, char *out) {
    static const char *hex_chars = "0123456789abcdef";
    for (int i = 0; i < 16; i++) {
        out[i * 2] = hex_chars[(uuid[i] >> 4) & 0x0F];
        out[i * 2 + 1] = hex_chars[uuid[i] & 0x0F];
    }
    out[32] = '\0'; // Null-terminate the string
}

inline int uuid_parse_compact(const char *in, uuid_t uuid) {
    if (strlen(in) != 32)
        return -1; // Invalid input length

    for (int i = 0; i < 16; i++) {
        int high = hex_char_to_int(in[i * 2]);
        int low = hex_char_to_int(in[i * 2 + 1]);

        if (high < 0 || low < 0)
            return -1; // Invalid hexadecimal character

        uuid[i] = (high << 4) | low;
    }

    return 0; // Success
}

int uuid_parse_flexi(const char *in, uuid_t uu) {
    if(!in || !*in)
        return -1;

    size_t hexCharCount = 0;
    size_t hyphenCount = 0;
    const char *s = in;
    int byteIndex = 0;
    uuid_t uuid; // work on a temporary place, to not corrupt the previous value of uu if we fail

    while (*s && byteIndex < 16) {
        if (*s == '-') {
            s++;
            hyphenCount++;

            if (unlikely(hyphenCount > 4))
                // Too many hyphens
                return -2;
        }

        if (likely(isxdigit(*s))) {
            int high = hex_char_to_int(*s++);
            hexCharCount++;

            if (likely(isxdigit(*s))) {
                int low = hex_char_to_int(*s++);
                hexCharCount++;

                uuid[byteIndex++] = (high << 4) | low;
            }
            else
                // Not a valid UUID (expected a pair of hex digits)
                return -3;
        }
        else
            // Not a valid UUID
            return -4;
    }

    if (unlikely(byteIndex < 16))
        // Not enough data to form a UUID
        return -5;

    if (unlikely(hexCharCount != 32))
        // wrong number of hex digits
        return -6;

    if(unlikely(hyphenCount != 0 && hyphenCount != 4))
        // wrong number of hyphens
        return -7;

    // copy the final value
    memcpy(uu, uuid, sizeof(uuid_t));

    return 0;
}


// ----------------------------------------------------------------------------
// unit test

static inline void remove_hyphens(const char *uuid_with_hyphens, char *uuid_without_hyphens) {
    while (*uuid_with_hyphens) {
        if (*uuid_with_hyphens != '-') {
            *uuid_without_hyphens++ = *uuid_with_hyphens;
        }
        uuid_with_hyphens++;
    }
    *uuid_without_hyphens = '\0';
}

int uuid_unittest(void) {
    const int num_tests = 100000;
    int failed_tests = 0;

    int i;
    for (i = 0; i < num_tests; i++) {
        uuid_t original_uuid, parsed_uuid;
        char uuid_str_with_hyphens[UUID_STR_LEN], uuid_str_without_hyphens[UUID_COMPACT_STR_LEN];

        // Generate a random UUID
        switch(i % 2) {
            case 0:
                uuid_generate(original_uuid);
                break;

            case 1:
                uuid_generate_random(original_uuid);
                break;
        }

        // Unparse it with hyphens
        bool lower = false;
        switch(i % 3) {
            case 0:
                uuid_unparse_lower(original_uuid, uuid_str_with_hyphens);
                lower = true;
                break;

            case 1:
                uuid_unparse(original_uuid, uuid_str_with_hyphens);
                break;

            case 2:
                uuid_unparse_upper(original_uuid, uuid_str_with_hyphens);
                break;
        }

        // Remove the hyphens
        remove_hyphens(uuid_str_with_hyphens, uuid_str_without_hyphens);

        if(lower) {
            char test[UUID_COMPACT_STR_LEN];
            uuid_unparse_lower_compact(original_uuid, test);
            if(strcmp(test, uuid_str_without_hyphens) != 0) {
                printf("uuid_unparse_lower_compact() failed, expected '%s', got '%s'\n",
                       uuid_str_without_hyphens, test);
                failed_tests++;
            }
        }

        // Parse the UUID string with hyphens
        int parse_result = uuid_parse_flexi(uuid_str_with_hyphens, parsed_uuid);
        if (parse_result != 0) {
            printf("uuid_parse_flexi() returned -1 (parsing error) for UUID with hyphens: %s\n", uuid_str_with_hyphens);
            failed_tests++;
        } else if (uuid_compare(original_uuid, parsed_uuid) != 0) {
            printf("uuid_parse_flexi() parsed value mismatch for UUID with hyphens: %s\n", uuid_str_with_hyphens);
            failed_tests++;
        }

        // Parse the UUID string without hyphens
        parse_result = uuid_parse_flexi(uuid_str_without_hyphens, parsed_uuid);
        if (parse_result != 0) {
            printf("uuid_parse_flexi() returned -1 (parsing error) for UUID without hyphens: %s\n", uuid_str_without_hyphens);
            failed_tests++;
        }
        else if(uuid_compare(original_uuid, parsed_uuid) != 0) {
            printf("uuid_parse_flexi() parsed value mismatch for UUID without hyphens: %s\n", uuid_str_without_hyphens);
            failed_tests++;
        }

        if(failed_tests)
            break;
    }

    printf("UUID: failed %d out of %d tests.\n", failed_tests, i);
    return failed_tests;
}