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) {
EXPECT_EQ("foo", str('f', 'o', 'o'));
EXPECT_EQ("123 234 -123 e7",
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'};
ArrayPtr<char> a = buf;
......
......@@ -25,6 +25,7 @@
#include <float.h>
#include <errno.h>
#include <stdlib.h>
#include <stdint.h>
namespace kj {
......@@ -118,18 +119,39 @@ String heapString(const char* value, size_t size) {
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> result; \
result.setSize(sprintf(result.begin(), format, i)); \
return result; \
return hexImpl<type>(i); \
}
HEXIFY_INT(unsigned char, "%x");
HEXIFY_INT(unsigned short, "%x");
HEXIFY_INT(unsigned int, "%x");
HEXIFY_INT(unsigned long, "%lx");
HEXIFY_INT(unsigned long long, "%llx");
HEXIFY_INT(unsigned char);
HEXIFY_INT(unsigned short);
HEXIFY_INT(unsigned int);
HEXIFY_INT(unsigned long);
HEXIFY_INT(unsigned long long);
#undef HEXIFY_INT
......@@ -143,27 +165,54 @@ StringPtr Stringifier::operator*(bool b) const {
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> result; \
result.setSize(sprintf(result.begin(), format, i)); \
return result; \
return stringifyImpl<type, unsigned>(i); \
}
STRINGIFY_INT(signed char, "%d");
STRINGIFY_INT(unsigned char, "%u");
STRINGIFY_INT(short, "%d");
STRINGIFY_INT(unsigned short, "%u");
STRINGIFY_INT(int, "%d");
STRINGIFY_INT(unsigned int, "%u");
STRINGIFY_INT(long, "%ld");
STRINGIFY_INT(unsigned long, "%lu");
STRINGIFY_INT(long long, "%lld");
STRINGIFY_INT(unsigned long long, "%llu");
STRINGIFY_INT(const void*, "%p");
STRINGIFY_INT(signed char, uint);
STRINGIFY_INT(unsigned char, uint);
STRINGIFY_INT(short, uint);
STRINGIFY_INT(unsigned short, uint);
STRINGIFY_INT(int, uint);
STRINGIFY_INT(unsigned int, uint);
STRINGIFY_INT(long, unsigned long);
STRINGIFY_INT(unsigned long, unsigned long);
STRINGIFY_INT(long long, unsigned long long);
STRINGIFY_INT(unsigned long long, unsigned long long);
#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 {
// ----------------------------------------------------------------------
......
......@@ -368,12 +368,12 @@ struct Stringifier {
CappedArray<char, sizeof(unsigned long long) * 3 + 2> operator*(unsigned long long i) const;
CappedArray<char, 24> operator*(float 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>
String operator*(ArrayPtr<T> arr) const;
_::Delimited<ArrayPtr<T>> operator*(ArrayPtr<T> arr) const;
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?
template <typename T, typename Result = decltype(instance<T>().toString())>
......@@ -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
// 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
// type, basic char/byte sequences (ArrayPtr<byte>, String, etc.), as well as Array<T> as long
// as T can also be stringified safely. To safely stringify a delimited array, you must use
// kj::delimited(arr, delim) rather than the deprecated kj::strArray(arr, delim).
// type (but NOT floating-points), basic char/byte sequences (ArrayPtr<byte>, String, etc.), as
// well as Array<T> as long as T can also be stringified safely. To safely stringify a delimited
// array, you must use kj::delimited(arr, delim) rather than the deprecated
// kj::strArray(arr, delim).
char* end = _::fillLimited(buffer.begin(), buffer.end() - 1,
toCharSequence(kj::fwd<Params>(params))...);
......@@ -470,13 +471,13 @@ StringPtr strPreallocated(ArrayPtr<char> buffer, Params&&... params) {
namespace _ { // private
template <typename T>
inline String Stringifier::operator*(ArrayPtr<T> arr) const {
return strArray(arr, ", ");
inline _::Delimited<ArrayPtr<T>> Stringifier::operator*(ArrayPtr<T> arr) const {
return _::Delimited<ArrayPtr<T>>(arr, ", ");
}
template <typename T>
inline String Stringifier::operator*(const Array<T>& arr) const {
return strArray(arr, ", ");
inline _::Delimited<ArrayPtr<const T>> Stringifier::operator*(const Array<T>& arr) const {
return _::Delimited<ArrayPtr<const T>>(arr, ", ");
}
} // 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