Commit 36c3f8e9 authored by Kenton Varda's avatar Kenton Varda

Stringification for all.

parent 47194adc
......@@ -25,6 +25,7 @@
#define CAPNPROTO_BLOB_H_
#include <kj/common.h>
#include <kj/string.h>
#include "common.h"
#include <string.h>
......@@ -133,6 +134,11 @@ public:
inline operator const char*() const { return data(); }
};
inline kj::StringPtr KJ_STRINGIFY(Text::Reader reader) {
// TODO(soon): Use size().
return reader.c_str();
}
class Data::Builder {
// Like Data::Reader except the pointers aren't const, and it can't be implicitly constructed from
// other types.
......@@ -187,6 +193,8 @@ public:
inline char* begin() const { return bytes; }
inline char* end() const { return bytes + size_; }
inline Data::Reader asReader() { return Data::Reader(bytes, size_); }
private:
char* bytes;
uint size_;
......@@ -207,10 +215,17 @@ public:
inline operator char*() const { return data(); }
inline operator const char*() const { return data(); }
inline Text::Reader asReader() { return Text::Reader(begin(), size()); }
private:
static char nulstr[1];
};
inline kj::StringPtr KJ_STRINGIFY(Text::Builder builder) {
// TODO(soon): Use size().
return builder.c_str();
}
inline bool operator==(const char* a, Data::Reader b) { return Data::Reader(a) == b; }
inline bool operator==(const char* a, Data::Builder b) { return Data::Reader(a) == (Data::Reader)b; }
inline bool operator==(Data::Reader a, Data::Builder b) { return a == (Data::Reader)b; }
......
......@@ -32,7 +32,6 @@
#include <kj/io.h>
#include "../schema-loader.h"
#include "../dynamic.h"
#include "../stringify.h"
#include <unistd.h>
#include <unordered_map>
#include <vector>
......@@ -67,8 +66,8 @@ private:
void fill(char* textPos, Branch* branchesPos, TextBlob&& first, Rest&&... rest);
template <typename T>
auto toContainer(T&& t) -> decltype(kj::STR * t) {
return kj::STR * t;
auto toContainer(T&& t) -> decltype(kj::toCharSequence(kj::fwd(t))) {
return kj::toCharSequence(kj::fwd(t));
}
TextBlob&& toContainer(TextBlob&& t) {
return kj::mv(t);
......@@ -135,7 +134,7 @@ void TextBlob::fill(char* textPos, Branch* branchesPos) {
template <typename First, typename... Rest>
void TextBlob::fill(char* textPos, Branch* branchesPos, First&& first, Rest&&... rest) {
textPos = kj::fill(textPos, kj::fwd<First>(first));
textPos = kj::internal::fill(textPos, kj::fwd<First>(first));
fill(textPos, branchesPos, kj::fwd<Rest>(rest)...);
}
......@@ -206,8 +205,6 @@ struct Indent {
inline Iterator end() const { return Iterator(amount); }
};
inline Indent operator*(kj::Stringifier, Indent i) { return i; }
// =======================================================================================
SchemaLoader schemaLoader;
......@@ -365,13 +362,13 @@ TextBlob genValue(schema::Type::Reader type, schema::Value::Reader value, Schema
case schema::Value::Body::UINT64_VALUE: return text(body.getUint64Value());
case schema::Value::Body::FLOAT32_VALUE: return text(body.getFloat32Value());
case schema::Value::Body::FLOAT64_VALUE: return text(body.getFloat64Value());
case schema::Value::Body::TEXT_VALUE: return stringify(body.getTextValue());
case schema::Value::Body::DATA_VALUE: return stringify(body.getDataValue());
case schema::Value::Body::TEXT_VALUE: return text(DynamicValue::Reader(body.getTextValue()));
case schema::Value::Body::DATA_VALUE: return text(DynamicValue::Reader(body.getDataValue()));
case schema::Value::Body::LIST_VALUE: {
PRECOND(type.getBody().which() == schema::Type::Body::LIST_TYPE, "type/value mismatch");
auto value = body.getListValue<DynamicList>(
ListSchema::of(type.getBody().getListType(), scope));
return text(stringify(value));
return text(value);
}
case schema::Value::Body::ENUM_VALUE: {
PRECOND(type.getBody().which() == schema::Type::Body::ENUM_TYPE, "type/value mismatch");
......@@ -386,7 +383,7 @@ TextBlob genValue(schema::Type::Reader type, schema::Value::Reader value, Schema
PRECOND(type.getBody().which() == schema::Type::Body::STRUCT_TYPE, "type/value mismatch");
auto value = body.getStructValue<DynamicStruct>(
scope.getDependency(type.getBody().getStructType()).asStruct());
return text(stringify(value));
return text(value);
}
case schema::Value::Body::INTERFACE_VALUE: {
return text("");
......
......@@ -1318,6 +1318,27 @@ DynamicList::Reader DynamicList::Builder::asReader() {
// =======================================================================================
DynamicValue::Reader DynamicValue::Builder::asReader() {
switch (type) {
case UNKNOWN: return Reader();
case VOID: return Reader(voidValue);
case BOOL: return Reader(boolValue);
case INT: return Reader(intValue);
case UINT: return Reader(uintValue);
case FLOAT: return Reader(floatValue);
case TEXT: return Reader(textValue.asReader());
case DATA: return Reader(dataValue.asReader());
case LIST: return Reader(listValue.asReader());
case ENUM: return Reader(enumValue);
case STRUCT: return Reader(structValue.asReader());
case UNION: return Reader(unionValue.asReader());
case INTERFACE: FAIL_CHECK("Interfaces not implemented."); return Reader();
case OBJECT: return Reader(objectValue);
}
FAIL_CHECK("Missing switch case.");
return Reader();
}
namespace {
template <typename T>
......
......@@ -191,6 +191,9 @@ private:
: schema(schema), reader(reader) {}
friend struct DynamicStruct;
friend class DynamicUnion::Builder;
friend kj::String internal::unionString(
internal::StructReader reader, const internal::RawSchema& schema, uint memberIndex);
};
class DynamicUnion::Builder {
......@@ -227,6 +230,8 @@ public:
Data::Builder initObjectAsData(Text::Reader name, uint size);
// Convenience methods that identify the member by text name.
Reader asReader();
private:
StructSchema::Union schema;
internal::StructBuilder builder;
......@@ -287,7 +292,7 @@ private:
friend class MessageBuilder;
template <typename T, ::capnproto::Kind k>
friend struct ::capnproto::ToDynamic_;
friend kj::String internal::debugString(
friend kj::String internal::structString(
internal::StructReader reader, const internal::RawSchema& schema);
};
......@@ -644,6 +649,17 @@ private:
// specialization. Has a method apply() which does the work.
};
kj::String KJ_STRINGIFY(DynamicValue::Reader value);
kj::String KJ_STRINGIFY(DynamicValue::Builder value);
kj::String KJ_STRINGIFY(DynamicEnum value);
kj::String KJ_STRINGIFY(DynamicObject value);
kj::String KJ_STRINGIFY(DynamicUnion::Reader value);
kj::String KJ_STRINGIFY(DynamicUnion::Builder value);
kj::String KJ_STRINGIFY(DynamicStruct::Reader value);
kj::String KJ_STRINGIFY(DynamicStruct::Builder value);
kj::String KJ_STRINGIFY(DynamicList::Reader value);
kj::String KJ_STRINGIFY(DynamicList::Builder value);
// -------------------------------------------------------------------
// Inject the ability to use DynamicStruct for message roots and Dynamic{Struct,List} for
// generated Object accessors.
......@@ -869,6 +885,12 @@ struct DynamicObject::AsImpl<T, Kind::LIST> {
// -------------------------------------------------------------------
inline DynamicUnion::Reader DynamicUnion::Builder::asReader() {
return DynamicUnion::Reader(schema, builder.asReader());
}
// -------------------------------------------------------------------
template <typename T>
typename T::Reader DynamicStruct::Reader::as() {
static_assert(kind<T>() == Kind::STRUCT,
......
......@@ -172,13 +172,29 @@ inline const RawSchema& rawSchema() {
template <typename T>
struct TypeIdFor;
kj::String debugString(StructReader reader, const RawSchema& schema);
// Declared here so that we can declare inline debugString() methods on generated types.
template <typename T>
struct UnionMemberIndexFor;
template <typename T>
inline uint unionMemberIndex() { return UnionMemberIndexFor<T>::value; }
template <typename T>
struct UnionParentTypeFor;
template <typename T>
using UnionParentType = typename UnionParentTypeFor<T>::Type;
kj::String structString(StructReader reader, const RawSchema& schema);
kj::String unionString(StructReader reader, const RawSchema& schema, uint memberIndex);
// Declared here so that we can declare inline stringify methods on generated types.
// Defined in stringify.c++, which depends on dynamic.c++, which is allowed not to be linked in.
template <typename T>
inline kj::String debugString(StructReader reader) {
return debugString(reader, rawSchema<T>());
inline kj::String structString(StructReader reader) {
return structString(reader, rawSchema<T>());
}
template <typename T>
inline kj::String unionString(StructReader reader) {
return unionString(reader, rawSchema<UnionParentType<T>>(), unionMemberIndex<T>());
}
} // namespace internal
......@@ -199,6 +215,7 @@ inline constexpr uint64_t typeId() { return internal::TypeIdFor<T>::typeId; }
#define CAPNPROTO_DEFINE_ENUM(type) \
constexpr Kind KindOf<type>::kind; \
constexpr uint64_t TypeIdFor<type>::typeId;
#define CAPNPROTO_DECLARE_STRUCT(type, id, dataWordSize, pointerCount, preferredElementEncoding) \
template <> struct KindOf<type> { static constexpr Kind kind = Kind::STRUCT; }; \
template <> struct StructSizeFor<type> { \
......@@ -213,6 +230,15 @@ inline constexpr uint64_t typeId() { return internal::TypeIdFor<T>::typeId; }
constexpr Kind KindOf<type>::kind; \
constexpr StructSize StructSizeFor<type>::value; \
constexpr uint64_t TypeIdFor<type>::typeId;
#define CAPNPROTO_DECLARE_UNION(type, parentType, memberIndex) \
template <> struct KindOf<type> { static constexpr Kind kind = Kind::UNION; }; \
template <> struct UnionMemberIndexFor<type> { static constexpr uint value = memberIndex; }; \
template <> struct UnionParentTypeFor<type> { typedef parentType Type; }
#define CAPNPROTO_DEFINE_UNION(type) \
constexpr Kind KindOf<type>::kind; \
constexpr uint UnionMemberIndexFor<type>::value;
#define CAPNPROTO_DECLARE_INTERFACE(type, id) \
template <> struct KindOf<type> { static constexpr Kind kind = Kind::INTERFACE; }; \
template <> struct TypeIdFor<type> { static constexpr uint64_t typeId = 0x##id; }; \
......
......@@ -126,6 +126,7 @@ enum class Kind: uint8_t {
BLOB,
ENUM,
STRUCT,
UNION,
INTERFACE,
LIST,
UNKNOWN
......
......@@ -42,8 +42,7 @@ TEST(SchemaLoader, Load) {
Schema struct8Schema = loader.load(Schema::from<test::TestLists::Struct8>().getProto());
Schema structPSchema = loader.load(Schema::from<test::TestLists::StructP>().getProto());
EXPECT_STREQ(nativeSchema.getProto().debugString().cStr(),
testListsSchema.getProto().debugString().cStr());
EXPECT_EQ(kj::str(nativeSchema.getProto()), kj::str(testListsSchema.getProto()));
EXPECT_FALSE(testListsSchema == nativeSchema);
EXPECT_FALSE(struct32Schema == Schema::from<test::TestLists::Struct32>());
......@@ -165,8 +164,8 @@ TEST(SchemaLoader, Upgrade) {
StructSchema schema = loader.get(typeId<test::TestOldVersion>()).asStruct();
EXPECT_STREQ(Schema::from<test::TestOldVersion>().getProto().debugString().cStr(),
schema.getProto().debugString().cStr());
EXPECT_EQ(kj::str(Schema::from<test::TestOldVersion>().getProto()),
kj::str(schema.getProto()));
loadUnderAlternateTypeId<test::TestNewVersion>(loader, typeId<test::TestOldVersion>());
......@@ -185,8 +184,7 @@ TEST(SchemaLoader, Downgrade) {
StructSchema schema = loader.get(typeId<test::TestNewVersion>()).asStruct();
EXPECT_STREQ(Schema::from<test::TestNewVersion>().getProto().debugString().cStr(),
schema.getProto().debugString().cStr());
EXPECT_EQ(kj::str(Schema::from<test::TestNewVersion>().getProto()), kj::str(schema.getProto()));
loadUnderAlternateTypeId<test::TestOldVersion>(loader, typeId<test::TestNewVersion>());
......
......@@ -124,8 +124,10 @@ private:
return StructSchema(&internal::rawSchema<T>());
}
friend class Schema;
friend kj::String internal::debugString(
friend kj::String internal::structString(
internal::StructReader reader, const internal::RawSchema& schema);
friend kj::String internal::unionString(
internal::StructReader reader, const internal::RawSchema& schema, uint memberIndex);
};
class StructSchema::Member {
......
......@@ -45,12 +45,18 @@
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define CAPNPROTO_PRIVATE
#include "stringify.h"
#include "message.h"
#include "dynamic.h"
#include <kj/logging.h>
#include <gtest/gtest.h>
#include "test-util.h"
namespace kj {
inline std::ostream& operator<<(std::ostream& os, const kj::String& s) {
return os.write(s.begin(), s.size());
}
}
namespace capnproto {
namespace internal {
namespace {
......@@ -59,11 +65,11 @@ TEST(Stringify, DebugString) {
MallocMessageBuilder builder;
auto root = builder.initRoot<TestAllTypes>();
EXPECT_STREQ("()", root.debugString().cStr());
EXPECT_EQ("()", kj::str(root));
initTestMessage(root);
EXPECT_STREQ("("
EXPECT_EQ("("
"boolField = true, "
"int8Field = -123, "
"int16Field = -12345, "
......@@ -134,17 +140,39 @@ TEST(Stringify, DebugString) {
"(textField = \"structlist 2\"), "
"(textField = \"structlist 3\")], "
"enumList = [foo, garply])",
root.debugString().cStr());
kj::str(root));
}
TEST(Stringify, Unions) {
MallocMessageBuilder builder;
auto root = builder.initRoot<TestUnion>();
root.getUnion0().setU0f0s16(321);
root.getUnion1().setU1f0sp("foo");
root.getUnion2().setU2f0s1(true);
root.getUnion3().setU3f0s64(123456789012345678ll);
EXPECT_EQ("("
"union0 = u0f0s16(321), "
"union1 = u1f0sp(\"foo\"), "
"union2 = u2f0s1(true), "
"union3 = u3f0s64(123456789012345678))",
kj::str(root));
EXPECT_EQ("u0f0s16(321)", kj::str(root.getUnion0()));
EXPECT_EQ("u1f0sp(\"foo\")", kj::str(root.getUnion1()));
EXPECT_EQ("u2f0s1(true)", kj::str(root.getUnion2()));
EXPECT_EQ("u3f0s64(123456789012345678)", kj::str(root.getUnion3()));
}
TEST(Stringify, MoreValues) {
EXPECT_STREQ("123", stringify(123).cStr());
EXPECT_STREQ("1.23e47", stringify(123e45).cStr());
EXPECT_STREQ("\"foo\"", stringify("foo").cStr());
EXPECT_STREQ("\"\\a\\b\\n\\t\\\"\"", stringify("\a\b\n\t\"").cStr());
EXPECT_EQ("123", kj::str(DynamicValue::Reader(123)));
EXPECT_EQ("1.23e47", kj::str(DynamicValue::Reader(123e45)));
EXPECT_EQ("\"foo\"", kj::str(DynamicValue::Reader("foo")));
EXPECT_EQ("\"\\a\\b\\n\\t\\\"\"", kj::str(DynamicValue::Reader("\a\b\n\t\"")));
EXPECT_STREQ("foo", stringify(TestEnum::FOO).cStr());
EXPECT_STREQ("123", stringify(static_cast<TestEnum>(123)).cStr());
EXPECT_EQ("foo", kj::str(DynamicValue::Reader(TestEnum::FOO)));
EXPECT_EQ("123", kj::str(DynamicValue::Reader(static_cast<TestEnum>(123))));
}
} // namespace
......
......@@ -22,7 +22,7 @@
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define CAPNPROTO_PRIVATE
#include "stringify.h"
#include "dynamic.h"
#include <kj/logging.h>
#include <sstream>
......@@ -58,10 +58,10 @@ static void print(std::ostream& os, DynamicValue::Reader value,
break;
case DynamicValue::FLOAT: {
if (which == schema::Type::Body::FLOAT32_TYPE) {
auto buf = kj::STR * value.as<float>();
auto buf = kj::toCharSequence(value.as<float>());
os.write(buf.begin(), buf.size());
} else {
auto buf = kj::STR * value.as<double>();
auto buf = kj::toCharSequence(value.as<double>());
os.write(buf.begin(), buf.size());
}
break;
......@@ -170,8 +170,6 @@ static void print(std::ostream& os, DynamicValue::Reader value,
}
}
} // namespace
kj::String stringify(DynamicValue::Reader value) {
std::stringstream out;
print(out, value, schema::Type::Body::STRUCT_TYPE);
......@@ -179,12 +177,30 @@ kj::String stringify(DynamicValue::Reader value) {
return kj::heapString(content.data(), content.size());
}
} // namespace
kj::String KJ_STRINGIFY(DynamicValue::Reader value) { return stringify(value); }
kj::String KJ_STRINGIFY(DynamicValue::Builder value) { return stringify(value.asReader()); }
kj::String KJ_STRINGIFY(DynamicEnum value) { return stringify(value); }
kj::String KJ_STRINGIFY(DynamicObject value) { return stringify(value); }
kj::String KJ_STRINGIFY(DynamicUnion::Reader value) { return stringify(value); }
kj::String KJ_STRINGIFY(DynamicUnion::Builder value) { return stringify(value.asReader()); }
kj::String KJ_STRINGIFY(DynamicStruct::Reader value) { return stringify(value); }
kj::String KJ_STRINGIFY(DynamicStruct::Builder value) { return stringify(value.asReader()); }
kj::String KJ_STRINGIFY(DynamicList::Reader value) { return stringify(value); }
kj::String KJ_STRINGIFY(DynamicList::Builder value) { return stringify(value.asReader()); }
namespace internal {
kj::String debugString(StructReader reader, const RawSchema& schema) {
kj::String structString(StructReader reader, const RawSchema& schema) {
return stringify(DynamicStruct::Reader(StructSchema(&schema), reader));
}
kj::String unionString(StructReader reader, const RawSchema& schema, uint memberIndex) {
return stringify(DynamicUnion::Reader(
StructSchema(&schema).getMembers()[memberIndex].asUnion(), reader));
}
} // namespace internal
} // namespace capnproto
// Copyright (c) 2013, Kenton Varda <temporal@gmail.com>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef CAPNPROTO_STRINGIFY_H_
#define CAPNPROTO_STRINGIFY_H_
#include "dynamic.h"
namespace capnproto {
kj::String stringify(DynamicValue::Reader value);
// Stringify an arbitrary Cap'n Proto value. Note that DynamicValue::Reader can be implicitly
// constructed from any Cap'n Proto field type, so this will accept pretty much anything.
} // namespace capnproto
#endif // CAPNPROTO_STRINGIFY_H_
......@@ -353,6 +353,12 @@ struct TestLateUnion {
corge @5 :List(Int32);
grault @6 :Float32;
}
anotherUnion @7 union {
qux @8 :Text;
corge @9 :List(Int32);
grault @10 :Float32;
}
}
struct TestOldVersion {
......
......@@ -189,6 +189,14 @@ template <typename T> struct Decay_<const T> { typedef typename Decay_<T>::Type
template <typename T> struct Decay_<volatile T> { typedef typename Decay_<T>::Type Type; };
template <typename T> using Decay = typename Decay_<T>::Type;
template <bool b> struct EnableIf_;
template <> struct EnableIf_<true> { typedef void Type; };
template <bool b> using EnableIf = typename EnableIf_<b>::Type;
// Use like:
//
// template <typename T, typename = EnableIf<isValid<T>()>
// void func(T&& t);
template <typename T>
T instance() noexcept;
// Like std::declval, but doesn't transform T into an rvalue reference. If you want that, specify
......
......@@ -22,7 +22,7 @@
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "exception.h"
#include "util.h"
#include "string.h"
#include "logging.h"
#include <unistd.h>
#include <execinfo.h>
......@@ -30,7 +30,7 @@
namespace kj {
ArrayPtr<const char> operator*(const Stringifier&, Exception::Nature nature) {
ArrayPtr<const char> KJ_STRINGIFY(Exception::Nature nature) {
static const char* NATURE_STRINGS[] = {
"precondition not met",
"bug in code",
......@@ -44,7 +44,7 @@ ArrayPtr<const char> operator*(const Stringifier&, Exception::Nature nature) {
return arrayPtr(s, strlen(s));
}
ArrayPtr<const char> operator*(const Stringifier&, Exception::Durability durability) {
ArrayPtr<const char> KJ_STRINGIFY(Exception::Durability durability) {
static const char* DURABILITY_STRINGS[] = {
"temporary",
"permanent"
......@@ -149,7 +149,7 @@ void ExceptionCallback::onRecoverableException(Exception&& exception) {
if (std::uncaught_exception()) {
logMessage(str("unwind: ", exception.what(), '\n'));
} else {
throw std::move(exception);
throw kj::mv(exception);
}
#endif
}
......@@ -158,7 +158,7 @@ void ExceptionCallback::onFatalException(Exception&& exception) {
#if KJ_NO_EXCEPTIONS
logMessage(str(exception.what(), '\n'));
#else
throw std::move(exception);
throw kj::mv(exception);
#endif
}
......
......@@ -116,9 +116,8 @@ private:
mutable String whatBuffer;
};
struct Stringifier;
ArrayPtr<const char> operator*(const Stringifier&, Exception::Nature nature);
ArrayPtr<const char> operator*(const Stringifier&, Exception::Durability durability);
ArrayPtr<const char> KJ_STRINGIFY(Exception::Nature nature);
ArrayPtr<const char> KJ_STRINGIFY(Exception::Durability durability);
class ExceptionCallback {
// If you don't like C++ exceptions, you may implement and register an ExceptionCallback in order
......
......@@ -31,7 +31,7 @@ namespace kj {
Log::Severity Log::minSeverity = Log::Severity::INFO;
ArrayPtr<const char> operator*(const Stringifier&, Log::Severity severity) {
ArrayPtr<const char> KJ_STRINGIFY(Log::Severity severity) {
static const char* SEVERITY_STRINGS[] = {
"info",
"warning",
......@@ -163,20 +163,20 @@ static String makeDescription(DescriptionStyle style, const char* code, int erro
case LOG:
break;
case ASSERTION:
pos = fill(pos, expected, codeArray);
pos = internal::fill(pos, expected, codeArray);
break;
case SYSCALL:
pos = fill(pos, codeArray, colon, sysErrorArray);
pos = internal::fill(pos, codeArray, colon, sysErrorArray);
break;
}
for (size_t i = 0; i < argValues.size(); i++) {
if (i > 0 || style != LOG) {
pos = fill(pos, delim);
pos = internal::fill(pos, delim);
}
if (argNames[i].size() > 0 && argNames[i][0] != '\"') {
pos = fill(pos, argNames[i], sep);
pos = internal::fill(pos, argNames[i], sep);
}
pos = fill(pos, argValues[i]);
pos = internal::fill(pos, argValues[i]);
}
return result;
......
......@@ -96,7 +96,7 @@
#ifndef KJ_LOGGING_H_
#define KJ_LOGGING_H_
#include "util.h"
#include "string.h"
#include "exception.h"
namespace kj {
......@@ -208,7 +208,7 @@ private:
// Get the error code of the last error (e.g. from errno). Returns -1 on EINTR.
};
ArrayPtr<const char> operator*(const Stringifier&, Log::Severity severity);
ArrayPtr<const char> KJ_STRINGIFY(Log::Severity severity);
#define LOG(severity, ...) \
if (!::kj::Log::shouldLog(::kj::Log::Severity::severity)) {} else \
......
......@@ -21,7 +21,7 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "util.h"
#include "string.h"
#include <gtest/gtest.h>
#include <string>
......@@ -29,7 +29,7 @@ namespace kj {
namespace internal {
namespace {
TEST(Util, Foo) {
TEST(String, Str) {
EXPECT_EQ("foobar", str("foo", "bar"));
EXPECT_EQ("1 2 3 4", str(1, " ", 2u, " ", 3l, " ", 4ll));
EXPECT_EQ("1.5 foo 1e15 bar -3", str(1.5f, " foo ", 1e15, " bar ", -3));
......
......@@ -23,6 +23,11 @@
#include "string.h"
#include "logging.h"
#include <stdio.h>
#include <float.h>
#include <limits>
#include <errno.h>
#include <stdlib.h>
namespace kj {
......@@ -38,4 +43,280 @@ String heapString(const char* value, size_t size) {
return String(buffer, size, internal::HeapArrayDisposer::instance);
}
#define HEXIFY_INT(type, format) \
CappedArray<char, sizeof(type) * 4> hex(type i) { \
CappedArray<char, sizeof(type) * 4> result; \
result.setSize(sprintf(result.begin(), format, i)); \
return result; \
}
HEXIFY_INT(unsigned short, "%x");
HEXIFY_INT(unsigned int, "%x");
HEXIFY_INT(unsigned long, "%lx");
HEXIFY_INT(unsigned long long, "%llx");
#undef HEXIFY_INT
namespace internal {
StringPtr Stringifier::operator*(bool b) const {
return b ? StringPtr("true") : StringPtr("false");
}
#define STRINGIFY_INT(type, format) \
CappedArray<char, sizeof(type) * 4> Stringifier::operator*(type i) const { \
CappedArray<char, sizeof(type) * 4> result; \
result.setSize(sprintf(result.begin(), format, i)); \
return result; \
}
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");
#undef STRINGIFY_INT
namespace {
// ----------------------------------------------------------------------
// DoubleToBuffer()
// FloatToBuffer()
// Copied from Protocol Buffers, (C) Google, BSD license.
// Kenton wrote this code originally. The following commentary is
// from the original.
//
// Description: converts a double or float to a string which, if
// passed to NoLocaleStrtod(), will produce the exact same original double
// (except in case of NaN; all NaNs are considered the same value).
// We try to keep the string short but it's not guaranteed to be as
// short as possible.
//
// DoubleToBuffer() and FloatToBuffer() write the text to the given
// buffer and return it. The buffer must be at least
// kDoubleToBufferSize bytes for doubles and kFloatToBufferSize
// bytes for floats. kFastToBufferSize is also guaranteed to be large
// enough to hold either.
//
// We want to print the value without losing precision, but we also do
// not want to print more digits than necessary. This turns out to be
// trickier than it sounds. Numbers like 0.2 cannot be represented
// exactly in binary. If we print 0.2 with a very large precision,
// e.g. "%.50g", we get "0.2000000000000000111022302462515654042363167".
// On the other hand, if we set the precision too low, we lose
// significant digits when printing numbers that actually need them.
// It turns out there is no precision value that does the right thing
// for all numbers.
//
// Our strategy is to first try printing with a precision that is never
// over-precise, then parse the result with strtod() to see if it
// matches. If not, we print again with a precision that will always
// give a precise result, but may use more digits than necessary.
//
// An arguably better strategy would be to use the algorithm described
// in "How to Print Floating-Point Numbers Accurately" by Steele &
// White, e.g. as implemented by David M. Gay's dtoa(). It turns out,
// however, that the following implementation is about as fast as
// DMG's code. Furthermore, DMG's code locks mutexes, which means it
// will not scale well on multi-core machines. DMG's code is slightly
// more accurate (in that it will never use more digits than
// necessary), but this is probably irrelevant for most users.
//
// Rob Pike and Ken Thompson also have an implementation of dtoa() in
// third_party/fmt/fltfmt.cc. Their implementation is similar to this
// one in that it makes guesses and then uses strtod() to check them.
// Their implementation is faster because they use their own code to
// generate the digits in the first place rather than use snprintf(),
// thus avoiding format string parsing overhead. However, this makes
// it considerably more complicated than the following implementation,
// and it is embedded in a larger library. If speed turns out to be
// an issue, we could re-implement this in terms of their
// implementation.
// ----------------------------------------------------------------------
#ifdef _WIN32
// MSVC has only _snprintf, not snprintf.
//
// MinGW has both snprintf and _snprintf, but they appear to be different
// functions. The former is buggy. When invoked like so:
// char buffer[32];
// snprintf(buffer, 32, "%.*g\n", FLT_DIG, 1.23e10f);
// it prints "1.23000e+10". This is plainly wrong: %g should never print
// trailing zeros after the decimal point. For some reason this bug only
// occurs with some input values, not all. In any case, _snprintf does the
// right thing, so we use it.
#define snprintf _snprintf
#endif
inline bool IsNaN(double value) {
// NaN is never equal to anything, even itself.
return value != value;
}
// In practice, doubles should never need more than 24 bytes and floats
// should never need more than 14 (including null terminators), but we
// overestimate to be safe.
static const int kDoubleToBufferSize = 32;
static const int kFloatToBufferSize = 24;
static inline bool IsValidFloatChar(char c) {
return ('0' <= c && c <= '9') ||
c == 'e' || c == 'E' ||
c == '+' || c == '-';
}
void DelocalizeRadix(char* buffer) {
// Fast check: if the buffer has a normal decimal point, assume no
// translation is needed.
if (strchr(buffer, '.') != NULL) return;
// Find the first unknown character.
while (IsValidFloatChar(*buffer)) ++buffer;
if (*buffer == '\0') {
// No radix character found.
return;
}
// We are now pointing at the locale-specific radix character. Replace it
// with '.'.
*buffer = '.';
++buffer;
if (!IsValidFloatChar(*buffer) && *buffer != '\0') {
// It appears the radix was a multi-byte character. We need to remove the
// extra bytes.
char* target = buffer;
do { ++buffer; } while (!IsValidFloatChar(*buffer) && *buffer != '\0');
memmove(target, buffer, strlen(buffer) + 1);
}
}
void RemovePlus(char* buffer) {
// Remove any + characters because they are redundant and ugly.
for (;;) {
buffer = strchr(buffer, '+');
if (buffer == NULL) {
return;
}
memmove(buffer, buffer + 1, strlen(buffer + 1) + 1);
}
}
char* DoubleToBuffer(double value, char* buffer) {
// DBL_DIG is 15 for IEEE-754 doubles, which are used on almost all
// platforms these days. Just in case some system exists where DBL_DIG
// is significantly larger -- and risks overflowing our buffer -- we have
// this assert.
static_assert(DBL_DIG < 20, "DBL_DIG is too big.");
if (value == std::numeric_limits<double>::infinity()) {
strcpy(buffer, "inf");
return buffer;
} else if (value == -std::numeric_limits<double>::infinity()) {
strcpy(buffer, "-inf");
return buffer;
} else if (IsNaN(value)) {
strcpy(buffer, "nan");
return buffer;
}
int snprintf_result =
snprintf(buffer, kDoubleToBufferSize, "%.*g", DBL_DIG, value);
// The snprintf should never overflow because the buffer is significantly
// larger than the precision we asked for.
DCHECK(snprintf_result > 0 && snprintf_result < kDoubleToBufferSize);
// We need to make parsed_value volatile in order to force the compiler to
// write it out to the stack. Otherwise, it may keep the value in a
// register, and if it does that, it may keep it as a long double instead
// of a double. This long double may have extra bits that make it compare
// unequal to "value" even though it would be exactly equal if it were
// truncated to a double.
volatile double parsed_value = strtod(buffer, NULL);
if (parsed_value != value) {
int snprintf_result =
snprintf(buffer, kDoubleToBufferSize, "%.*g", DBL_DIG+2, value);
// Should never overflow; see above.
DCHECK(snprintf_result > 0 && snprintf_result < kDoubleToBufferSize);
}
DelocalizeRadix(buffer);
RemovePlus(buffer);
return buffer;
}
bool safe_strtof(const char* str, float* value) {
char* endptr;
errno = 0; // errno only gets set on errors
#if defined(_WIN32) || defined (__hpux) // has no strtof()
*value = strtod(str, &endptr);
#else
*value = strtof(str, &endptr);
#endif
return *str != 0 && *endptr == 0 && errno == 0;
}
char* FloatToBuffer(float value, char* buffer) {
// FLT_DIG is 6 for IEEE-754 floats, which are used on almost all
// platforms these days. Just in case some system exists where FLT_DIG
// is significantly larger -- and risks overflowing our buffer -- we have
// this assert.
static_assert(FLT_DIG < 10, "FLT_DIG is too big");
if (value == std::numeric_limits<double>::infinity()) {
strcpy(buffer, "inf");
return buffer;
} else if (value == -std::numeric_limits<double>::infinity()) {
strcpy(buffer, "-inf");
return buffer;
} else if (IsNaN(value)) {
strcpy(buffer, "nan");
return buffer;
}
int snprintf_result =
snprintf(buffer, kFloatToBufferSize, "%.*g", FLT_DIG, value);
// The snprintf should never overflow because the buffer is significantly
// larger than the precision we asked for.
DCHECK(snprintf_result > 0 && snprintf_result < kFloatToBufferSize);
float parsed_value;
if (!safe_strtof(buffer, &parsed_value) || parsed_value != value) {
int snprintf_result =
snprintf(buffer, kFloatToBufferSize, "%.*g", FLT_DIG+2, value);
// Should never overflow; see above.
DCHECK(snprintf_result > 0 && snprintf_result < kFloatToBufferSize);
}
DelocalizeRadix(buffer);
RemovePlus(buffer);
return buffer;
}
} // namespace
CappedArray<char, kFloatToBufferSize> Stringifier::operator*(float f) const {
CappedArray<char, kFloatToBufferSize> result;
result.setSize(strlen(FloatToBuffer(f, result.begin())));
return result;
}
CappedArray<char, kDoubleToBufferSize> Stringifier::operator*(double f) const {
CappedArray<char, kDoubleToBufferSize> result;
result.setSize(strlen(DoubleToBuffer(f, result.begin())));
return result;
}
} // namespace internal
} // namespace kj
......@@ -24,6 +24,7 @@
#ifndef KJ_STRING_H_
#define KJ_STRING_H_
#include <initializer_list>
#include "array.h"
#include <string.h>
......@@ -140,6 +141,177 @@ String heapString(StringPtr value);
String heapString(ArrayPtr<const char> value);
// Allocates a copy of the given value on the heap.
// =======================================================================================
// Magic str() function which transforms parameters to text and concatenates them into one big
// String.
namespace internal {
inline size_t sum(std::initializer_list<size_t> nums) {
size_t result = 0;
for (auto num: nums) {
result += num;
}
return result;
}
inline char* fill(char* ptr) { return ptr; }
template <typename First, typename... Rest>
char* fill(char* __restrict__ target, const First& first, Rest&&... rest) {
auto i = first.begin();
auto end = first.end();
while (i != end) {
*target++ = *i++;
}
return fill(target, kj::fwd<Rest>(rest)...);
}
template <typename... Params>
String concat(Params&&... params) {
// Concatenate a bunch of containers into a single Array. The containers can be anything that
// is iterable and whose elements can be converted to `char`.
String result = heapString(sum({params.size()...}));
fill(result.begin(), kj::fwd<Params>(params)...);
return result;
}
inline String concat(String&& arr) {
return kj::mv(arr);
}
struct Stringifier {
// This is a dummy type with only one instance: STR (below). To make an arbitrary type
// stringifiable, define `operator*(Stringifier, T)` to return an iterable container of `char`.
// The container type must have a `size()` method. Be sure to declare the operator in the same
// namespace as `T` **or** in the global scope.
//
// A more usual way to accomplish what we're doing here would be to require that you define
// a function like `toString(T)` and then rely on argument-dependent lookup. However, this has
// the problem that it pollutes other people's namespaces and even the global namespace. For
// example, some other project may already have functions called `toString` which do something
// different. Declaring `operator*` with `Stringifier` as the left operand cannot conflict with
// anything.
inline ArrayPtr<const char> operator*(ArrayPtr<const char> s) const { return s; }
inline ArrayPtr<const char> operator*(const Array<const char>& s) const { return s; }
inline ArrayPtr<const char> operator*(const Array<char>& s) const { return s; }
template<size_t n>
inline ArrayPtr<const char> operator*(const CappedArray<char, n>& s) const { return s; }
inline ArrayPtr<const char> operator*(const char* s) const { return arrayPtr(s, strlen(s)); }
inline ArrayPtr<const char> operator*(const String& s) const { return s.asArray(); }
inline ArrayPtr<const char> operator*(const StringPtr& s) const { return s.asArray(); }
inline FixedArray<char, 1> operator*(char c) const {
FixedArray<char, 1> result;
result[0] = c;
return result;
}
StringPtr operator*(bool b) const;
CappedArray<char, sizeof(short) * 4> operator*(short i) const;
CappedArray<char, sizeof(unsigned short) * 4> operator*(unsigned short i) const;
CappedArray<char, sizeof(int) * 4> operator*(int i) const;
CappedArray<char, sizeof(unsigned int) * 4> operator*(unsigned int i) const;
CappedArray<char, sizeof(long) * 4> operator*(long i) const;
CappedArray<char, sizeof(unsigned long) * 4> operator*(unsigned long i) const;
CappedArray<char, sizeof(long long) * 4> operator*(long long i) const;
CappedArray<char, sizeof(unsigned long long) * 4> 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*) * 4> operator*(const void* s) const;
template <typename T>
Array<char> operator*(ArrayPtr<T> arr) const;
template <typename T>
Array<char> operator*(const Array<T>& arr) const;
};
static constexpr Stringifier STR = Stringifier();
} // namespace internal
template <typename T>
auto toCharSequence(T&& value) -> decltype(internal::STR * kj::fwd<T>(value)) {
// Returns an iterable of chars that represent a textual representation of the value, suitable
// for debugging.
//
// Most users should use str() instead, but toCharSequence() may occasionally be useful to avoid
// heap allocation overhead that str() implies.
//
// To specialize this function for your type, see KJ_STRINGIFY.
return internal::STR * kj::fwd<T>(value);
}
CappedArray<char, sizeof(unsigned short) * 4> hex(unsigned short i);
CappedArray<char, sizeof(unsigned int) * 4> hex(unsigned int i);
CappedArray<char, sizeof(unsigned long) * 4> hex(unsigned long i);
CappedArray<char, sizeof(unsigned long long) * 4> hex(unsigned long long i);
template <typename... Params>
String str(Params&&... params) {
// Magic function which builds a string from a bunch of arbitrary values. Example:
// str(1, " / ", 2, " = ", 0.5)
// returns:
// "1 / 2 = 0.5"
// To teach `str` how to stringify a type, see `Stringifier`.
return internal::concat(toCharSequence(kj::fwd<Params>(params))...);
}
inline String str(String&& s) { return mv(s); }
// Overload to prevent redundant allocation.
template <typename T>
String strArray(T&& arr, const char* delim) {
size_t delimLen = strlen(delim);
KJ_STACK_ARRAY(decltype(internal::STR * arr[0]), pieces, arr.size(), 8, 32);
size_t size = 0;
for (size_t i = 0; i < arr.size(); i++) {
if (i > 0) size += delimLen;
pieces[i] = internal::STR * arr[i];
size += pieces[i].size();
}
String result = heapString(size);
char* pos = result.begin();
for (size_t i = 0; i < arr.size(); i++) {
if (i > 0) {
memcpy(pos, delim, delimLen);
pos += delimLen;
}
pos = internal::fill(pos, pieces[i]);
}
return result;
}
namespace internal {
template <typename T>
inline Array<char> Stringifier::operator*(ArrayPtr<T> arr) const {
return strArray(arr, ", ");
}
template <typename T>
inline Array<char> Stringifier::operator*(const Array<T>& arr) const {
return strArray(arr, ", ");
}
} // namespace internal
#define KJ_STRINGIFY(...) operator*(::kj::internal::Stringifier, __VA_ARGS__)
// Defines a stringifier for a custom type. Example:
//
// class Foo {...};
// inline StringPtr KJ_STRINGIFY(const Foo& foo) { return foo.name(); }
//
// This allows Foo to be passed to str().
//
// The function should be declared either in the same namespace as the target type or in the global
// namespace. It can return any type which is an iterable container of chars.
// =======================================================================================
// Inline implementation details.
......
// Copyright (c) 2013, Kenton Varda <temporal@gmail.com>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "util.h"
#include "logging.h"
#include <stdio.h>
#include <float.h>
#include <limits>
#include <errno.h>
#include <stdlib.h>
namespace kj {
#define STRINGIFY_INT(type, format) \
CappedArray<char, sizeof(type) * 4> Stringifier::operator*(type i) const { \
CappedArray<char, sizeof(type) * 4> result; \
result.setSize(sprintf(result.begin(), format, i)); \
return result; \
}
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");
#undef STRINGIFY_INT
#define HEXIFY_INT(type, format) \
CappedArray<char, sizeof(type) * 4> hex(type i) { \
CappedArray<char, sizeof(type) * 4> result; \
result.setSize(sprintf(result.begin(), format, i)); \
return result; \
}
HEXIFY_INT(unsigned short, "%x");
HEXIFY_INT(unsigned int, "%x");
HEXIFY_INT(unsigned long, "%lx");
HEXIFY_INT(unsigned long long, "%llx");
#undef HEXIFY_INT
namespace {
// ----------------------------------------------------------------------
// DoubleToBuffer()
// FloatToBuffer()
// Copied from Protocol Buffers, (C) Google, BSD license.
// Kenton wrote this code originally. The following commentary is
// from the original.
//
// Description: converts a double or float to a string which, if
// passed to NoLocaleStrtod(), will produce the exact same original double
// (except in case of NaN; all NaNs are considered the same value).
// We try to keep the string short but it's not guaranteed to be as
// short as possible.
//
// DoubleToBuffer() and FloatToBuffer() write the text to the given
// buffer and return it. The buffer must be at least
// kDoubleToBufferSize bytes for doubles and kFloatToBufferSize
// bytes for floats. kFastToBufferSize is also guaranteed to be large
// enough to hold either.
//
// We want to print the value without losing precision, but we also do
// not want to print more digits than necessary. This turns out to be
// trickier than it sounds. Numbers like 0.2 cannot be represented
// exactly in binary. If we print 0.2 with a very large precision,
// e.g. "%.50g", we get "0.2000000000000000111022302462515654042363167".
// On the other hand, if we set the precision too low, we lose
// significant digits when printing numbers that actually need them.
// It turns out there is no precision value that does the right thing
// for all numbers.
//
// Our strategy is to first try printing with a precision that is never
// over-precise, then parse the result with strtod() to see if it
// matches. If not, we print again with a precision that will always
// give a precise result, but may use more digits than necessary.
//
// An arguably better strategy would be to use the algorithm described
// in "How to Print Floating-Point Numbers Accurately" by Steele &
// White, e.g. as implemented by David M. Gay's dtoa(). It turns out,
// however, that the following implementation is about as fast as
// DMG's code. Furthermore, DMG's code locks mutexes, which means it
// will not scale well on multi-core machines. DMG's code is slightly
// more accurate (in that it will never use more digits than
// necessary), but this is probably irrelevant for most users.
//
// Rob Pike and Ken Thompson also have an implementation of dtoa() in
// third_party/fmt/fltfmt.cc. Their implementation is similar to this
// one in that it makes guesses and then uses strtod() to check them.
// Their implementation is faster because they use their own code to
// generate the digits in the first place rather than use snprintf(),
// thus avoiding format string parsing overhead. However, this makes
// it considerably more complicated than the following implementation,
// and it is embedded in a larger library. If speed turns out to be
// an issue, we could re-implement this in terms of their
// implementation.
// ----------------------------------------------------------------------
#ifdef _WIN32
// MSVC has only _snprintf, not snprintf.
//
// MinGW has both snprintf and _snprintf, but they appear to be different
// functions. The former is buggy. When invoked like so:
// char buffer[32];
// snprintf(buffer, 32, "%.*g\n", FLT_DIG, 1.23e10f);
// it prints "1.23000e+10". This is plainly wrong: %g should never print
// trailing zeros after the decimal point. For some reason this bug only
// occurs with some input values, not all. In any case, _snprintf does the
// right thing, so we use it.
#define snprintf _snprintf
#endif
inline bool IsNaN(double value) {
// NaN is never equal to anything, even itself.
return value != value;
}
// In practice, doubles should never need more than 24 bytes and floats
// should never need more than 14 (including null terminators), but we
// overestimate to be safe.
static const int kDoubleToBufferSize = 32;
static const int kFloatToBufferSize = 24;
static inline bool IsValidFloatChar(char c) {
return ('0' <= c && c <= '9') ||
c == 'e' || c == 'E' ||
c == '+' || c == '-';
}
void DelocalizeRadix(char* buffer) {
// Fast check: if the buffer has a normal decimal point, assume no
// translation is needed.
if (strchr(buffer, '.') != NULL) return;
// Find the first unknown character.
while (IsValidFloatChar(*buffer)) ++buffer;
if (*buffer == '\0') {
// No radix character found.
return;
}
// We are now pointing at the locale-specific radix character. Replace it
// with '.'.
*buffer = '.';
++buffer;
if (!IsValidFloatChar(*buffer) && *buffer != '\0') {
// It appears the radix was a multi-byte character. We need to remove the
// extra bytes.
char* target = buffer;
do { ++buffer; } while (!IsValidFloatChar(*buffer) && *buffer != '\0');
memmove(target, buffer, strlen(buffer) + 1);
}
}
void RemovePlus(char* buffer) {
// Remove any + characters because they are redundant and ugly.
for (;;) {
buffer = strchr(buffer, '+');
if (buffer == NULL) {
return;
}
memmove(buffer, buffer + 1, strlen(buffer + 1) + 1);
}
}
char* DoubleToBuffer(double value, char* buffer) {
// DBL_DIG is 15 for IEEE-754 doubles, which are used on almost all
// platforms these days. Just in case some system exists where DBL_DIG
// is significantly larger -- and risks overflowing our buffer -- we have
// this assert.
static_assert(DBL_DIG < 20, "DBL_DIG is too big.");
if (value == std::numeric_limits<double>::infinity()) {
strcpy(buffer, "inf");
return buffer;
} else if (value == -std::numeric_limits<double>::infinity()) {
strcpy(buffer, "-inf");
return buffer;
} else if (IsNaN(value)) {
strcpy(buffer, "nan");
return buffer;
}
int snprintf_result =
snprintf(buffer, kDoubleToBufferSize, "%.*g", DBL_DIG, value);
// The snprintf should never overflow because the buffer is significantly
// larger than the precision we asked for.
DCHECK(snprintf_result > 0 && snprintf_result < kDoubleToBufferSize);
// We need to make parsed_value volatile in order to force the compiler to
// write it out to the stack. Otherwise, it may keep the value in a
// register, and if it does that, it may keep it as a long double instead
// of a double. This long double may have extra bits that make it compare
// unequal to "value" even though it would be exactly equal if it were
// truncated to a double.
volatile double parsed_value = strtod(buffer, NULL);
if (parsed_value != value) {
int snprintf_result =
snprintf(buffer, kDoubleToBufferSize, "%.*g", DBL_DIG+2, value);
// Should never overflow; see above.
DCHECK(snprintf_result > 0 && snprintf_result < kDoubleToBufferSize);
}
DelocalizeRadix(buffer);
RemovePlus(buffer);
return buffer;
}
bool safe_strtof(const char* str, float* value) {
char* endptr;
errno = 0; // errno only gets set on errors
#if defined(_WIN32) || defined (__hpux) // has no strtof()
*value = strtod(str, &endptr);
#else
*value = strtof(str, &endptr);
#endif
return *str != 0 && *endptr == 0 && errno == 0;
}
char* FloatToBuffer(float value, char* buffer) {
// FLT_DIG is 6 for IEEE-754 floats, which are used on almost all
// platforms these days. Just in case some system exists where FLT_DIG
// is significantly larger -- and risks overflowing our buffer -- we have
// this assert.
static_assert(FLT_DIG < 10, "FLT_DIG is too big");
if (value == std::numeric_limits<double>::infinity()) {
strcpy(buffer, "inf");
return buffer;
} else if (value == -std::numeric_limits<double>::infinity()) {
strcpy(buffer, "-inf");
return buffer;
} else if (IsNaN(value)) {
strcpy(buffer, "nan");
return buffer;
}
int snprintf_result =
snprintf(buffer, kFloatToBufferSize, "%.*g", FLT_DIG, value);
// The snprintf should never overflow because the buffer is significantly
// larger than the precision we asked for.
DCHECK(snprintf_result > 0 && snprintf_result < kFloatToBufferSize);
float parsed_value;
if (!safe_strtof(buffer, &parsed_value) || parsed_value != value) {
int snprintf_result =
snprintf(buffer, kFloatToBufferSize, "%.*g", FLT_DIG+2, value);
// Should never overflow; see above.
DCHECK(snprintf_result > 0 && snprintf_result < kFloatToBufferSize);
}
DelocalizeRadix(buffer);
RemovePlus(buffer);
return buffer;
}
} // namespace
CappedArray<char, kFloatToBufferSize> Stringifier::operator*(float f) const {
CappedArray<char, kFloatToBufferSize> result;
result.setSize(strlen(FloatToBuffer(f, result.begin())));
return result;
}
CappedArray<char, kDoubleToBufferSize> Stringifier::operator*(double f) const {
CappedArray<char, kDoubleToBufferSize> result;
result.setSize(strlen(DoubleToBuffer(f, result.begin())));
return result;
}
} // namespace kj
// Copyright (c) 2013, Kenton Varda <temporal@gmail.com>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef KJ_UTIL_H_
#define KJ_UTIL_H_
#include <initializer_list>
#include <utility>
#include <type_traits>
#include "array.h"
#include "string.h"
#include <string.h>
namespace kj {
// =======================================================================================
// String stuff
inline size_t sum(std::initializer_list<size_t> nums) {
size_t result = 0;
for (auto num: nums) {
result += num;
}
return result;
}
inline char* fill(char* ptr) { return ptr; }
template <typename First, typename... Rest>
char* fill(char* __restrict__ target, const First& first, Rest&&... rest) {
auto i = first.begin();
auto end = first.end();
while (i != end) {
*target++ = *i++;
}
return fill(target, std::forward<Rest>(rest)...);
}
template <typename... Params>
String concat(Params&&... params) {
// Concatenate a bunch of containers into a single Array. The containers can be anything that
// is iterable and whose elements can be converted to `Element`.
String result = heapString(sum({params.size()...}));
fill(result.begin(), std::forward<Params>(params)...);
return result;
}
inline String concat(String&& arr) {
return std::move(arr);
}
struct Stringifier {
// This is a dummy type with only one instance: STR (below). To make an arbitrary type
// stringifiable, define `operator*(Stringifier, T)` to return an iterable container of `char`.
// The container type must have a `size()` method. Be sure to declare the operator in the same
// namespace as `T` **or** in the global scope.
//
// A more usual way to accomplish what we're doing here would be to require that you define
// a function like `toString(T)` and then rely on argument-dependent lookup. However, this has
// the problem that it pollutes other people's namespaces and even the global namespace. For
// example, some other project may already have functions called `toString` which do something
// different. Declaring `operator*` with `Stringifier` as the left operand cannot conflict with
// anything.
inline ArrayPtr<const char> operator*(ArrayPtr<const char> s) const { return s; }
inline ArrayPtr<const char> operator*(const Array<const char>& s) const { return s; }
inline ArrayPtr<const char> operator*(const Array<char>& s) const { return s; }
template<size_t n>
inline ArrayPtr<const char> operator*(const CappedArray<char, n>& s) const { return s; }
inline ArrayPtr<const char> operator*(const char* s) const { return arrayPtr(s, strlen(s)); }
inline ArrayPtr<const char> operator*(const String& s) const { return s.asArray(); }
inline ArrayPtr<const char> operator*(const StringPtr& s) const { return s.asArray(); }
inline FixedArray<char, 1> operator*(char c) const {
FixedArray<char, 1> result;
result[0] = c;
return result;
}
CappedArray<char, sizeof(short) * 4> operator*(short i) const;
CappedArray<char, sizeof(unsigned short) * 4> operator*(unsigned short i) const;
CappedArray<char, sizeof(int) * 4> operator*(int i) const;
CappedArray<char, sizeof(unsigned int) * 4> operator*(unsigned int i) const;
CappedArray<char, sizeof(long) * 4> operator*(long i) const;
CappedArray<char, sizeof(unsigned long) * 4> operator*(unsigned long i) const;
CappedArray<char, sizeof(long long) * 4> operator*(long long i) const;
CappedArray<char, sizeof(unsigned long long) * 4> 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*) * 4> operator*(const void* s) const;
template <typename T>
Array<char> operator*(ArrayPtr<T> arr) const;
template <typename T>
Array<char> operator*(const Array<T>& arr) const;
};
static constexpr Stringifier STR = Stringifier();
CappedArray<char, sizeof(unsigned short) * 4> hex(unsigned short i);
CappedArray<char, sizeof(unsigned int) * 4> hex(unsigned int i);
CappedArray<char, sizeof(unsigned long) * 4> hex(unsigned long i);
CappedArray<char, sizeof(unsigned long long) * 4> hex(unsigned long long i);
template <typename... Params>
String str(Params&&... params) {
// Magic function which builds a string from a bunch of arbitrary values. Example:
// str(1, " / ", 2, " = ", 0.5)
// returns:
// "1 / 2 = 0.5"
// To teach `str` how to stringify a type, see `Stringifier`.
return concat(STR * std::forward<Params>(params)...);
}
template <typename T>
String strArray(T&& arr, const char* delim) {
size_t delimLen = strlen(delim);
KJ_STACK_ARRAY(decltype(STR * arr[0]), pieces, arr.size(), 8, 32);
size_t size = 0;
for (size_t i = 0; i < arr.size(); i++) {
if (i > 0) size += delimLen;
pieces[i] = STR * arr[i];
size += pieces[i].size();
}
String result = heapString(size);
char* pos = result.begin();
for (size_t i = 0; i < arr.size(); i++) {
if (i > 0) {
memcpy(pos, delim, delimLen);
pos += delimLen;
}
pos = fill(pos, pieces[i]);
}
return result;
}
template <typename T>
inline Array<char> Stringifier::operator*(ArrayPtr<T> arr) const {
return strArray(arr, ", ");
}
template <typename T>
inline Array<char> Stringifier::operator*(const Array<T>& arr) const {
return strArray(arr, ", ");
}
template <typename T, typename Func>
auto mapArray(T&& arr, Func&& func) -> Array<decltype(func(arr[0]))> {
// TODO(cleanup): Use ArrayBuilder.
Array<decltype(func(arr[0]))> result = heapArray<decltype(func(arr[0]))>(arr.size());
size_t pos = 0;
for (auto& element: arr) {
result[pos++] = func(element);
}
return result;
}
} // namespace kj
#endif // KJ_UTIL_H_
......@@ -855,6 +855,9 @@ compileDecl scope (StructDecl (Located _ name) maybeTypeId isFixed annotations d
unions = [d | DescUnion d <- members]
(finalDataSize, finalPointerCount) <-
recover (dataSize, pointerCount) $ enforceFixed isFixed (dataSize, pointerCount)
let memberByNumber d@(DescField f) = Just (fieldNumber f, d)
memberByNumber d@(DescUnion u) = Just (unionNumber u, d)
memberByNumber _ = Nothing
return (let
in DescStruct StructDesc
{ structName = name
......@@ -867,6 +870,7 @@ compileDecl scope (StructDecl (Located _ name) maybeTypeId isFixed annotations d
, structUnions = unions
, structAnnotations = compiledAnnotations
, structMemberMap = memberMap
, structMembersByNumber = Map.fromList $ mapMaybe memberByNumber members
, structMembers = members
, structFieldPackingMap = fieldPackingMap
})))
......
......@@ -441,6 +441,8 @@ outerFileContext schemaNodes = fileContext where
unionContext parent desc = mkStrContext context where
titleCase = toTitleCase $ unionName desc
unionIndex = Map.findIndex (unionNumber desc) $ structMembersByNumber $ unionParent desc
context "typeStruct" = MuBool False
context "typeUnion" = MuBool True
context "typeName" = MuVariable titleCase
......@@ -454,6 +456,7 @@ outerFileContext schemaNodes = fileContext where
context "unionTitleCase" = MuVariable titleCase
context "unionTagOffset" = MuVariable $ unionTagOffset desc
context "unionFields" = MuList $ map (fieldContext context) $ unionFields desc
context "unionIndex" = MuVariable unionIndex
context s = parent s
childContext parent name = mkStrContext context where
......
......@@ -444,6 +444,7 @@ data StructDesc = StructDesc
, structUnions :: [UnionDesc]
, structAnnotations :: AnnotationMap
, structMemberMap :: MemberMap
, structMembersByNumber :: Map.Map Integer Desc -- top-level members only
, structMembers :: [Desc]
-- Don't use this directly, use the members of FieldDesc and UnionDesc.
......
......@@ -119,6 +119,11 @@ CAPNPROTO_DECLARE_STRUCT(
CAPNPROTO_DECLARE_ENUM(
::{{#fileNamespaces}}{{namespaceName}}::{{/fileNamespaces}}{{typeFullName}}::{{enumName}}, {{enumId}});
{{/structNestedEnums}}
{{#structUnions}}
CAPNPROTO_DECLARE_UNION(
::{{#fileNamespaces}}{{namespaceName}}::{{/fileNamespaces}}{{structFullName}}::{{unionTitleCase}},
::{{#fileNamespaces}}{{namespaceName}}::{{/fileNamespaces}}{{structFullName}}, {{unionIndex}});
{{/structUnions}}
{{/typeStruct}}
{{/typeStructOrUnion}}
{{/fileTypes}}
......@@ -145,9 +150,6 @@ public:
inline explicit Reader(::capnproto::internal::StructReader base): _reader(base) {}
{{#typeStruct}}
inline ::kj::String debugString() {
return ::capnproto::internal::debugString<{{typeName}}>(_reader);
}
inline size_t totalSizeInWords() {
return _reader.totalSize() / ::capnproto::WORDS;
}
......@@ -186,7 +188,18 @@ private:
template <typename T, ::capnproto::Kind k>
friend struct ::capnproto::internal::PointerHelpers;
friend class ::capnproto::MessageBuilder;
friend ::kj::String KJ_STRINGIFY({{typeFullName}}::Reader reader);
};
inline ::kj::String KJ_STRINGIFY({{typeFullName}}::Reader reader) {
{{#typeStruct}}
return ::capnproto::internal::structString<{{typeFullName}}>(reader._reader);
{{/typeStruct}}
{{#typeUnion}}
return ::capnproto::internal::unionString<{{typeFullName}}>(reader._reader);
{{/typeUnion}}
}
{{! ------------------------------------------------------------------------------------------- }}
class {{typeFullName}}::Builder {
......@@ -199,7 +212,6 @@ public:
inline Reader asReader() { return *this; }
{{#typeStruct}}
inline ::kj::String debugString() { return asReader().debugString(); }
inline size_t totalSizeInWords() { return asReader().totalSizeInWords(); }
{{#structUnions}}
......@@ -250,7 +262,17 @@ private:
::capnproto::internal::StructBuilder _builder;
template <typename T, ::capnproto::Kind k>
friend struct ::capnproto::ToDynamic_;
friend ::kj::String KJ_STRINGIFY({{typeFullName}}::Builder builder);
};
inline ::kj::String KJ_STRINGIFY({{typeFullName}}::Builder builder) {
{{#typeStruct}}
return ::capnproto::internal::structString<{{typeFullName}}>(builder._builder.asReader());
{{/typeStruct}}
{{#typeUnion}}
return ::capnproto::internal::unionString<{{typeFullName}}>(builder._builder.asReader());
{{/typeUnion}}
}
{{/typeStructOrUnion}}
{{/fileTypes}}
{{! =========================================================================================== }}
......
......@@ -85,6 +85,10 @@ CAPNPROTO_DEFINE_STRUCT(
CAPNPROTO_DEFINE_ENUM(
::{{#fileNamespaces}}{{namespaceName}}::{{/fileNamespaces}}{{typeFullName}}::{{enumName}});
{{/structNestedEnums}}
{{#structUnions}}
CAPNPROTO_DEFINE_UNION(
::{{#fileNamespaces}}{{namespaceName}}::{{/fileNamespaces}}{{structFullName}}::{{unionTitleCase}});
{{/structUnions}}
{{/typeStruct}}
{{/typeStructOrUnion}}
{{/fileTypes}}
......
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