Skip to content

Commit

Permalink
Remove dead std::to_chars version of float-to-str conversion
Browse files Browse the repository at this point in the history
Summary:
This removes the dead code path that does float-to-string conversion with `std::to_chars`.

This increased binary size on android because android apps bundle a copy of the C++ standard library in each APK. This was the opposite of the intention to reduce binary size for android by relying on the standard library instead of double-conversion.

Reviewed By: Gownta

Differential Revision: D66378465

fbshipit-source-id: 7ba44c3388e704a867197e366f0e7d2740f0a849
  • Loading branch information
skrueger authored and facebook-github-bot committed Nov 28, 2024
1 parent 46429aa commit 0870c6d
Show file tree
Hide file tree
Showing 3 changed files with 7 additions and 655 deletions.
355 changes: 0 additions & 355 deletions third-party/folly/src/folly/Conv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -704,361 +704,6 @@ template Expected<__int128, ConversionCode> str_to_integral<__int128>(
template Expected<unsigned __int128, ConversionCode>
str_to_integral<unsigned __int128>(StringPiece* src) noexcept;
#endif

#if defined(FOLLY_CONV_AVALIABILITY_TO_CHARS_FLOATING_POINT) && \
FOLLY_CONV_AVALIABILITY_TO_CHARS_FLOATING_POINT == 1
DtoaFlagsSet::DtoaFlagsSet(DtoaFlags flags) : flags_(flags) {}

bool DtoaFlagsSet::isSet(DtoaFlags flag) const {
return (flags_ & flag) == flag;
}

bool DtoaFlagsSet::emitPositiveExponentSign() const {
return isSet(DtoaFlags::EMIT_POSITIVE_EXPONENT_SIGN);
}

bool DtoaFlagsSet::emitTrailingDecimalPoint() const {
return isSet(DtoaFlags::EMIT_TRAILING_DECIMAL_POINT);
}

bool DtoaFlagsSet::emitTrailingZeroAfterPoint() const {
return isSet(DtoaFlags::EMIT_TRAILING_ZERO_AFTER_POINT);
}

bool DtoaFlagsSet::uniqueZero() const {
return isSet(DtoaFlags::UNIQUE_ZERO);
}

bool DtoaFlagsSet::noTrailingZero() const {
return isSet(DtoaFlags::NO_TRAILING_ZERO);
}

int ParsedDecimal::numPrecisionFigures() const {
int numInts = 0;

bool intIsZero = true;
int numLeadingIntZeros = 0;
bool isLeadingIntZero = true;
for (char* p = integerBegin; p && p != integerEnd; p++) {
if (*p == '0') {
if (isLeadingIntZero) {
numLeadingIntZeros += 1;
} else {
numInts += 1;
}
} else if (std::isdigit(*p)) {
intIsZero = false;
isLeadingIntZero = false;
numInts += 1;
} else {
folly::throw_exception<std::runtime_error>("non-numeric int");
}
}

bool fractionalIsZero = true;
int numFractional = 0;
int numLeadingFractionalZeros = 0;
bool isLeadingFractionalZero = true;
for (char* p = fractionalBegin; p && p != fractionalEnd; p++) {
if (*p == '0') {
if (isLeadingFractionalZero) {
numLeadingFractionalZeros += 1;
} else {
numFractional += 1;
}
} else if (std::isdigit(*p)) {
fractionalIsZero = false;
isLeadingFractionalZero = false;
numFractional += 1;
} else {
folly::throw_exception<std::runtime_error>("non-numeric frac");
}
}

if (intIsZero && fractionalIsZero) {
return numLeadingIntZeros + numLeadingFractionalZeros;
} else if (intIsZero) {
return numLeadingFractionalZeros + numFractional;
} else if (fractionalIsZero) {
return numInts + numLeadingFractionalZeros + numFractional;
} else {
return numInts + numLeadingFractionalZeros + numFractional;
}
}

std::optional<detail::ParsedDecimal::FractionalSuffix>
ParsedDecimal::fractionalSuffix() const {
if (exponentSymbol) {
if (exponentEnd) {
return std::make_pair(exponentSymbol, exponentEnd);
} else if (exponentSign) {
return std::make_pair(exponentSymbol, exponentSign);
} else {
return std::make_pair(exponentSymbol, exponentSymbol + 1);
}
} else if (exponentSign) {
if (exponentEnd) {
return std::make_pair(exponentSign, exponentEnd);
} else {
return std::make_pair(exponentSign, exponentSign + 1);
}
} else if (exponentBegin) {
if (exponentEnd) {
return std::make_pair(exponentEnd, exponentEnd);
} else {
return std::make_pair(exponentBegin, exponentSign + 1);
}
} else {
return std::nullopt;
}
}

void ParsedDecimal::shiftFractionalSuffixPtrs(size_t amount) {
if (exponentSymbol) {
exponentSymbol += amount;
}
if (exponentSign) {
exponentSign += amount;
}
if (exponentBegin) {
exponentBegin += amount;
}
if (exponentEnd) {
exponentEnd += amount;
}
}

namespace {

struct Stream : std::istream {
struct CharBuf : std::streambuf {
CharBuf(char* begin, char* end) { setg(begin, begin, end); }

char* pos() const { return gptr(); }
};
CharBuf& buf_;

explicit Stream(CharBuf& buf) : std::istream(&buf), buf_(buf) {}

char* pos() { return buf_.pos(); }

void advance() { get(); }
};

} // namespace

ParsedDecimal::ParsedDecimal(char* begin, char* end) {
if (!begin || !end || begin >= end) {
folly::throw_exception<std::invalid_argument>("invalid args");
}

Stream::CharBuf buf(begin, end);
Stream stream(buf);
if (stream.peek() == '-') {
negativeSign = stream.pos();
stream.advance();
}

if (char c = stream.peek(); std::isdigit(c)) {
integerBegin = stream.pos();

while (!stream.eof() && std::isdigit(stream.peek())) {
stream.advance();
}

integerEnd = stream.pos();
}

if (stream.eof()) {
if (!integerBegin) {
folly::throw_exception<std::invalid_argument>("no int part");
}

return;
}

if (stream.peek() == '.') {
decimalPoint = stream.pos();
stream.advance();
}

if (stream.eof()) {
if (!integerBegin) {
folly::throw_exception<std::invalid_argument>("no int part");
}
return;
}

if (char c = stream.peek(); std::isdigit(c)) {
fractionalBegin = stream.pos();

while (!stream.eof() && std::isdigit(stream.peek())) {
stream.advance();
}

fractionalEnd = stream.pos();
}

if (!integerBegin && !fractionalBegin) {
// there was no integer or fractional part.
folly::throw_exception<std::invalid_argument>("no int or frac part");
}

if (stream.eof()) {
return;
}

if (stream.peek() == 'e') {
exponentSymbol = stream.pos();
stream.advance();

if (stream.eof()) {
return;
}

if (char c = stream.peek(); c == '-' || c == '+') {
exponentSign = stream.pos();
stream.advance();
}

if (char c = stream.peek(); std::isdigit(c)) {
exponentBegin = stream.pos();
while (!stream.eof() && std::isdigit(stream.peek())) {
stream.advance();
}

exponentEnd = stream.pos();
}
}

while (!stream.eof()) {
int c = stream.get();
if (c != '\0' && !std::isspace(c)) {
folly::throw_exception<std::invalid_argument>("unexpected chars");
}
}
}

std::pair<char*, char*> formatAsDoubleConversion(
bool valueIsZero,
DtoaMode mode,
unsigned int numDigits,
DtoaFlags flags,
char* resultBegin,
char* resultEnd,
char* bufferEnd) {
detail::ParsedDecimal parsedDecimal(resultBegin, resultEnd);
detail::DtoaFlagsSet flagsSet{flags};
if (parsedDecimal.negativeSign && flagsSet.uniqueZero() && valueIsZero) {
// skip the negative sign (-) if it's a zero and UNIQUE_ZERO is set
resultBegin += 1;
}

unsigned int numTrailingZerosToAdd = 0;
if (!flagsSet.noTrailingZero() && mode == DtoaMode::PRECISION) {
// std::to_chars outputs no trailing zeros, so if it's not set, add
// trailing zeros
unsigned int numPrecisionFigures = parsedDecimal.numPrecisionFigures();
if (numDigits > numPrecisionFigures) {
numTrailingZerosToAdd = numDigits - numPrecisionFigures;
}
}

bool insertDecimalPoint = false;
char* insertionPoint;
if (parsedDecimal.fractionalEnd) {
insertionPoint = parsedDecimal.fractionalEnd;
} else if (parsedDecimal.decimalPoint) {
insertionPoint = parsedDecimal.decimalPoint + 1;
} else {
insertionPoint = parsedDecimal.integerEnd;
if (flagsSet.emitTrailingDecimalPoint() || numTrailingZerosToAdd > 0) {
insertDecimalPoint = true;
}

if (flagsSet.emitTrailingZeroAfterPoint()) {
numTrailingZerosToAdd += 1;
}
}

unsigned int numCharsToInsert =
numTrailingZerosToAdd + (insertDecimalPoint ? 1 : 0);

if (numCharsToInsert > 0) {
if (resultEnd + numCharsToInsert > bufferEnd) {
folly::throw_exception<std::invalid_argument>("buffer too small");
}

std::optional<detail::ParsedDecimal::FractionalSuffix> fractionalsuffix =
parsedDecimal.fractionalSuffix();
if (fractionalsuffix.has_value()) {
auto [fractionalSuffixBegin, fractionalSuffixEnd] = *fractionalsuffix;
std::memmove(
insertionPoint + numCharsToInsert,
fractionalSuffixBegin,
fractionalSuffixEnd - fractionalSuffixBegin);
parsedDecimal.shiftFractionalSuffixPtrs(numCharsToInsert);
}

resultEnd += numCharsToInsert;
}

if (insertDecimalPoint) {
*insertionPoint++ = '.';
}

while (numTrailingZerosToAdd) {
*insertionPoint++ = '0';
numTrailingZerosToAdd -= 1;
}

if (parsedDecimal.exponentSymbol) {
// std::tochars outputs a lowercase e and it needs to be uppercase.
*parsedDecimal.exponentSymbol = 'E';
}

size_t charsToRemove = 0;
char* removalBegin = nullptr;
if (!flagsSet.emitPositiveExponentSign() && parsedDecimal.exponentSign &&
*parsedDecimal.exponentSign == '+') {
// std::to_chars outputs a + sign, remove it if the flag wasn't set.
// e.g., 1.23e+45 -> 1.23e45
removalBegin = parsedDecimal.exponentSign;
charsToRemove += 1;
}

if (char* p = parsedDecimal.exponentBegin; p && *p == '0') {
// std::to_chars outputs a leading zero, remove it to match
// double_conversion formating. e.g., 1.23e+04 -> 1.23e4
if (!removalBegin) {
removalBegin = p;
}

while (p != parsedDecimal.exponentEnd && *p == '0') {
charsToRemove += 1;
p += 1;
}

if (p == parsedDecimal.exponentEnd) {
// they all were 0 digits. keep a single 0.
charsToRemove -= 1;
p -= 1;
if (p == removalBegin) {
// there was only one 0, keep it.
removalBegin = nullptr;
}
}
}

if (charsToRemove && removalBegin) {
size_t len = resultEnd - (removalBegin + charsToRemove);
std::memmove(removalBegin, removalBegin + charsToRemove, len);
resultEnd -= charsToRemove;
}

return std::pair{resultBegin, resultEnd};
}
#endif // FOLLY_CONV_AVALIABILITY_TO_CHARS_FLOATING_POINT
} // namespace detail

ConversionError makeConversionError(ConversionCode code, StringPiece input) {
Expand Down
Loading

0 comments on commit 0870c6d

Please sign in to comment.