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 */
28namespace IsoCodes
29{
30constexpr inline bool isAlpha(char c)
31{
32 return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
33}
34
35constexpr inline bool isAlpha(QChar c)
36{
37 return c.row() == 0 ? isAlpha(c: c.cell()) : false;
38}
39
40constexpr inline bool isDigit(char c)
41{
42 return c >= '0' && c <= '9';
43}
44
45constexpr inline bool isDigit(QChar c)
46{
47 return c.row() == 0 ? isDigit(c: c.cell()) : false;
48}
49
50constexpr inline uint8_t mapToUpper(char c)
51{
52 return c >= 'a' ? c - 32 : c;
53}
54
55constexpr inline uint8_t mapToUpper(QChar c)
56{
57 return mapToUpper(c: c.cell());
58}
59
60template<typename T>
61constexpr inline uint16_t alpha2CodeToKey(T code, std::size_t size)
62{
63 return (size == 2 && isAlpha(code[0]) && isAlpha(code[1])) ? mapToUpper(code[0]) << 8 | mapToUpper(code[1]) : 0;
64}
65
66template<std::size_t N>
67constexpr inline uint16_t alpha2CodeToKey(const char (&code)[N])
68{
69 return alpha2CodeToKey(code, N - 1);
70}
71
72constexpr inline uint16_t alpha2CodeToKey(QStringView code)
73{
74 return alpha2CodeToKey(code, size: code.size());
75}
76
77constexpr inline uint8_t mapToAlphaNumKey(char c)
78{
79 // this maps digits 0-9 to values 1-10, and letters to the numbers 11-36
80 uint8_t key = c;
81 if (key <= '9') {
82 return key - '/';
83 } else if (key >= 'a') {
84 key -= 32;
85 }
86 return key - 'A' + 11;
87}
88
89constexpr inline uint8_t mapToAlphaNumKey(QChar c)
90{
91 return mapToAlphaNumKey(c: c.cell());
92}
93
94enum {
95 AlphaNumKeyFactor = 37,
96};
97
98template<typename T>
99constexpr inline char mapFromAlphaNumKey(T key)
100{
101 char c = key % IsoCodes::AlphaNumKeyFactor;
102 if (c > 0 && c < 11) {
103 return c + '/';
104 }
105 if (c >= 11) {
106 return c + 'A' - 11;
107 }
108 return 0;
109}
110
111template<typename T>
112constexpr inline uint16_t alphaNum3CodeToKey(T code, std::size_t size)
113{
114 if (size > 3 || size == 0) {
115 return 0;
116 }
117 uint16_t key = 0;
118 for (std::size_t i = 0; i < size; ++i) {
119 if (!isAlpha(code[i]) && !isDigit(code[i])) {
120 return 0;
121 }
122 key *= AlphaNumKeyFactor;
123 key += mapToAlphaNumKey(code[i]);
124 }
125 for (std::size_t i = size; i < 3; ++i) { // "left align" to retain lexicographical order
126 key *= AlphaNumKeyFactor;
127 }
128 return key;
129}
130
131constexpr inline uint16_t alphaNum3CodeToKey(QStringView code)
132{
133 return alphaNum3CodeToKey(code, size: code.size());
134}
135
136template<typename T>
137constexpr inline uint16_t alpha3CodeToKey(T code, std::size_t size)
138{
139 return (size == 3 && isAlpha(code[0]) && isAlpha(code[1]) && isAlpha(code[2])) ? alphaNum3CodeToKey(code, 3) : 0;
140}
141
142template<std::size_t N>
143constexpr inline uint16_t alpha3CodeToKey(const char (&code)[N])
144{
145 return alpha3CodeToKey(code, N - 1);
146}
147
148constexpr inline uint16_t alpha3CodeToKey(QStringView code)
149{
150 return alpha3CodeToKey(code, size: code.size());
151}
152
153constexpr inline uint32_t subdivisionCodeToKey(const char *code, std::size_t size)
154{
155 if (size < 4 || code[2] != '-') {
156 return 0;
157 }
158
159 const auto countryKey = alpha2CodeToKey(code, size: 2);
160 const auto subdivKey = alphaNum3CodeToKey(code: code + 3, size: size - 3);
161 return countryKey > 0 && subdivKey > 0 ? ((uint32_t)(countryKey) << 16 | subdivKey) : 0;
162}
163
164constexpr inline uint32_t subdivisionCodeToKey(QStringView code)
165{
166 if (code.size() < 4 || code[2] != QLatin1Char('-')) {
167 return 0;
168 }
169
170 const auto countryKey = alpha2CodeToKey(code: code.left(n: 2));
171 const auto subdivKey = alphaNum3CodeToKey(code: code.mid(pos: 3), size: code.size() - 3);
172 return countryKey > 0 && subdivKey > 0 ? ((uint32_t)(countryKey) << 16 | subdivKey) : 0;
173}
174
175template<std::size_t N>
176constexpr inline uint32_t subdivisionCodeToKey(const char (&code)[N])
177{
178 return subdivisionCodeToKey(code, N - 1);
179}
180
181/// Before v4.16 iso-codes used for parent code just the subdivision code, without the country
182/// (only by error sometimes). Since that version the full code now is always used.
183/// Handle both cases gracefully.
184/// Does not check the country part for sanity, but just discards the info.
185constexpr inline uint16_t parentCodeToKey(QStringView code)
186{
187 if (code.size() < 4) {
188 return alphaNum3CodeToKey(code);
189 }
190 if (code[2] != QLatin1Char('-')) {
191 return 0;
192 }
193
194 const auto countryKey = alpha2CodeToKey(code: code.left(n: 2));
195 const auto subdivKey = alphaNum3CodeToKey(code: code.mid(pos: 3), size: code.size() - 3);
196 return countryKey > 0 ? subdivKey : 0;
197}
198}
199
200#endif // ISOCODES_P_H
201

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