1
/* Copyright 2012 10gen Inc.
3
* Licensed under the Apache License, Version 2.0 (the "License");
4
* you may not use this file except in compliance with the License.
5
* You may obtain a copy of the License at
7
* http://www.apache.org/licenses/LICENSE-2.0
9
* Unless required by applicable law or agreed to in writing, software
10
* distributed under the License is distributed on an "AS IS" BASIS,
11
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
* See the License for the specific language governing permissions and
13
* limitations under the License.
16
#include "mongo/base/parse_number.h"
20
#include "mongo/platform/cstdint.h"
25
* Returns the value of the digit "c", with the same conversion behavior as strtol.
27
* Assumes "c" is an ASCII character or UTF-8 octet.
29
static uint8_t _digitValue(char c) {
30
if (c >= '0' && c <= '9')
31
return uint8_t(c - '0');
32
if (c >= 'a' && c <= 'z')
33
return uint8_t(c - 'a' + 10);
34
if (c >= 'A' && c <= 'Z')
35
return uint8_t(c - 'A' + 10);
36
return 36; // Illegal digit value for all supported bases.
40
* Assuming "stringValue" represents a parseable number, extracts the sign and returns a
41
* substring with any sign characters stripped away. "*isNegative" is set to true if the
42
* number is negative, and false otherwise.
44
static inline StringData _extractSign(const StringData& stringValue, bool* isNegative) {
45
if (stringValue.empty()) {
51
switch (stringValue[0]) {
53
foundSignMarker = true;
57
foundSignMarker = true;
61
foundSignMarker = false;
67
return stringValue.substr(1);
72
* Assuming "stringValue" represents a parseable number, determines what base to use given
73
* "inputBase". Stores the correct base into "*outputBase". Follows strtol rules. If
74
* "inputBase" is not 0, *outputBase is set to "inputBase". Otherwise, if "stringValue" starts
75
* with "0x" or "0X", sets outputBase to 16, or if it starts with 0, sets outputBase to 8.
77
* Returns stringValue, unless it sets *outputBase to 16, in which case it will strip off the
78
* "0x" or "0X" prefix, if present.
80
static inline StringData _extractBase(
81
const StringData& stringValue, int inputBase, int* outputBase) {
83
const StringData hexPrefixLower("0x", StringData::LiteralTag());
84
const StringData hexPrefixUpper("0X", StringData::LiteralTag());
86
if (stringValue.size() > 2 && (stringValue.startsWith(hexPrefixLower) ||
87
stringValue.startsWith(hexPrefixUpper))) {
89
return stringValue.substr(2);
91
if (stringValue.size() > 1 && stringValue[0] == '0') {
99
*outputBase = inputBase;
100
if (inputBase == 16 && (stringValue.startsWith(hexPrefixLower) ||
101
stringValue.startsWith(hexPrefixUpper))) {
102
return stringValue.substr(2);
108
template <typename NumberType>
109
Status parseNumberFromStringWithBase(
110
const StringData& stringValue, int base, NumberType* result) {
112
typedef ::std::numeric_limits<NumberType> limits;
114
if (base == 1 || base < 0 || base > 36)
115
return Status(ErrorCodes::BadValue, "Invalid base", 0);
117
bool isNegative = false;
118
StringData str = _extractBase(_extractSign(stringValue, &isNegative), base, &base);
121
return Status(ErrorCodes::FailedToParse, "No digits");
125
if (limits::is_signed) {
126
for (size_t i = 0; i < str.size(); ++i) {
127
NumberType digitValue = NumberType(_digitValue(str[i]));
128
if (int(digitValue) >= base)
129
return Status(ErrorCodes::FailedToParse, "Bad digit");
131
// MSVC: warning C4146: unary minus operator applied to unsigned type, result still unsigned
132
// This code is statically known to be dead when NumberType is unsigned, so the warning is not real
133
#pragma warning(push)
134
#pragma warning(disable:4146)
135
if ((NumberType(limits::min() / base) > n) ||
136
((limits::min() - NumberType(n * base)) > -digitValue)) {
139
return Status(ErrorCodes::FailedToParse, "Underflow");
142
n *= NumberType(base);
143
n -= NumberType(digitValue);
147
return Status(ErrorCodes::FailedToParse, "Negative value");
151
for (size_t i = 0; i < str.size(); ++i) {
152
NumberType digitValue = NumberType(_digitValue(str[i]));
153
if (int(digitValue) >= base)
154
return Status(ErrorCodes::FailedToParse, "Bad digit");
155
if ((NumberType(limits::max() / base) < n) ||
156
(NumberType(limits::max() - n * base) < digitValue)) {
158
return Status(ErrorCodes::FailedToParse, "Overflow");
161
n *= NumberType(base);
162
n += NumberType(digitValue);
169
// Definition of the various supported implementations of parseNumberFromStringWithBase.
171
#define DEFINE_PARSE_NUMBER_FROM_STRING_WITH_BASE(NUMBER_TYPE) \
172
template Status parseNumberFromStringWithBase<NUMBER_TYPE>(const StringData&, int, NUMBER_TYPE*);
174
DEFINE_PARSE_NUMBER_FROM_STRING_WITH_BASE(long)
175
DEFINE_PARSE_NUMBER_FROM_STRING_WITH_BASE(long long)
176
DEFINE_PARSE_NUMBER_FROM_STRING_WITH_BASE(unsigned long)
177
DEFINE_PARSE_NUMBER_FROM_STRING_WITH_BASE(unsigned long long)
178
DEFINE_PARSE_NUMBER_FROM_STRING_WITH_BASE(short)
179
DEFINE_PARSE_NUMBER_FROM_STRING_WITH_BASE(unsigned short)
180
DEFINE_PARSE_NUMBER_FROM_STRING_WITH_BASE(int)
181
DEFINE_PARSE_NUMBER_FROM_STRING_WITH_BASE(unsigned int)
182
DEFINE_PARSE_NUMBER_FROM_STRING_WITH_BASE(int8_t);
183
DEFINE_PARSE_NUMBER_FROM_STRING_WITH_BASE(uint8_t);