Commit 2d291f8d authored by Kenton Varda's avatar Kenton Varda

Ensure primitive stringifiers are signal-safe.

Alas, this means we can't use sprintf() anymore. Ugh.
parent 0d851985
...@@ -35,6 +35,12 @@ TEST(String, Str) { ...@@ -35,6 +35,12 @@ TEST(String, Str) {
EXPECT_EQ("foo", str('f', 'o', 'o')); EXPECT_EQ("foo", str('f', 'o', 'o'));
EXPECT_EQ("123 234 -123 e7", EXPECT_EQ("123 234 -123 e7",
str((int8_t)123, " ", (uint8_t)234, " ", (int8_t)-123, " ", hex((uint8_t)0xe7))); str((int8_t)123, " ", (uint8_t)234, " ", (int8_t)-123, " ", hex((uint8_t)0xe7)));
EXPECT_EQ("-128 -32768 -2147483648 -9223372036854775808",
str((signed char)-128, ' ', (signed short)-32768, ' ',
((int)-2147483647) - 1, ' ', ((long long)-9223372036854775807ll) - 1))
EXPECT_EQ("ff ffff ffffffff ffffffffffffffff",
str(hex((uint8_t)0xff), ' ', hex((uint16_t)0xffff), ' ', hex((uint32_t)0xffffffffu), ' ',
hex((uint64_t)0xffffffffffffffffull)));
char buf[3] = {'f', 'o', 'o'}; char buf[3] = {'f', 'o', 'o'};
ArrayPtr<char> a = buf; ArrayPtr<char> a = buf;
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include <float.h> #include <float.h>
#include <errno.h> #include <errno.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdint.h>
namespace kj { namespace kj {
...@@ -118,18 +119,39 @@ String heapString(const char* value, size_t size) { ...@@ -118,18 +119,39 @@ String heapString(const char* value, size_t size) {
return String(buffer, size, _::HeapArrayDisposer::instance); return String(buffer, size, _::HeapArrayDisposer::instance);
} }
#define HEXIFY_INT(type, format) \ template <typename T>
static CappedArray<char, sizeof(T) * 2 + 1> hexImpl(T i) {
// We don't use sprintf() because it's not async-signal-safe (for strPreallocated()).
CappedArray<char, sizeof(T) * 2 + 1> result;
char reverse[sizeof(T) * 2];
char* p = reverse;
if (i == 0) {
*p++ = 0;
} else {
while (i > 0) {
*p++ = i % 16;
i /= 16;
}
}
char* p2 = result.begin();
while (p > reverse) {
*p2++ = "0123456789abcdef"[*--p];
}
result.setSize(p2 - result.begin());
return result;
}
#define HEXIFY_INT(type) \
CappedArray<char, sizeof(type) * 2 + 1> hex(type i) { \ CappedArray<char, sizeof(type) * 2 + 1> hex(type i) { \
CappedArray<char, sizeof(type) * 2 + 1> result; \ return hexImpl<type>(i); \
result.setSize(sprintf(result.begin(), format, i)); \
return result; \
} }
HEXIFY_INT(unsigned char, "%x"); HEXIFY_INT(unsigned char);
HEXIFY_INT(unsigned short, "%x"); HEXIFY_INT(unsigned short);
HEXIFY_INT(unsigned int, "%x"); HEXIFY_INT(unsigned int);
HEXIFY_INT(unsigned long, "%lx"); HEXIFY_INT(unsigned long);
HEXIFY_INT(unsigned long long, "%llx"); HEXIFY_INT(unsigned long long);
#undef HEXIFY_INT #undef HEXIFY_INT
...@@ -143,27 +165,54 @@ StringPtr Stringifier::operator*(bool b) const { ...@@ -143,27 +165,54 @@ StringPtr Stringifier::operator*(bool b) const {
return b ? StringPtr("true") : StringPtr("false"); return b ? StringPtr("true") : StringPtr("false");
} }
#define STRINGIFY_INT(type, format) \ template <typename T, typename Unsigned>
static CappedArray<char, sizeof(T) * 3 + 2> stringifyImpl(T i) {
// We don't use sprintf() because it's not async-signal-safe (for strPreallocated()).
CappedArray<char, sizeof(T) * 3 + 2> result;
bool negative = i < 0;
Unsigned u = negative ? -i : i;
char reverse[sizeof(T) * 3 + 1];
char* p = reverse;
if (u == 0) {
*p++ = 0;
} else {
while (u > 0) {
*p++ = u % 10;
u /= 10;
}
}
char* p2 = result.begin();
if (negative) *p2++ = '-';
while (p > reverse) {
*p2++ = '0' + *--p;
}
result.setSize(p2 - result.begin());
return result;
}
#define STRINGIFY_INT(type, unsigned) \
CappedArray<char, sizeof(type) * 3 + 2> Stringifier::operator*(type i) const { \ CappedArray<char, sizeof(type) * 3 + 2> Stringifier::operator*(type i) const { \
CappedArray<char, sizeof(type) * 3 + 2> result; \ return stringifyImpl<type, unsigned>(i); \
result.setSize(sprintf(result.begin(), format, i)); \
return result; \
} }
STRINGIFY_INT(signed char, "%d"); STRINGIFY_INT(signed char, uint);
STRINGIFY_INT(unsigned char, "%u"); STRINGIFY_INT(unsigned char, uint);
STRINGIFY_INT(short, "%d"); STRINGIFY_INT(short, uint);
STRINGIFY_INT(unsigned short, "%u"); STRINGIFY_INT(unsigned short, uint);
STRINGIFY_INT(int, "%d"); STRINGIFY_INT(int, uint);
STRINGIFY_INT(unsigned int, "%u"); STRINGIFY_INT(unsigned int, uint);
STRINGIFY_INT(long, "%ld"); STRINGIFY_INT(long, unsigned long);
STRINGIFY_INT(unsigned long, "%lu"); STRINGIFY_INT(unsigned long, unsigned long);
STRINGIFY_INT(long long, "%lld"); STRINGIFY_INT(long long, unsigned long long);
STRINGIFY_INT(unsigned long long, "%llu"); STRINGIFY_INT(unsigned long long, unsigned long long);
STRINGIFY_INT(const void*, "%p");
#undef STRINGIFY_INT #undef STRINGIFY_INT
CappedArray<char, sizeof(const void*) * 2 + 1> Stringifier::operator*(const void* i) const { \
return hexImpl<uintptr_t>(reinterpret_cast<uintptr_t>(i));
}
namespace { namespace {
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
......
...@@ -368,12 +368,12 @@ struct Stringifier { ...@@ -368,12 +368,12 @@ struct Stringifier {
CappedArray<char, sizeof(unsigned long long) * 3 + 2> operator*(unsigned long long i) const; CappedArray<char, sizeof(unsigned long long) * 3 + 2> operator*(unsigned long long i) const;
CappedArray<char, 24> operator*(float f) const; CappedArray<char, 24> operator*(float f) const;
CappedArray<char, 32> operator*(double f) const; CappedArray<char, 32> operator*(double f) const;
CappedArray<char, sizeof(const void*) * 3 + 2> operator*(const void* s) const; CappedArray<char, sizeof(const void*) * 2 + 1> operator*(const void* s) const;
template <typename T> template <typename T>
String operator*(ArrayPtr<T> arr) const; _::Delimited<ArrayPtr<T>> operator*(ArrayPtr<T> arr) const;
template <typename T> template <typename T>
String operator*(const Array<T>& arr) const; _::Delimited<ArrayPtr<const T>> operator*(const Array<T>& arr) const;
#if KJ_COMPILER_SUPPORTS_STL_STRING_INTEROP // supports expression SFINAE? #if KJ_COMPILER_SUPPORTS_STL_STRING_INTEROP // supports expression SFINAE?
template <typename T, typename Result = decltype(instance<T>().toString())> template <typename T, typename Result = decltype(instance<T>().toString())>
...@@ -457,9 +457,10 @@ StringPtr strPreallocated(ArrayPtr<char> buffer, Params&&... params) { ...@@ -457,9 +457,10 @@ StringPtr strPreallocated(ArrayPtr<char> buffer, Params&&... params) {
// This is useful for optimization. It can also potentially be used safely in async signal // This is useful for optimization. It can also potentially be used safely in async signal
// handlers. HOWEVER, to use in an async signal handler, all of the stringifiers for the inputs // handlers. HOWEVER, to use in an async signal handler, all of the stringifiers for the inputs
// must also be signal-safe. KJ guarantees signal safety when stringifying any built-in integer // must also be signal-safe. KJ guarantees signal safety when stringifying any built-in integer
// type, basic char/byte sequences (ArrayPtr<byte>, String, etc.), as well as Array<T> as long // type (but NOT floating-points), basic char/byte sequences (ArrayPtr<byte>, String, etc.), as
// as T can also be stringified safely. To safely stringify a delimited array, you must use // well as Array<T> as long as T can also be stringified safely. To safely stringify a delimited
// kj::delimited(arr, delim) rather than the deprecated kj::strArray(arr, delim). // array, you must use kj::delimited(arr, delim) rather than the deprecated
// kj::strArray(arr, delim).
char* end = _::fillLimited(buffer.begin(), buffer.end() - 1, char* end = _::fillLimited(buffer.begin(), buffer.end() - 1,
toCharSequence(kj::fwd<Params>(params))...); toCharSequence(kj::fwd<Params>(params))...);
...@@ -470,13 +471,13 @@ StringPtr strPreallocated(ArrayPtr<char> buffer, Params&&... params) { ...@@ -470,13 +471,13 @@ StringPtr strPreallocated(ArrayPtr<char> buffer, Params&&... params) {
namespace _ { // private namespace _ { // private
template <typename T> template <typename T>
inline String Stringifier::operator*(ArrayPtr<T> arr) const { inline _::Delimited<ArrayPtr<T>> Stringifier::operator*(ArrayPtr<T> arr) const {
return strArray(arr, ", "); return _::Delimited<ArrayPtr<T>>(arr, ", ");
} }
template <typename T> template <typename T>
inline String Stringifier::operator*(const Array<T>& arr) const { inline _::Delimited<ArrayPtr<const T>> Stringifier::operator*(const Array<T>& arr) const {
return strArray(arr, ", "); return _::Delimited<ArrayPtr<const T>>(arr, ", ");
} }
} // namespace _ (private) } // namespace _ (private)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment