blob: 4875dc61fa3c3ecbaa2113f89a3064ae31b99ab7 [file] [log] [blame]
// Copyright 2016 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "lib/fxl/strings/string_number_conversions.h"
#include <stdint.h>
#include <limits>
#include <type_traits>
#include "lib/fxl/logging.h"
namespace fxl {
namespace {
template <typename NumberType>
bool GetDigitValue(const char s, Base base, NumberType* out_digit) {
FXL_DCHECK(out_digit);
if (s < '0')
return false;
if (s <= '9') {
*out_digit = static_cast<NumberType>(s - '0');
return true;
}
if (base != Base::k16)
return false;
if (s >= 'a' && s <= 'f') {
*out_digit = static_cast<NumberType>(s - 'a' + 10);
return true;
}
if (s >= 'A' && s <= 'F') {
*out_digit = static_cast<NumberType>(s - 'A' + 10);
return true;
}
return false;
}
// Helper for |StringToNumberWithError()|. Note that this may modify |*number|
// even on failure.
template <typename NumberType>
bool StringToPositiveNumberWithError(const char* s, size_t length, Base base,
NumberType* number) {
const NumberType kBase = static_cast<NumberType>(base == Base::k10 ? 10 : 16);
constexpr NumberType kMaxAllowed = std::numeric_limits<NumberType>::max();
FXL_DCHECK(s);
FXL_DCHECK(length > 0u);
FXL_DCHECK(number);
*number = 0;
for (size_t i = 0; i < length; i++) {
NumberType new_digit;
if (!GetDigitValue(s[i], base, &new_digit))
return false;
// This is really a check of "*number * kBase + new_digit > kMaxAllowed":
if (*number > kMaxAllowed / kBase ||
(*number == kMaxAllowed / kBase && new_digit > kMaxAllowed % kBase))
return false;
*number = *number * kBase + new_digit;
}
return true;
}
// Helper for |StringToNumberWithError()|. Note that this may modify |*number|
// even on failure.
template <typename NumberType>
bool StringToNegativeNumberWithError(const char* s, size_t length, Base base,
NumberType* number) {
const NumberType kBase = static_cast<NumberType>(base == Base::k10 ? 10 : 16);
constexpr NumberType kMinAllowed = std::numeric_limits<NumberType>::min();
FXL_DCHECK(s);
FXL_DCHECK(length > 0u);
FXL_DCHECK(number);
*number = 0;
for (size_t i = 0; i < length; i++) {
NumberType new_digit;
if (!GetDigitValue(s[i], base, &new_digit))
return false;
// This is really a check of "*number * kBase - new_digit > kMinAllowed":
if (*number < kMinAllowed / kBase ||
(kMinAllowed / kBase == *number && new_digit > -(kMinAllowed % kBase)))
return false;
*number = *number * kBase - new_digit;
}
return true;
}
} // namespace
template <typename NumberType>
std::string NumberToString(NumberType number, Base base) {
// Special-case zero (since nonzero cases naturally produce digits).
if (!number)
return std::string("0");
using UnsignedNumberType = typename std::make_unsigned<NumberType>::type;
// Note: The negative case is safe, since the standard requires that, e.g.,
// for n a negative int32_t, |static_cast<uint32_t>(n)| = 2^32 - n and for a
// uint32_t m, |-m| = 2^32 - m.
bool number_is_negative = (number < static_cast<NumberType>(0));
UnsignedNumberType abs_number = number_is_negative
? -static_cast<UnsignedNumberType>(number)
: static_cast<UnsignedNumberType>(number);
char buf[50]; // Big enough to hold the result from even a 128-bit number.
size_t i = sizeof(buf);
while (abs_number) {
i--;
if (base == Base::k10) {
buf[i] = '0' + abs_number % 10u;
abs_number /= 10u;
} else {
UnsignedNumberType val = abs_number % 16u;
buf[i] = (val < 10) ? ('0' + val) : ('A' + val % 10u);
abs_number /= 16u;
}
}
if (number_is_negative) {
i--;
buf[i] = '-';
}
return std::string(buf + i, buf + sizeof(buf));
}
template <typename NumberType>
bool StringToNumberWithError(fxl::StringView string, NumberType* number,
Base base) {
FXL_DCHECK(number);
if (string.empty())
return false;
const char* s = string.data();
size_t length = string.size();
NumberType result = 0;
if (std::is_signed<NumberType>::value && string[0] == '-') {
if (length < 2)
return false;
if (!StringToNegativeNumberWithError<NumberType>(s + 1, length - 1u, base,
&result))
return false;
} else {
if (!StringToPositiveNumberWithError<NumberType>(s, length, base, &result))
return false;
}
*number = result;
return true;
}
// Explicit instantiatiations for (u)intN_t; count on (unsigned) int being one
// of these:
template std::string NumberToString<int8_t>(int8_t number, Base base);
template std::string NumberToString<uint8_t>(uint8_t number, Base base);
template std::string NumberToString<int16_t>(int16_t number, Base base);
template std::string NumberToString<uint16_t>(uint16_t number, Base base);
template std::string NumberToString<int32_t>(int32_t number, Base base);
template std::string NumberToString<uint32_t>(uint32_t number, Base base);
template std::string NumberToString<int64_t>(int64_t number, Base base);
template std::string NumberToString<uint64_t>(uint64_t number, Base base);
template bool StringToNumberWithError<int8_t>(fxl::StringView string,
int8_t* number, Base base);
template bool StringToNumberWithError<uint8_t>(fxl::StringView string,
uint8_t* number, Base base);
template bool StringToNumberWithError<int16_t>(fxl::StringView string,
int16_t* number, Base base);
template bool StringToNumberWithError<uint16_t>(fxl::StringView string,
uint16_t* number, Base base);
template bool StringToNumberWithError<int32_t>(fxl::StringView string,
int32_t* number, Base base);
template bool StringToNumberWithError<uint32_t>(fxl::StringView string,
uint32_t* number, Base base);
template bool StringToNumberWithError<int64_t>(fxl::StringView string,
int64_t* number, Base base);
template bool StringToNumberWithError<uint64_t>(fxl::StringView string,
uint64_t* number, Base base);
} // namespace fxl