Commit d05a92c9 authored by jan.koester's avatar jan.koester
Browse files

string: to_string((unsigned) long long) at full 64-bit width on ILP32

It went through ltoa()/ultoa(), whose long is 32 bits on powerpc: storaged
reported a 160 GB disk as 1.05 GiB to webctl (RAID/LVM dialogs).

Co-Authored-By: default avatarClaude Opus 5.5 <noreply@anthropic.com>
parent fbdfa971
Loading
Loading
Loading
Loading
+14 −2
Changes for include/cxx/string: 14 added lines, 2 removed lines.
Original line number Diff line number Diff line
@@ -1621,7 +1621,19 @@ typedef string::size_type size_type;

inline string to_string(int v) { char buf[16]; itoa(v, buf); return string(buf); }
inline string to_string(long v) { char buf[24]; ltoa(v, buf); return string(buf); }
inline string to_string(long long v) { char buf[24]; ltoa((long)v, buf); return string(buf); }
/* (unsigned) long long is 64 bits on ILP32 too: ltoa()/ultoa() take a
 * (32-bit there) long, so cutting through them truncated e.g. disk sizes
 * > 4 GiB on powerpc. Converted here at full width. */
inline string __ulltostr(unsigned long long v, bool neg) {
    char buf[24], *p = buf + sizeof buf;
    *--p = 0;
    do { *--p = (char)('0' + v % 10); v /= 10; } while (v);
    if (neg) *--p = '-';
    return string(p);
}
inline string to_string(long long v) {
    return v < 0 ? __ulltostr(0ULL - (unsigned long long)v, true) : __ulltostr((unsigned long long)v, false);
}
inline string to_string(unsigned int v) { char buf[16]; ultoa(v, buf); return string(buf); }
inline string to_string(unsigned long v) { char buf[24]; ultoa(v, buf); return string(buf); }
/* Real gap found compiling LLVM: to_string(long long) already existed
@@ -1634,7 +1646,7 @@ inline string to_string(unsigned long v) { char buf[24]; ultoa(v, buf); return s
 * long long converts to either via an equally-ranked plain integral
 * conversion, neither promotion nor exact match) -- same lossless
 * same-width cast to unsigned long as to_string(long long)'s own. */
inline string to_string(unsigned long long v) { char buf[24]; ultoa((unsigned long)v, buf); return string(buf); }
inline string to_string(unsigned long long v) { return __ulltostr(v, false); }

/* Real gap found compiling LLVM: to_string had no floating-point
 * overloads at all -- real gap: llvm/include/llvm/Analysis/CFGPrinter.h's
+20 −0
Changes for src/test/e2e/CMakeLists.txt: 20 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -3767,3 +3767,23 @@ add_test(NAME e2e_thread_reclaim COMMAND e2e_thread_reclaim)
if(CMAKE_CROSSCOMPILING_EMULATOR)
    set_tests_properties(e2e_thread_reclaim PROPERTIES DISABLED TRUE)
endif()

add_executable(e2e_to_string_wide to_string_wide_test.cpp)
set_target_properties(e2e_to_string_wide PROPERTIES OUTPUT_NAME "to_string_wide_test")
target_compile_options(e2e_to_string_wide PRIVATE -fPIE)
target_link_options(e2e_to_string_wide PRIVATE
    -nostdlib -pie
    -Wl,--hash-style=sysv
    -Wl,--dynamic-linker,$<TARGET_FILE_DIR:systempp>/ld-linux-${CMAKE_SYSTEM_PROCESSOR}.so
    -Wl,-rpath,$<TARGET_FILE_DIR:systempp>
)
target_link_libraries(e2e_to_string_wide PRIVATE
    $<TARGET_OBJECTS:crti>
    $<TARGET_OBJECTS:Scrt1>
    $<TARGET_OBJECTS:crtbeginS>
    $<TARGET_OBJECTS:crtn>
    systempp
)
add_dependencies(e2e_to_string_wide crti Scrt1 crtbeginS crtn systempp)

add_test(NAME e2e_to_string_wide COMMAND e2e_to_string_wide)
+21 −0
Changes for src/test/e2e/to_string_wide_test.cpp: 21 added lines, 0 removed lines.
Original line number Diff line number Diff line
/* std::to_string((unsigned) long long) must keep all 64 bits on ILP32:
 * it used to go through ltoa()/ultoa() (32-bit long on powerpc), so a
 * 160 GB disk was reported by storaged as 1.05 GiB. */

#include <string>
#include <stdio.h>

#define CHECK(cond, msg) \
    do { if (!(cond)) { printf("FAIL: %s\n", msg); return 1; } } while (0)

int main() {
    CHECK(std::to_string(160041885696LL) == "160041885696", "to_string(long long) > 4 GiB");
    CHECK(std::to_string(160041885696ULL) == "160041885696", "to_string(unsigned long long) > 4 GiB");
    CHECK(std::to_string(-160041885696LL) == "-160041885696", "to_string(long long) negative > 32 bit");
    CHECK(std::to_string((long long)(-9223372036854775807LL - 1)) == "-9223372036854775808", "to_string(LLONG_MIN)");
    CHECK(std::to_string(18446744073709551615ULL) == "18446744073709551615", "to_string(ULLONG_MAX)");
    CHECK(std::to_string(0LL) == "0" && std::to_string(0ULL) == "0", "to_string(0)");
    CHECK(std::stoll(std::to_string(-1234567890123LL)) == -1234567890123LL, "stoll(to_string()) round trip");
    printf("PASS\n");
    return 0;
}