-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathutils.cpp
More file actions
195 lines (153 loc) · 5.6 KB
/
Copy pathutils.cpp
File metadata and controls
195 lines (153 loc) · 5.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
#include <cmath>
#include "utils.h"
#ifndef CONFIGCAT_EXTERNAL_SHA_ENABLED
#include <hash-library/sha1.h>
#include <hash-library/sha256.h>
#endif // CONFIGCAT_EXTERNAL_SHA_ENABLED
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/EPSILON
#define JS_NUMBER_EPSILON 2.2204460492503130808472633361816e-16
using namespace std;
namespace configcat {
std::optional<double> datetime_to_unixtimeseconds(const std::chrono::system_clock::time_point& tp) {
long long millisecondsSinceEpoch = tp.time_since_epoch() / std::chrono::milliseconds(1);
auto timestamp = millisecondsSinceEpoch / 1000.0;
// Allow values only between 0001-01-01T00:00:00.000Z and 9999-12-31T23:59:59.999
return timestamp < -62135596800 || 253402300800 <= timestamp ? nullopt : optional<double>(timestamp);
}
std::optional<std::chrono::system_clock::time_point> datetime_from_unixtimeseconds(double timestamp) {
// Allow values only between 0001-01-01T00:00:00.000Z and 9999-12-31T23:59:59.999
if (timestamp < -62135596800 || 253402300800 <= timestamp) {
return nullopt;
}
auto durationSinceEpoch = std::chrono::seconds(static_cast<long long>(timestamp)) +
std::chrono::milliseconds(static_cast<long long>((timestamp - static_cast<long long>(timestamp)) * 1000));
return chrono::system_clock::time_point{ durationSinceEpoch };
}
int getExponent(double abs) {
auto exp = log10(abs);
auto ceil = std::ceil(exp);
return (int)(std::abs(exp - ceil) < JS_NUMBER_EPSILON ? ceil : floor(exp));
}
int getSignificantDecimals(double number) {
if (!number) {
return 0;
}
number = std::abs(number);
auto exp = std::min(0, getExponent(number));
for (; exp > -17; --exp) {
auto pow10 = pow(10, exp);
auto fracr = round(number / pow10) * pow10;
if (abs(number - fracr) < number * 10.0 * JS_NUMBER_EPSILON) {
break;
}
}
return min(17, -exp);
}
std::string number_to_string(double number) {
if (isnan(number)) {
return "NaN";
}
else if (isinf(number)) {
return number > 0 ? "Infinity" : "-Infinity";
}
else if (!number) {
return "0";
}
const auto abs = std::abs(number);
int exp;
if (1e-6 <= abs && abs < 1e21) {
exp = 0;
}
else {
exp = getExponent(abs);
number /= pow(10, exp);
}
// NOTE: sprintf can't really deal with 17 decimal places,
// e.g. sprintf(buf, "%.17f", 0.1) results in '0.10000000000000001'.
// So we need to manually calculate the actual number of significant decimals.
auto decimals = getSignificantDecimals(number);
auto str = string_format(string_format("%%.%df", decimals), number);
if (exp) {
str += (exp > 0 ? "e+" : "e") + string_format("%d", exp);
}
return str;
}
std::optional<double> number_from_string(const std::string& str) {
if (str.empty()) return nullopt;
auto strPtr = const_cast<string*>(&str);
// Make a copy of str only if necessary.
string strCopy;
if (isspace(str[0]) || isspace(str[str.size() - 1])) {
strCopy = string(str);
trim(strCopy);
if (strCopy.empty()) return nullopt;
strPtr = &strCopy;
}
if (*strPtr == "NaN") return numeric_limits<double>::quiet_NaN();
else if (*strPtr == "Infinity" || *strPtr == "+Infinity") return numeric_limits<double>::infinity();
else if (*strPtr == "-Infinity") return -numeric_limits<double>::infinity();
// Accept ',' as decimal separator.
if (strPtr->find(',') != string::npos) {
if (strPtr == &str) {
strCopy = string(str);
strPtr = &strCopy;
}
replace(strPtr->begin(), strPtr->end(), ',', '.');
}
// Reject hex numbers and other forms of INF, NAN, etc. that are accepted by std::stod.
size_t index = 0;
auto ch = (*strPtr)[index];
if (ch == '+' || ch == '-') {
++index;
if (index >= strPtr->size()) return nullopt;
ch = (*strPtr)[index];
}
if (isdigit(ch)) {
++index;
if (index < strPtr->size() && !isdigit(ch = (*strPtr)[index]) && ch != '.' && ch != 'e' && ch != 'E') return std::nullopt;
}
else if (ch != '.') return nullopt;
size_t charsProcessed;
double value;
try { value = stod(*strPtr, &charsProcessed); }
catch (const invalid_argument&) { return nullopt; }
// Reject strings which contain invalid characters after the number.
if (charsProcessed != strPtr->size()) {
return nullopt;
}
return value;
}
std::optional<long long> integer_from_string(const std::string& str) {
if (str.empty()) return nullopt;
auto strPtr = const_cast<string*>(&str);
// Make a copy of str only if necessary.
string strCopy;
if (isspace(str[0]) || isspace(str[str.size() - 1])) {
strCopy = string(str);
trim(strCopy);
if (strCopy.empty()) {
return nullopt;
}
strPtr = &strCopy;
}
size_t charsProcessed;
long long value;
try { value = stoll(*strPtr, &charsProcessed); }
catch (const invalid_argument&) { return nullopt; }
// Reject strings which contain invalid characters after the number.
if (charsProcessed != strPtr->size()) {
return nullopt;
}
return value;
}
#ifndef CONFIGCAT_EXTERNAL_SHA_ENABLED
SHA1 sha1Calculator;
SHA256 sha256Calculator;
std::string sha1(const std::string& input) {
return sha1Calculator(input);
}
std::string sha256(const std::string& input) {
return sha256Calculator(input);
}
#endif // CONFIGCAT_EXTERNAL_SHA_ENABLED
} // namespace configcat