Introduce ctl::to_string()

This commit is contained in:
Justine Tunney 2024-07-01 05:40:38 -07:00
parent acbabedf27
commit e627bfa359
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
11 changed files with 432 additions and 40 deletions

View file

@ -284,10 +284,10 @@ include third_party/ncurses/BUILD.mk # │
include third_party/readline/BUILD.mk # │ include third_party/readline/BUILD.mk # │
include third_party/libunwind/BUILD.mk # | include third_party/libunwind/BUILD.mk # |
include third_party/libcxxabi/BUILD.mk # | include third_party/libcxxabi/BUILD.mk # |
include third_party/double-conversion/BUILD.mk # │
include ctl/BUILD.mk # │ include ctl/BUILD.mk # │
include third_party/libcxx/BUILD.mk # │ include third_party/libcxx/BUILD.mk # │
include third_party/openmp/BUILD.mk # │ include third_party/openmp/BUILD.mk # │
include third_party/double-conversion/BUILD.mk # │
include third_party/pcre/BUILD.mk # │ include third_party/pcre/BUILD.mk # │
include third_party/less/BUILD.mk # │ include third_party/less/BUILD.mk # │
include net/https/BUILD.mk # │ include net/https/BUILD.mk # │

View file

@ -21,6 +21,7 @@ CTL_A_DIRECTDEPS = \
LIBC_NEXGEN32E \ LIBC_NEXGEN32E \
LIBC_STDIO \ LIBC_STDIO \
LIBC_STR \ LIBC_STR \
THIRD_PARTY_DOUBLECONVERSION \
THIRD_PARTY_GDTOA \ THIRD_PARTY_GDTOA \
THIRD_PARTY_LIBCXXABI \ THIRD_PARTY_LIBCXXABI \
THIRD_PARTY_LIBUNWIND \ THIRD_PARTY_LIBUNWIND \

35
ctl/dubble.cc Normal file
View file

@ -0,0 +1,35 @@
// -*- mode:c++; indent-tabs-mode:nil; c-basic-offset:4; coding:utf-8 -*-
// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi
//
// Copyright 2024 Justine Alexandra Roberts Tunney
//
// Permission to use, copy, modify, and/or distribute this software for
// any purpose with or without fee is hereby granted, provided that the
// above copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
// PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include "dubble.h"
namespace ctl {
const double_conversion::DoubleToStringConverter kDoubleToPrintfG(
double_conversion::DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN |
double_conversion::DoubleToStringConverter::NO_TRAILING_ZERO,
"inf",
"nan",
'e',
-6,
10, // let 32-bit ints be represented without exponent
6,
0,
0);
} // namespace ctl

13
ctl/dubble.h Normal file
View file

@ -0,0 +1,13 @@
// -*-mode:c++;indent-tabs-mode:nil;c-basic-offset:4;tab-width:8;coding:utf-8-*-
// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi
#ifndef COSMOPOLITAN_CTL_DUBBLE_H_
#define COSMOPOLITAN_CTL_DUBBLE_H_
#include "third_party/double-conversion/double-to-string.h"
namespace ctl {
extern const double_conversion::DoubleToStringConverter kDoubleToPrintfG;
} // namespace ctl
#endif // COSMOPOLITAN_CTL_DUBBLE_H_

View file

@ -17,11 +17,15 @@
// PERFORMANCE OF THIS SOFTWARE. // PERFORMANCE OF THIS SOFTWARE.
#include "ostream.h" #include "ostream.h"
#include "dubble.h"
#include "libc/fmt/itoa.h"
#include "libc/stdio/stdio.h" #include "libc/stdio/stdio.h"
#include "string_view.h" #include "string_view.h"
namespace ctl { namespace ctl {
extern const double_conversion::DoubleToStringConverter kDoubleToPrintfG;
ostream cout(stdout); ostream cout(stdout);
ostream cerr(stderr); ostream cerr(stderr);
@ -50,7 +54,7 @@ ostream&
ostream::operator<<(const char* str) ostream::operator<<(const char* str)
{ {
if (good() && str) if (good() && str)
if (fprintf(file_, "%s", str) < 0) if (fputs(str, file_) < 0)
setstate(badbit); setstate(badbit);
return *this; return *this;
} }
@ -67,35 +71,84 @@ ostream::operator<<(char c)
ostream& ostream&
ostream::operator<<(int n) ostream::operator<<(int n)
{ {
if (good()) if (good()) {
if (fprintf(file_, "%d", n) < 0) char buf[12];
FormatInt32(buf, n);
if (fputs(buf, file_) < 0)
setstate(badbit); setstate(badbit);
}
return *this;
}
ostream&
ostream::operator<<(unsigned n)
{
if (good()) {
char buf[12];
FormatUint32(buf, n);
if (fputs(buf, file_) < 0)
setstate(badbit);
}
return *this; return *this;
} }
ostream& ostream&
ostream::operator<<(long n) ostream::operator<<(long n)
{ {
if (good()) if (good()) {
if (fprintf(file_, "%ld", n) < 0) char buf[21];
FormatInt64(buf, n);
if (fputs(buf, file_) < 0)
setstate(badbit); setstate(badbit);
}
return *this;
}
ostream&
ostream::operator<<(unsigned long n)
{
if (good()) {
char buf[21];
FormatUint64(buf, n);
if (fputs(buf, file_) < 0)
setstate(badbit);
}
return *this;
}
ostream&
ostream::operator<<(float f)
{
if (good()) {
char buf[128];
double_conversion::StringBuilder b(buf, sizeof(buf));
kDoubleToPrintfG.ToShortestSingle(f, &b);
b.Finalize();
if (fputs(buf, file_) < 0)
setstate(badbit);
}
return *this; return *this;
} }
ostream& ostream&
ostream::operator<<(double d) ostream::operator<<(double d)
{ {
if (good()) if (good()) {
if (fprintf(file_, "%f", d) < 0) char buf[128];
double_conversion::StringBuilder b(buf, sizeof(buf));
kDoubleToPrintfG.ToShortest(d, &b);
b.Finalize();
if (fputs(buf, file_) < 0)
setstate(badbit); setstate(badbit);
}
return *this; return *this;
} }
ostream& ostream&
ostream::operator<<(const string_view& s) ostream::operator<<(const string_view& s)
{ {
if (good()) if (good() && s.size())
if (fprintf(file_, "%.*s", (int)s.size(), s.data()) < 0) if (!fwrite(s.data(), s.size(), 1, file_))
setstate(badbit); setstate(badbit);
return *this; return *this;
} }
@ -106,7 +159,7 @@ ostream::operator<<(bool b)
if (good()) { if (good()) {
const char* value = const char* value =
(flags() & boolalpha) ? (b ? "true" : "false") : (b ? "1" : "0"); (flags() & boolalpha) ? (b ? "true" : "false") : (b ? "1" : "0");
if (fprintf(file_, "%s", value) < 0) if (fputs(value, file_) < 0)
setstate(badbit); setstate(badbit);
} }
return *this; return *this;

View file

@ -16,12 +16,15 @@ class ostream : public ios
virtual ~ostream(); virtual ~ostream();
ostream& operator<<(const char*); ostream& operator<<(const char*);
ostream& operator<<(bool);
ostream& operator<<(char); ostream& operator<<(char);
ostream& operator<<(int); ostream& operator<<(int);
ostream& operator<<(unsigned);
ostream& operator<<(long); ostream& operator<<(long);
ostream& operator<<(unsigned long);
ostream& operator<<(float);
ostream& operator<<(double); ostream& operator<<(double);
ostream& operator<<(const ctl::string_view&); ostream& operator<<(const ctl::string_view&);
ostream& operator<<(bool);
ostream& operator<<(ostream& (*)(ostream&)); ostream& operator<<(ostream& (*)(ostream&));
ostream& put(char); ostream& put(char);

View file

@ -17,28 +17,30 @@
// PERFORMANCE OF THIS SOFTWARE. // PERFORMANCE OF THIS SOFTWARE.
#include "ostringstream.h" #include "ostringstream.h"
#include "dubble.h"
#include "libc/fmt/itoa.h" #include "libc/fmt/itoa.h"
#include "libc/stdio/stdio.h"
namespace ctl { namespace ctl {
extern const double_conversion::DoubleToStringConverter kDoubleToPrintfG;
ostringstream::ostringstream() : buffer_(), write_pos_(0) ostringstream::ostringstream() : buffer_(), write_pos_(0)
{ {
} }
ostringstream::ostringstream(const ctl::string_view& str) ostringstream::ostringstream(const string_view& str)
: buffer_(str), write_pos_(0) : buffer_(str), write_pos_(0)
{ {
} }
ctl::string string
ostringstream::str() const ostringstream::str() const
{ {
return buffer_; return buffer_;
} }
void void
ostringstream::str(const ctl::string& s) ostringstream::str(const string& s)
{ {
buffer_ = s; buffer_ = s;
write_pos_ = 0; write_pos_ = 0;
@ -65,7 +67,7 @@ ostringstream::operator<<(char c)
} }
ostringstream& ostringstream&
ostringstream::operator<<(const ctl::string_view& s) ostringstream::operator<<(const string_view& s)
{ {
if (good()) { if (good()) {
if (write_pos_ + s.size() <= buffer_.size()) { if (write_pos_ + s.size() <= buffer_.size()) {
@ -82,36 +84,40 @@ ostringstream::operator<<(const ctl::string_view& s)
ostringstream& ostringstream&
ostringstream::operator<<(int n) ostringstream::operator<<(int n)
{ {
char temp[12]; if (good()) {
if (good()) char buf[12];
*this << ctl::string_view(temp, FormatInt32(temp, n) - temp); *this << string_view(buf, FormatInt32(buf, n) - buf);
}
return *this; return *this;
} }
ostringstream& ostringstream&
ostringstream::operator<<(unsigned int n) ostringstream::operator<<(unsigned n)
{ {
char temp[12]; if (good()) {
if (good()) char buf[12];
*this << ctl::string_view(temp, FormatUint32(temp, n) - temp); *this << string_view(buf, FormatUint32(buf, n) - buf);
}
return *this; return *this;
} }
ostringstream& ostringstream&
ostringstream::operator<<(long n) ostringstream::operator<<(long n)
{ {
char temp[21]; if (good()) {
if (good()) char buf[21];
*this << ctl::string_view(temp, FormatInt64(temp, n) - temp); *this << string_view(buf, FormatInt64(buf, n) - buf);
}
return *this; return *this;
} }
ostringstream& ostringstream&
ostringstream::operator<<(unsigned long n) ostringstream::operator<<(unsigned long n)
{ {
char temp[21]; if (good()) {
if (good()) char buf[21];
*this << ctl::string_view(temp, FormatUint64(temp, n) - temp); *this << string_view(buf, FormatUint64(buf, n) - buf);
}
return *this; return *this;
} }
@ -119,9 +125,11 @@ ostringstream&
ostringstream::operator<<(float f) ostringstream::operator<<(float f)
{ {
if (good()) { if (good()) {
char temp[32]; char buf[128];
int len = snprintf(temp, sizeof(temp), "%g", f); double_conversion::StringBuilder b(buf, sizeof(buf));
*this << ctl::string_view(temp, len); kDoubleToPrintfG.ToShortestSingle(f, &b);
b.Finalize();
*this << string_view(buf);
} }
return *this; return *this;
} }
@ -130,9 +138,11 @@ ostringstream&
ostringstream::operator<<(double d) ostringstream::operator<<(double d)
{ {
if (good()) { if (good()) {
char temp[32]; char buf[128];
int len = snprintf(temp, sizeof(temp), "%g", d); double_conversion::StringBuilder b(buf, sizeof(buf));
*this << ctl::string_view(temp, len); kDoubleToPrintfG.ToShortest(d, &b);
b.Finalize();
*this << string_view(buf);
} }
return *this; return *this;
} }

View file

@ -338,11 +338,27 @@ class string
return *this; return *this;
} }
string operator+(const char c) const noexcept
{
char s[2] = { c };
return strcat(*this, s);
}
string operator+(const string& s) const noexcept
{
return strcat(*this, s);
}
string operator+(const ctl::string_view s) const noexcept string operator+(const ctl::string_view s) const noexcept
{ {
return strcat(*this, s); return strcat(*this, s);
} }
string operator+(const char* s) const noexcept
{
return strcat(*this, s);
}
int compare(const ctl::string_view s) const noexcept int compare(const ctl::string_view s) const noexcept
{ {
return strcmp(*this, s); return strcmp(*this, s);
@ -409,6 +425,33 @@ static_assert(sizeof(string) == __::string_size);
static_assert(sizeof(__::small_string) == __::string_size); static_assert(sizeof(__::small_string) == __::string_size);
static_assert(sizeof(__::big_string) == __::string_size); static_assert(sizeof(__::big_string) == __::string_size);
ctl::string
to_string(int);
ctl::string
to_string(long);
ctl::string
to_string(long long);
ctl::string
to_string(unsigned);
ctl::string
to_string(unsigned long);
ctl::string
to_string(unsigned long long);
ctl::string
to_string(float);
ctl::string
to_string(double);
ctl::string
to_string(long double);
} // namespace ctl } // namespace ctl
#pragma GCC diagnostic push #pragma GCC diagnostic push

109
ctl/to_string.cc Normal file
View file

@ -0,0 +1,109 @@
// -*- mode:c++; indent-tabs-mode:nil; c-basic-offset:4; coding:utf-8 -*-
// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi
//
// Copyright 2024 Justine Alexandra Roberts Tunney
//
// Permission to use, copy, modify, and/or distribute this software for
// any purpose with or without fee is hereby granted, provided that the
// above copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
// PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include "dubble.h"
#include "libc/fmt/itoa.h"
#include "libc/math.h"
#include "string.h"
#include "third_party/gdtoa/gdtoa.h"
namespace ctl {
extern const double_conversion::DoubleToStringConverter kDoubleToPrintfG;
string
to_string(int value)
{
char buf[12];
return { buf, FormatInt32(buf, value) - buf };
}
string
to_string(unsigned value)
{
char buf[12];
return { buf, FormatUint32(buf, value) - buf };
}
string
to_string(long value)
{
char buf[21];
return { buf, FormatInt64(buf, value) - buf };
}
string
to_string(unsigned long value)
{
char buf[21];
return { buf, FormatUint64(buf, value) - buf };
}
string
to_string(long long value)
{
char buf[21];
return { buf, FormatInt64(buf, value) - buf };
}
string
to_string(unsigned long long value)
{
char buf[21];
return { buf, FormatUint64(buf, value) - buf };
}
string
to_string(float value)
{
char buf[128];
double_conversion::StringBuilder b(buf, sizeof(buf));
kDoubleToPrintfG.ToShortestSingle(value, &b);
b.Finalize();
return string(buf);
}
string
to_string(double value)
{
char buf[128];
double_conversion::StringBuilder b(buf, sizeof(buf));
kDoubleToPrintfG.ToShortest(value, &b);
b.Finalize();
return string(buf);
}
string
to_string(long double value)
{
#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
return to_string((double)value);
#else
char buf[128];
#if LDBL_MANT_DIG == 113
g_Qfmt_p(buf, &value, 16, 128, NIK(2, 0, 0));
#elif LDBL_MANT_DIG == 64
g_xfmt_p(buf, &value, 16, 128, NIK(2, 0, 0));
#else
#error "unsupported long double"
#endif
return string(buf);
#endif
}
} // namespace ctl

View file

@ -35,10 +35,9 @@ main()
// Test basic copy // Test basic copy
ctl::copy(src.begin(), src.end(), dest.begin()); ctl::copy(src.begin(), src.end(), dest.begin());
for (size_t i = 0; i < 5; ++i) { for (size_t i = 0; i < 5; ++i)
if (dest[i] != src[i]) if (dest[i] != src[i])
return 1; return 1;
}
// Test partial copy // Test partial copy
ctl::array<int, 5> dest2 = { 0, 0, 0, 0, 0 }; ctl::array<int, 5> dest2 = { 0, 0, 0, 0, 0 };
@ -57,10 +56,9 @@ main()
// Test copy with empty range // Test copy with empty range
ctl::array<int, 5> dest4 = { 0, 0, 0, 0, 0 }; ctl::array<int, 5> dest4 = { 0, 0, 0, 0, 0 };
ctl::copy(src.begin(), src.begin(), dest4.begin()); ctl::copy(src.begin(), src.begin(), dest4.begin());
for (size_t i = 0; i < 5; ++i) { for (size_t i = 0; i < 5; ++i)
if (dest4[i] != 0) if (dest4[i] != 0)
return 4; return 4;
}
// Test copy return value // Test copy return value
ctl::array<int, 5> dest5 = { 0, 0, 0, 0, 0 }; ctl::array<int, 5> dest5 = { 0, 0, 0, 0, 0 };

127
test/ctl/to_string_test.cc Normal file
View file

@ -0,0 +1,127 @@
// -*- mode:c++; indent-tabs-mode:nil; c-basic-offset:4; coding:utf-8 -*-
// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi
//
// Copyright 2024 Justine Alexandra Roberts Tunney
//
// Permission to use, copy, modify, and/or distribute this software for
// any purpose with or without fee is hereby granted, provided that the
// above copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
// PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include "ctl/string.h"
#include "libc/intrin/kprintf.h"
#include "libc/limits.h"
#include "libc/math.h"
#include "libc/mem/leaks.h"
int
main()
{
// test integer conversion
{
if (ctl::to_string(0) != "0")
return 1;
if (ctl::to_string(3) != "3")
return 1;
if (ctl::to_string(INT32_MAX) != "2147483647")
return 2;
if (ctl::to_string(INT32_MIN) != "-2147483648")
return 3;
if (ctl::to_string(UINT32_MAX) != "4294967295")
return 4;
if (ctl::to_string(INT64_MAX) != "9223372036854775807")
return 5;
if (ctl::to_string(INT64_MIN) != "-9223372036854775808")
return 6;
if (ctl::to_string(UINT64_MAX) != "18446744073709551615")
return 7;
}
// test float conversion
// we diverge from std::to_string(float) because it's garbage
{
if (ctl::to_string(0.f) != "0") // 0.000000
return 8;
if (ctl::to_string(-0.f) != "-0") // 0.000000
return 9;
if (ctl::to_string(3.f) != "3") // 3.000000
return 10;
if (ctl::to_string(3.14f) != "3.14") // 3.140000
return 11;
if (ctl::to_string(3.140001f) != "3.140001")
return 12;
if (ctl::to_string(1000000000.f) != "1000000000") // 1000000000.000000
return 12;
if (ctl::to_string(10000000000.f) != "1e+10") // 10000000000.000000
return 12;
if (ctl::to_string(NAN) != "nan")
return 13;
if (ctl::to_string(INFINITY) != "inf")
return 14;
if (ctl::to_string(-INFINITY) != "-inf")
return 15;
if (ctl::to_string(FLT_MIN) != "1.1754944e-38") // 0.000000 lool
return 16;
if (ctl::to_string(-FLT_MIN) != "-1.1754944e-38")
return 17;
if (ctl::to_string(FLT_MAX) != "3.4028235e+38")
return 18;
if (ctl::to_string(-FLT_MAX) != "-3.4028235e+38")
return 19;
}
// test double conversion
{
if (ctl::to_string(0.) != "0")
return 20;
if (ctl::to_string(-0.) != "-0")
return 21;
if (ctl::to_string(3.) != "3")
return 22;
if (ctl::to_string(2147483647.) != "2147483647")
return 23;
if (ctl::to_string(-2147483648.) != "-2147483648")
return 23;
if (ctl::to_string(10000000000.) != "1e+10")
return 23;
if (ctl::to_string(3.14) != "3.14")
return 23;
if (ctl::to_string(3.140001) != "3.140001")
return 24;
if (ctl::to_string(DBL_MIN) != "2.2250738585072014e-308")
return 25;
if (ctl::to_string(-DBL_MIN) != "-2.2250738585072014e-308")
return 26;
if (ctl::to_string(DBL_MAX) != "1.7976931348623157e+308")
return 27;
if (ctl::to_string(-DBL_MAX) != "-1.7976931348623157e+308")
return 28;
}
// test long double conversion
{
if (ctl::to_string(0.L) != "0")
return 29;
if (ctl::to_string(-0.L) != "-0")
return 30;
if (ctl::to_string(3.L) != "3")
return 31;
if (ctl::to_string(3.14L) != "3.14")
return 32;
if (ctl::to_string(LDBL_MAX) != "1.189731495357232e+4932")
return 33;
if (ctl::to_string(-LDBL_MAX) != "-1.189731495357232e+4932")
return 34;
}
CheckForMemoryLeaks();
}