1/*
2 SPDX-FileCopyrightText: 2021 Volker Krause <vkrause@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#ifndef ISOCODES_P_H
8#define ISOCODES_P_H
9
10#include <QStringView>
11
12#include <cstdint>
13
14/*!
15 * Utilities for efficient storage of ISO codes.
16 * The focus on constexpr here matters, as this is used for the compiled in data tables,
17 * anything in there has to be constexpr in order to put the data tables into the shared read-only
18 * data section.
19 *
20 * There's basically two formats in here:
21 * - upper case + digit codes of up to 3 characters are stored as a 3 digit base 37 number, which
22 * fits into a 16bit value
23 * To simplify data table handling this is done in a way that lexicographical order is retained.
24 * - two letter upper case codes like ISO 3166-1 alpha2: those are stored equivalent to a const char[2]
25 * technically the same approach as for 3 char codes could be used as well, but that doesn't give us
26 * any space advantage while considerably easing debugging (codes are directly readable).
27 *
28 * \internal
29 */
30namespace IsoCodes
31{
32constexpr inline bool isAlpha(char c)
33{
34 return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
35}
36
37constexpr inline bool isAlpha(QChar c)
38{
39 return c.row() == 0 ? isAlpha(c: c.cell()) : false;
40}
41
42constexpr inline bool isDigit(char c)
43{
44 return c >= '0' && c <= '9';
45}
46
47constexpr inline bool isDigit(QChar c)
48{
49 return c.row() == 0 ? isDigit(c: c.cell()) : false;
50}
51
52constexpr inline uint8_t mapToUpper(char c)
53{
54 return c >= 'a' ? c - 32 : c;
55}
56
57constexpr inline uint8_t mapToUpper(QChar c)
58{
59 return mapToUpper(c: c.cell());
60}
61
62template<typename T>
63constexpr inline uint16_t alpha2CodeToKey(T code, std::size_t size)
64{
65 return (size == 2 && isAlpha(code[0]) && isAlpha(code[1])) ? mapToUpper(code[0]) << 8 | mapToUpper(code[1]) : 0;
66}
67
68template<std::size_t N>
69constexpr inline uint16_t alpha2CodeToKey(const char (&code)[N])
70{
71 return alpha2CodeToKey(code, N - 1);
72}
73
74constexpr inline uint16_t alpha2CodeToKey(QStringView code)
75{
76 return alpha2CodeToKey(code, size: code.size());
77}
78
79constexpr inline uint8_t mapToAlphaNumKey(char c)
80{
81 // this maps digits 0-9 to values 1-10, and letters to the numbers 11-36
82 uint8_t key = c;
83 if (key <= '9') {
84 return key - '/';
85 } else if (key >= 'a') {
86 key -= 32;
87 }
88 return key - 'A' + 11;
89}
90
91constexpr inline uint8_t mapToAlphaNumKey(QChar c)
92{
93 return mapToAlphaNumKey(c: c.cell());
94}
95
96enum {
97 AlphaNumKeyFactor = 37,
98};
99
100template<typename T>
101constexpr inline char mapFromAlphaNumKey(T key)
102{
103 char c = key % IsoCodes::AlphaNumKeyFactor;
104 if (c > 0 && c < 11) {
105 return c + '/';
106 }
107 if (c >= 11) {
108 return c + 'A' - 11;
109 }
110 return 0;
111}
112
113template<typename T>
114constexpr inline uint16_t alphaNum3CodeToKey(T code, std::size_t size)
115{
116 if (size > 3 || size == 0) {
117 return 0;
118 }
119 uint16_t key = 0;
120 for (std::size_t i = 0; i < size; ++i) {
121 if (!isAlpha(code[i]) && !isDigit(code[i])) {
122 return 0;
123 }
124 key *= AlphaNumKeyFactor;
125 key += mapToAlphaNumKey(code[i]);
126 }
127 for (std::size_t i = size; i < 3; ++i) { // "left align" to retain lexicographical order
128 key *= AlphaNumKeyFactor;
129 }
130 return key;
131}
132
133constexpr inline uint16_t alphaNum3CodeToKey(QStringView code)
134{
135 return alphaNum3CodeToKey(code, size: code.size());
136}
137
138template<typename T>
139constexpr inline uint16_t alpha3CodeToKey(T code, std::size_t size)
140{
141 return (size == 3 && isAlpha(code[0]) && isAlpha(code[1]) && isAlpha(code[2])) ? alphaNum3CodeToKey(code, 3) : 0;
142}
143
144template<std::size_t N>
145constexpr inline uint16_t alpha3CodeToKey(const char (&code)[N])
146{
147 return alpha3CodeToKey(code, N - 1);
148}
149
150constexpr inline uint16_t alpha3CodeToKey(QStringView code)
151{
152 return alpha3CodeToKey(code, size: code.size());
153}
154
155constexpr inline uint32_t subdivisionCodeToKey(const char *code, std::size_t size)
156{
157 if (size < 4 || code[2] != '-') {
158 return 0;
159 }
160
161 const auto countryKey = alpha2CodeToKey(code, size: 2);
162 const auto subdivKey = alphaNum3CodeToKey(code: code + 3, size: size - 3);
163 return countryKey > 0 && subdivKey > 0 ? ((uint32_t)(countryKey) << 16 | subdivKey) : 0;
164}
165
166constexpr inline uint32_t subdivisionCodeToKey(QStringView code)
167{
168 if (code.size() < 4 || code[2] != QLatin1Char('-')) {
169 return 0;
170 }
171
172 const auto countryKey = alpha2CodeToKey(code: code.left(n: 2));
173 const auto subdivKey = alphaNum3CodeToKey(code: code.mid(pos: 3), size: code.size() - 3);
174 return countryKey > 0 && subdivKey > 0 ? ((uint32_t)(countryKey) << 16 | subdivKey) : 0;
175}
176
177template<std::size_t N>
178constexpr inline uint32_t subdivisionCodeToKey(const char (&code)[N])
179{
180 return subdivisionCodeToKey(code, N - 1);
181}
182
183/// Before v4.16 iso-codes used for parent code just the subdivision code, without the country
184/// (only by error sometimes). Since that version the full code now is always used.
185/// Handle both cases gracefully.
186/// Does not check the country part for sanity, but just discards the info.
187constexpr inline uint16_t parentCodeToKey(QStringView code)
188{
189 if (code.size() < 4) {
190 return alphaNum3CodeToKey(code);
191 }
192 if (code[2] != QLatin1Char('-')) {
193 return 0;
194 }
195
196 const auto countryKey = alpha2CodeToKey(code: code.left(n: 2));
197 const auto subdivKey = alphaNum3CodeToKey(code: code.mid(pos: 3), size: code.size() - 3);
198 return countryKey > 0 ? subdivKey : 0;
199}
200}
201
202#endif // ISOCODES_P_H
203

source code of ki18n/src/localedata/isocodes_p.h