Coverage Report

Created: 2026-10-28 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/proj/src/iso19111/internal.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  PROJ
4
 * Purpose:  ISO19111:2019 implementation
5
 * Author:   Even Rouault <even dot rouault at spatialys dot com>
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2018, Even Rouault <even dot rouault at spatialys dot com>
9
 *
10
 * Permission is hereby granted, free of charge, to any person obtaining a
11
 * copy of this software and associated documentation files (the "Software"),
12
 * to deal in the Software without restriction, including without limitation
13
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14
 * and/or sell copies of the Software, and to permit persons to whom the
15
 * Software is furnished to do so, subject to the following conditions:
16
 *
17
 * The above copyright notice and this permission notice shall be included
18
 * in all copies or substantial portions of the Software.
19
 *
20
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26
 * DEALINGS IN THE SOFTWARE.
27
 ****************************************************************************/
28
29
#ifndef FROM_PROJ_CPP
30
#define FROM_PROJ_CPP
31
#endif
32
33
#include "proj/internal/internal.hpp"
34
35
#include <cmath>
36
#include <cstdint>
37
#include <cstring>
38
#ifdef _MSC_VER
39
#include <string.h>
40
#else
41
#include <strings.h>
42
#endif
43
#include <exception>
44
#include <iomanip> // std::setprecision
45
#include <locale>
46
#include <sstream> // std::istringstream and std::ostringstream
47
#include <string>
48
49
#include "sqlite3.h"
50
51
NS_PROJ_START
52
53
namespace internal {
54
55
// ---------------------------------------------------------------------------
56
57
/**
58
 * Replace all occurrences of before with after.
59
 */
60
std::string replaceAll(const std::string &str, const std::string &before,
61
52.4k
                       const std::string &after) {
62
52.4k
    std::string ret(str);
63
52.4k
    const size_t nBeforeSize = before.size();
64
52.4k
    const size_t nAfterSize = after.size();
65
52.4k
    if (nBeforeSize) {
66
52.4k
        size_t nStartPos = 0;
67
281k
        while ((nStartPos = ret.find(before, nStartPos)) != std::string::npos) {
68
229k
            ret.replace(nStartPos, nBeforeSize, after);
69
229k
            nStartPos += nAfterSize;
70
229k
        }
71
52.4k
    }
72
52.4k
    return ret;
73
52.4k
}
74
75
// ---------------------------------------------------------------------------
76
77
1.23G
inline static bool EQUALN(const char *a, const char *b, size_t size) {
78
#ifdef _MSC_VER
79
    return _strnicmp(a, b, size) == 0;
80
#else
81
1.23G
    return strncasecmp(a, b, size) == 0;
82
1.23G
#endif
83
1.23G
}
84
85
/**
86
 * Case-insensitive equality test
87
 */
88
9.54M
bool ci_equal(const std::string &a, const std::string &b) noexcept {
89
9.54M
    const auto size = a.size();
90
9.54M
    if (size != b.size()) {
91
8.29M
        return false;
92
8.29M
    }
93
1.25M
    return EQUALN(a.c_str(), b.c_str(), size);
94
9.54M
}
95
96
9.54M
bool ci_equal(const std::string &a, const char *b) noexcept {
97
9.54M
    const auto size = a.size();
98
9.54M
    if (size != strlen(b)) {
99
8.11M
        return false;
100
8.11M
    }
101
1.43M
    return EQUALN(a.c_str(), b, size);
102
9.54M
}
103
104
0
bool ci_equal(const char *a, const char *b) noexcept {
105
0
    const auto size = strlen(a);
106
0
    if (size != strlen(b)) {
107
0
        return false;
108
0
    }
109
0
    return EQUALN(a, b, size);
110
0
}
111
112
// ---------------------------------------------------------------------------
113
114
38.9k
bool ci_less(const std::string &a, const std::string &b) noexcept {
115
#ifdef _MSC_VER
116
    return _stricmp(a.c_str(), b.c_str()) < 0;
117
#else
118
38.9k
    return strcasecmp(a.c_str(), b.c_str()) < 0;
119
38.9k
#endif
120
38.9k
}
121
122
// ---------------------------------------------------------------------------
123
124
/**
125
 * Convert to lower case.
126
 */
127
128
std::string tolower(const std::string &str)
129
130
11.2k
{
131
11.2k
    std::string ret(str);
132
11.2k
    for (char &ch : ret)
133
351k
        ch =
134
351k
            (ch >= 'A' && ch <= 'Z') ? static_cast<char>(ch + ('a' - 'A')) : ch;
135
11.2k
    return ret;
136
11.2k
}
137
138
// ---------------------------------------------------------------------------
139
140
/**
141
 * Convert to upper case.
142
 */
143
144
std::string toupper(const std::string &str)
145
146
931
{
147
931
    std::string ret(str);
148
931
    for (char &ch : ret)
149
45.2k
        ch =
150
45.2k
            (ch >= 'a' && ch <= 'z') ? static_cast<char>(ch - ('a' - 'A')) : ch;
151
931
    return ret;
152
931
}
153
154
// ---------------------------------------------------------------------------
155
156
/** Strip leading and trailing double quote characters */
157
141k
std::string stripQuotes(const std::string &str) {
158
141k
    if (str.size() >= 2 && str[0] == '"' && str.back() == '"') {
159
11.1k
        return str.substr(1, str.size() - 2);
160
11.1k
    }
161
130k
    return str;
162
141k
}
163
164
// ---------------------------------------------------------------------------
165
166
546k
size_t ci_find(const std::string &str, const char *needle) noexcept {
167
546k
    const size_t needleSize = strlen(needle);
168
40.5M
    for (size_t i = 0; i + needleSize <= str.size(); i++) {
169
39.9M
        if (EQUALN(str.c_str() + i, needle, needleSize)) {
170
7.50k
            return i;
171
7.50k
        }
172
39.9M
    }
173
539k
    return std::string::npos;
174
546k
}
175
176
// ---------------------------------------------------------------------------
177
178
size_t ci_find(const std::string &str, const std::string &needle,
179
174M
               size_t startPos) noexcept {
180
174M
    const size_t needleSize = needle.size();
181
1.35G
    for (size_t i = startPos; i + needleSize <= str.size(); i++) {
182
1.18G
        if (EQUALN(str.c_str() + i, needle.c_str(), needleSize)) {
183
14.1k
            return i;
184
14.1k
        }
185
1.18G
    }
186
174M
    return std::string::npos;
187
174M
}
188
189
// ---------------------------------------------------------------------------
190
191
/*
192
bool starts_with(const std::string &str, const std::string &prefix) noexcept {
193
    if (str.size() < prefix.size()) {
194
        return false;
195
    }
196
    return std::memcmp(str.c_str(), prefix.c_str(), prefix.size()) == 0;
197
}
198
*/
199
200
// ---------------------------------------------------------------------------
201
202
/*
203
bool starts_with(const std::string &str, const char *prefix) noexcept {
204
    const size_t prefixSize = std::strlen(prefix);
205
    if (str.size() < prefixSize) {
206
        return false;
207
    }
208
    return std::memcmp(str.c_str(), prefix, prefixSize) == 0;
209
}
210
*/
211
212
// ---------------------------------------------------------------------------
213
214
1.40M
bool ci_starts_with(const char *str, const char *prefix) noexcept {
215
1.40M
    const auto str_size = strlen(str);
216
1.40M
    const auto prefix_size = strlen(prefix);
217
1.40M
    if (str_size < prefix_size) {
218
11.5k
        return false;
219
11.5k
    }
220
1.39M
    return EQUALN(str, prefix, prefix_size);
221
1.40M
}
222
223
// ---------------------------------------------------------------------------
224
225
bool ci_starts_with(const std::string &str,
226
12.5M
                    const std::string &prefix) noexcept {
227
12.5M
    if (str.size() < prefix.size()) {
228
381k
        return false;
229
381k
    }
230
12.1M
    return EQUALN(str.c_str(), prefix.c_str(), prefix.size());
231
12.5M
}
232
233
// ---------------------------------------------------------------------------
234
235
168k
bool ends_with(const std::string &str, const std::string &suffix) noexcept {
236
168k
    if (str.size() < suffix.size()) {
237
63.3k
        return false;
238
63.3k
    }
239
105k
    return std::memcmp(str.c_str() + str.size() - suffix.size(), suffix.c_str(),
240
105k
                       suffix.size()) == 0;
241
168k
}
242
243
// ---------------------------------------------------------------------------
244
245
755k
double c_locale_stod(const std::string &s) {
246
755k
    bool success;
247
755k
    double val = c_locale_stod(s, success);
248
755k
    if (!success) {
249
2.15k
        throw std::invalid_argument("non double value");
250
2.15k
    }
251
753k
    return val;
252
755k
}
253
254
878k
double c_locale_stod(const std::string &s, bool &success) {
255
256
878k
    success = true;
257
878k
    const auto s_size = s.size();
258
    // Fast path
259
878k
    if (s_size > 0 && s_size < 15) {
260
774k
        std::int64_t acc = 0;
261
774k
        std::int64_t div = 1;
262
774k
        bool afterDot = false;
263
774k
        size_t i = 0;
264
774k
        if (s[0] == '-') {
265
38.9k
            ++i;
266
38.9k
            div = -1;
267
735k
        } else if (s[0] == '+') {
268
632
            ++i;
269
632
        }
270
6.29M
        for (; i < s_size; ++i) {
271
5.52M
            const auto ch = s[i];
272
5.52M
            if (ch >= '0' && ch <= '9') {
273
4.87M
                acc = acc * 10 + ch - '0';
274
4.87M
                if (afterDot) {
275
1.18M
                    div *= 10;
276
1.18M
                }
277
4.87M
            } else if (ch == '.') {
278
640k
                afterDot = true;
279
640k
            } else {
280
11.2k
                div = 0;
281
11.2k
            }
282
5.52M
        }
283
774k
        if (div) {
284
766k
            return static_cast<double>(acc) / div;
285
766k
        }
286
774k
    }
287
288
112k
    std::istringstream iss(s);
289
112k
    iss.imbue(std::locale::classic());
290
112k
    double d;
291
112k
    iss >> d;
292
112k
    if (!iss.eof() || iss.fail()) {
293
86.3k
        success = false;
294
86.3k
        d = 0;
295
86.3k
    }
296
112k
    return d;
297
878k
}
298
299
// ---------------------------------------------------------------------------
300
301
31.1k
std::vector<std::string> split(const std::string &str, char separator) {
302
31.1k
    std::vector<std::string> res;
303
31.1k
    size_t lastPos = 0;
304
31.1k
    size_t newPos = 0;
305
150k
    while ((newPos = str.find(separator, lastPos)) != std::string::npos) {
306
119k
        res.push_back(str.substr(lastPos, newPos - lastPos));
307
119k
        lastPos = newPos + 1;
308
119k
    }
309
31.1k
    res.push_back(str.substr(lastPos));
310
31.1k
    return res;
311
31.1k
}
312
313
// ---------------------------------------------------------------------------
314
315
std::vector<std::string> split(const std::string &str,
316
2.60k
                               const std::string &separator) {
317
2.60k
    std::vector<std::string> res;
318
2.60k
    size_t lastPos = 0;
319
2.60k
    size_t newPos = 0;
320
4.07k
    while ((newPos = str.find(separator, lastPos)) != std::string::npos) {
321
1.47k
        res.push_back(str.substr(lastPos, newPos - lastPos));
322
1.47k
        lastPos = newPos + separator.size();
323
1.47k
    }
324
2.60k
    res.push_back(str.substr(lastPos));
325
2.60k
    return res;
326
2.60k
}
327
328
// ---------------------------------------------------------------------------
329
330
#ifdef _WIN32
331
332
// For some reason, sqlite3_snprintf() in the sqlite3 builds used on AppVeyor
333
// doesn't round identically to the Unix builds, and thus breaks a number of
334
// unit test. So to avoid this, use the stdlib formatting
335
336
std::string toString(int val) {
337
    std::ostringstream buffer;
338
    buffer.imbue(std::locale::classic());
339
    buffer << val;
340
    return buffer.str();
341
}
342
343
std::string toString(double val, int precision) {
344
    std::ostringstream buffer;
345
    buffer.imbue(std::locale::classic());
346
    buffer << std::setprecision(precision);
347
    buffer << val;
348
    auto str = buffer.str();
349
    if (precision == 15 && str.find("9999999999") != std::string::npos) {
350
        buffer.str("");
351
        buffer.clear();
352
        buffer << std::setprecision(14);
353
        buffer << val;
354
        return buffer.str();
355
    }
356
    return str;
357
}
358
359
#else
360
361
248k
std::string toString(int val) {
362
    // use sqlite3 API that is slightly faster than std::ostringstream
363
    // with forcing the C locale. sqlite3_snprintf() emulates a C locale.
364
248k
    constexpr int BUF_SIZE = 16;
365
248k
    char szBuffer[BUF_SIZE];
366
248k
    sqlite3_snprintf(BUF_SIZE, szBuffer, "%d", val);
367
248k
    return szBuffer;
368
248k
}
369
370
76.7k
std::string toString(double val, int precision) {
371
    // use sqlite3 API that is slightly faster than std::ostringstream
372
    // with forcing the C locale. sqlite3_snprintf() emulates a C locale.
373
76.7k
    constexpr int BUF_SIZE = 32;
374
76.7k
    char szBuffer[BUF_SIZE];
375
76.7k
    sqlite3_snprintf(BUF_SIZE, szBuffer, "%.*g", precision, val);
376
76.7k
    if (precision == 15 && strstr(szBuffer, "9999999999")) {
377
199
        sqlite3_snprintf(BUF_SIZE, szBuffer, "%.14g", val);
378
199
    }
379
76.7k
    return szBuffer;
380
76.7k
}
381
382
#endif
383
384
// ---------------------------------------------------------------------------
385
386
1.92k
std::string concat(const char *a, const std::string &b) {
387
1.92k
    std::string res(a);
388
1.92k
    res += b;
389
1.92k
    return res;
390
1.92k
}
391
392
6.36k
std::string concat(const char *a, const std::string &b, const char *c) {
393
6.36k
    std::string res(a);
394
6.36k
    res += b;
395
6.36k
    res += c;
396
6.36k
    return res;
397
6.36k
}
398
399
// ---------------------------------------------------------------------------
400
401
// Avoid rounding issues due to year -> second (SI unit) -> year roundtrips
402
0
double getRoundedEpochInDecimalYear(double year) {
403
    // Try to see if the value is close to xxxx.yyy decimal year.
404
0
    if (std::fabs(1000 * year - std::round(1000 * year)) <= 1e-3) {
405
0
        year = std::round(1000 * year) / 1000.0;
406
0
    }
407
0
    return year;
408
0
}
409
410
std::string join(const std::vector<std::string> &strings,
411
0
                 const char *delimiter) {
412
0
    std::string result;
413
0
    bool first = true;
414
0
    for (const auto &str : strings) {
415
0
        if (!first)
416
0
            result += delimiter;
417
0
        first = false;
418
0
        result += str;
419
0
    }
420
421
0
    return result;
422
0
}
423
424
// ---------------------------------------------------------------------------
425
426
} // namespace internal
427
428
NS_PROJ_END