1
2 // Copyright 2024 The Pigweed Authors
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 // use this file except in compliance with the License. You may obtain a copy of
6 // the License at
7 //
8 // https://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 // License for the specific language governing permissions and limitations under
14 // the License.
15
16 #include <pw_assert/check.h>
17
18 #include <cinttypes>
19
20 #include "pw_bluetooth_sapphire/internal/host/common/uint128.h"
21 #include "pw_bluetooth_sapphire/internal/host/common/uuid.h"
22
23 #ifndef SCNx8
24 #define SCNx8 "hhx"
25 #endif
26
27 namespace bt {
28 namespace {
29
30 // Format string that can be passed to sscanf. This allows sscanf to convert
31 // each octet into a uint8_t.
32 constexpr char kScanUuidFormatString[] =
33 "%2" SCNx8 "%2" SCNx8 "%2" SCNx8 "%2" SCNx8
34 "-"
35 "%2" SCNx8 "%2" SCNx8
36 "-"
37 "%2" SCNx8 "%2" SCNx8
38 "-"
39 "%2" SCNx8 "%2" SCNx8
40 "-"
41 "%2" SCNx8 "%2" SCNx8 "%2" SCNx8 "%2" SCNx8 "%2" SCNx8 "%2" SCNx8;
42
43 // Parses the contents of a |uuid_string| and returns the result in |out_bytes|.
44 // Returns false if |uuid_string| does not represent a valid UUID.
45 // TODO(armansito): After having used UUID in camel-case words all over the
46 // place, I've decided that it sucks. I'm explicitly naming this using the
47 // "Uuid" style as a reminder to fix style elsewhere.
ParseUuidString(const std::string & uuid_string,UInt128 * out_bytes)48 bool ParseUuidString(const std::string& uuid_string, UInt128* out_bytes) {
49 PW_DCHECK(out_bytes);
50
51 if (uuid_string.length() == 4) {
52 // Possibly a 16-bit short UUID, parse it in context of the Base UUID.
53 return ParseUuidString(
54 "0000" + uuid_string + "-0000-1000-8000-00805F9B34FB", out_bytes);
55 }
56
57 // This is a 36 character string, including 4 "-" characters and two
58 // characters for each of the 16-octets that form the 128-bit UUID.
59 if (uuid_string.length() != 36)
60 return false;
61
62 int result = std::sscanf(uuid_string.c_str(),
63 kScanUuidFormatString,
64 out_bytes->data() + 15,
65 out_bytes->data() + 14,
66 out_bytes->data() + 13,
67 out_bytes->data() + 12,
68 out_bytes->data() + 11,
69 out_bytes->data() + 10,
70 out_bytes->data() + 9,
71 out_bytes->data() + 8,
72 out_bytes->data() + 7,
73 out_bytes->data() + 6,
74 out_bytes->data() + 5,
75 out_bytes->data() + 4,
76 out_bytes->data() + 3,
77 out_bytes->data() + 2,
78 out_bytes->data() + 1,
79 out_bytes->data());
80
81 return (result > 0) && (static_cast<size_t>(result) == out_bytes->size());
82 }
83
84 } // namespace
85
IsStringValidUuid(const std::string & uuid_string)86 bool IsStringValidUuid(const std::string& uuid_string) {
87 UInt128 bytes;
88 return ParseUuidString(uuid_string, &bytes);
89 }
90
StringToUuid(const std::string & uuid_string,UUID * out_uuid)91 bool StringToUuid(const std::string& uuid_string, UUID* out_uuid) {
92 PW_DCHECK(out_uuid);
93
94 UInt128 bytes;
95 if (!ParseUuidString(uuid_string, &bytes)) {
96 return false;
97 }
98
99 *out_uuid = UUID(bytes);
100 return true;
101 }
102
103 } // namespace bt
104