File indexing completed on 2024-04-21 03:54:29
0001 /* 0002 SPDX-FileCopyrightText: 2021 Volker Krause <vkrause@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #ifndef ISOCODES_P_H 0008 #define ISOCODES_P_H 0009 0010 #include <QStringView> 0011 0012 #include <cstdint> 0013 0014 /** 0015 * Utilities for efficient storage of ISO codes. 0016 * The focus on constexpr here matters, as this is used for the compiled in data tables, 0017 * anything in there has to be constexpr in order to put the data tables into the shared read-only 0018 * data section. 0019 * 0020 * There's basically two formats in here: 0021 * - upper case + digit codes of up to 3 characters are stored as a 3 digit base 37 number, which 0022 * fits into a 16bit value 0023 * To simplify data table handling this is done in a way that lexicographical order is retained. 0024 * - two letter upper case codes like ISO 3166-1 alpha2: those are stored equivalent to a const char[2] 0025 * technically the same approach as for 3 char codes could be used as well, but that doesn't give us 0026 * any space advantage while considerably easing debugging (codes are directly readable). 0027 */ 0028 namespace IsoCodes 0029 { 0030 constexpr inline bool isAlpha(char c) 0031 { 0032 return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); 0033 } 0034 0035 constexpr inline bool isAlpha(QChar c) 0036 { 0037 return c.row() == 0 ? isAlpha(c.cell()) : false; 0038 } 0039 0040 constexpr inline bool isDigit(char c) 0041 { 0042 return c >= '0' && c <= '9'; 0043 } 0044 0045 constexpr inline bool isDigit(QChar c) 0046 { 0047 return c.row() == 0 ? isDigit(c.cell()) : false; 0048 } 0049 0050 constexpr inline uint8_t mapToUpper(char c) 0051 { 0052 return c >= 'a' ? c - 32 : c; 0053 } 0054 0055 constexpr inline uint8_t mapToUpper(QChar c) 0056 { 0057 return mapToUpper(c.cell()); 0058 } 0059 0060 template<typename T> 0061 constexpr inline uint16_t alpha2CodeToKey(T code, std::size_t size) 0062 { 0063 return (size == 2 && isAlpha(code[0]) && isAlpha(code[1])) ? mapToUpper(code[0]) << 8 | mapToUpper(code[1]) : 0; 0064 } 0065 0066 template<std::size_t N> 0067 constexpr inline uint16_t alpha2CodeToKey(const char (&code)[N]) 0068 { 0069 return alpha2CodeToKey(code, N - 1); 0070 } 0071 0072 constexpr inline uint16_t alpha2CodeToKey(QStringView code) 0073 { 0074 return alpha2CodeToKey(code, code.size()); 0075 } 0076 0077 constexpr inline uint8_t mapToAlphaNumKey(char c) 0078 { 0079 // this maps digits 0-9 to values 1-10, and letters to the numbers 11-36 0080 uint8_t key = c; 0081 if (key <= '9') { 0082 return key - '/'; 0083 } else if (key >= 'a') { 0084 key -= 32; 0085 } 0086 return key - 'A' + 11; 0087 } 0088 0089 constexpr inline uint8_t mapToAlphaNumKey(QChar c) 0090 { 0091 return mapToAlphaNumKey(c.cell()); 0092 } 0093 0094 enum { 0095 AlphaNumKeyFactor = 37, 0096 }; 0097 0098 template<typename T> 0099 constexpr inline char mapFromAlphaNumKey(T key) 0100 { 0101 char c = key % IsoCodes::AlphaNumKeyFactor; 0102 if (c > 0 && c < 11) { 0103 return c + '/'; 0104 } 0105 if (c >= 11) { 0106 return c + 'A' - 11; 0107 } 0108 return 0; 0109 } 0110 0111 template<typename T> 0112 constexpr inline uint16_t alphaNum3CodeToKey(T code, std::size_t size) 0113 { 0114 if (size > 3 || size == 0) { 0115 return 0; 0116 } 0117 uint16_t key = 0; 0118 for (std::size_t i = 0; i < size; ++i) { 0119 if (!isAlpha(code[i]) && !isDigit(code[i])) { 0120 return 0; 0121 } 0122 key *= AlphaNumKeyFactor; 0123 key += mapToAlphaNumKey(code[i]); 0124 } 0125 for (std::size_t i = size; i < 3; ++i) { // "left align" to retain lexicographical order 0126 key *= AlphaNumKeyFactor; 0127 } 0128 return key; 0129 } 0130 0131 constexpr inline uint16_t alphaNum3CodeToKey(QStringView code) 0132 { 0133 return alphaNum3CodeToKey(code, code.size()); 0134 } 0135 0136 template<typename T> 0137 constexpr inline uint16_t alpha3CodeToKey(T code, std::size_t size) 0138 { 0139 return (size == 3 && isAlpha(code[0]) && isAlpha(code[1]) && isAlpha(code[2])) ? alphaNum3CodeToKey(code, 3) : 0; 0140 } 0141 0142 template<std::size_t N> 0143 constexpr inline uint16_t alpha3CodeToKey(const char (&code)[N]) 0144 { 0145 return alpha3CodeToKey(code, N - 1); 0146 } 0147 0148 constexpr inline uint16_t alpha3CodeToKey(QStringView code) 0149 { 0150 return alpha3CodeToKey(code, code.size()); 0151 } 0152 0153 constexpr inline uint32_t subdivisionCodeToKey(const char *code, std::size_t size) 0154 { 0155 if (size < 4 || code[2] != '-') { 0156 return 0; 0157 } 0158 0159 const auto countryKey = alpha2CodeToKey(code, 2); 0160 const auto subdivKey = alphaNum3CodeToKey(code + 3, size - 3); 0161 return countryKey > 0 && subdivKey > 0 ? ((uint32_t)(countryKey) << 16 | subdivKey) : 0; 0162 } 0163 0164 constexpr inline uint32_t subdivisionCodeToKey(QStringView code) 0165 { 0166 if (code.size() < 4 || code[2] != QLatin1Char('-')) { 0167 return 0; 0168 } 0169 0170 const auto countryKey = alpha2CodeToKey(code.left(2)); 0171 const auto subdivKey = alphaNum3CodeToKey(code.mid(3), code.size() - 3); 0172 return countryKey > 0 && subdivKey > 0 ? ((uint32_t)(countryKey) << 16 | subdivKey) : 0; 0173 } 0174 0175 template<std::size_t N> 0176 constexpr inline uint32_t subdivisionCodeToKey(const char (&code)[N]) 0177 { 0178 return subdivisionCodeToKey(code, N - 1); 0179 } 0180 0181 /// Before v4.16 iso-codes used for parent code just the subdivision code, without the country 0182 /// (only by error sometimes). Since that version the full code now is always used. 0183 /// Handle both cases gracefully. 0184 /// Does not check the country part for sanity, but just discards the info. 0185 constexpr inline uint16_t parentCodeToKey(QStringView code) 0186 { 0187 if (code.size() < 4) { 0188 return alphaNum3CodeToKey(code); 0189 } 0190 if (code[2] != QLatin1Char('-')) { 0191 return 0; 0192 } 0193 0194 const auto countryKey = alpha2CodeToKey(code.left(2)); 0195 const auto subdivKey = alphaNum3CodeToKey(code.mid(3), code.size() - 3); 0196 return countryKey > 0 ? subdivKey : 0; 0197 } 0198 } 0199 0200 #endif // ISOCODES_P_H