Commit ecdd2ae4 authored by Kenton Varda's avatar Kenton Varda

Add debugString() method to generated struct types.

parent 5872cc4f
......@@ -72,6 +72,7 @@ includecapnp_HEADERS = \
src/capnproto/message.h \
src/capnproto/schema.h \
src/capnproto/dynamic.h \
src/capnproto/stringify.h \
src/capnproto/io.h \
src/capnproto/serialize.h \
src/capnproto/serialize-packed.h \
......@@ -100,6 +101,7 @@ libcapnproto_a_SOURCES= \
src/capnproto/message.c++ \
src/capnproto/schema.c++ \
src/capnproto/dynamic.c++ \
src/capnproto/stringify.c++ \
src/capnproto/io.c++ \
src/capnproto/serialize.c++ \
src/capnproto/serialize-packed.c++
......@@ -145,6 +147,7 @@ capnproto_test_SOURCES = \
src/capnproto/message-test.c++ \
src/capnproto/schema-test.c++ \
src/capnproto/dynamic-test.c++ \
src/capnproto/stringify-test.c++ \
src/capnproto/encoding-test.c++ \
src/capnproto/serialize-test.c++ \
src/capnproto/serialize-packed-test.c++ \
......
......@@ -92,6 +92,9 @@ public:
inline bool operator< (const Reader& other) const { return !(other <= *this); }
inline bool operator> (const Reader& other) const { return !(*this <= other); }
inline const char* begin() const { return bytes; }
inline const char* end() const { return bytes + size_; }
private:
const char* bytes;
uint size_;
......@@ -181,6 +184,9 @@ public:
memcpy(bytes, other, size_);
}
inline char* begin() const { return bytes; }
inline char* end() const { return bytes + size_; }
private:
char* bytes;
uint size_;
......
......@@ -97,8 +97,8 @@ void dynamicInitTestMessage(DynamicStruct::Builder builder) {
subBuilder.set("boolList", {false, true, false, true, true});
subBuilder.set("int8List", {12, -34, -0x80, 0x7f});
subBuilder.set("int16List", {1234, -5678, -0x8000, 0x7fff});
subBuilder.set("int32List", {12345678, -90123456, -0x8000000, 0x7ffffff});
// gcc warns on -0x800...ll and the only work-around I could find was to do -0x7ff...ll-1.
// gcc warns on -0x800... and the only work-around I could find was to do -0x7ff...-1.
subBuilder.set("int32List", {12345678, -90123456, -0x7fffffff - 1, 0x7fffffff});
subBuilder.set("int64List", {123456789012345ll, -678901234567890ll, -0x7fffffffffffffffll-1, 0x7fffffffffffffffll});
subBuilder.set("uInt8List", {12u, 34u, 0u, 0xffu});
subBuilder.set("uInt16List", {1234u, 5678u, 0u, 0xffffu});
......@@ -218,8 +218,8 @@ void dynamicCheckTestMessage(Reader reader) {
checkList<bool>(subReader.get("boolList"), {false, true, false, true, true});
checkList<int8_t>(subReader.get("int8List"), {12, -34, -0x80, 0x7f});
checkList<int16_t>(subReader.get("int16List"), {1234, -5678, -0x8000, 0x7fff});
checkList<int32_t>(subReader.get("int32List"), {12345678, -90123456, -0x8000000, 0x7ffffff});
// gcc warns on -0x800...ll and the only work-around I could find was to do -0x7ff...ll-1.
// gcc warns on -0x800... and the only work-around I could find was to do -0x7ff...-1.
checkList<int32_t>(subReader.get("int32List"), {12345678, -90123456, -0x7fffffff-1, 0x7fffffff});
checkList<int64_t>(subReader.get("int64List"), {123456789012345ll, -678901234567890ll, -0x7fffffffffffffffll-1, 0x7fffffffffffffffll});
checkList<uint8_t>(subReader.get("uInt8List"), {12u, 34u, 0u, 0xffu});
checkList<uint16_t>(subReader.get("uInt16List"), {1234u, 5678u, 0u, 0xffffu});
......@@ -741,6 +741,21 @@ TEST(DynamicApi, LateUnion) {
EXPECT_EQ("hello", root.as<test::TestLateUnion>().getTheUnion().getQux());
}
TEST(DynamicApi, Has) {
MallocMessageBuilder builder;
auto root = builder.initRoot<DynamicStruct>(Schema::from<test::TestDefaults>());
EXPECT_FALSE(root.has("int32Field"));
root.set("int32Field", 123);
EXPECT_TRUE(root.has("int32Field"));
root.set("int32Field", -12345678);
EXPECT_FALSE(root.has("int32Field"));
EXPECT_FALSE(root.has("structField"));
root.init("structField");
EXPECT_TRUE(root.has("structField"));
}
} // namespace
} // namespace internal
} // namespace capnproto
......@@ -298,6 +298,140 @@ DynamicValue::Builder DynamicStruct::Builder::get(StructSchema::Member member) {
PRECOND(member.getContainingStruct() == schema, "`member` is not a member of this struct.");
return getImpl(builder, member);
}
bool DynamicStruct::Reader::has(StructSchema::Member member) {
PRECOND(member.getContainingStruct() == schema, "`member` is not a member of this struct.");
auto body = member.getProto().getBody();
switch (body.which()) {
case schema::StructNode::Member::Body::UNION_MEMBER: {
auto u = body.getUnionMember();
if (reader.getDataField<uint16_t>(u.getDiscriminantOffset() * ELEMENTS) != 0) {
// Union has non-default member set.
return true;
}
auto members = member.asUnion().getMembers();
if (members.size() == 0) {
// Union has no defined members. This should probably be disallowed?
return false;
}
// The union has the default member set, so now the question is whether that member is set
// to its default value. So, continue on with the function using that member.
member = members[0];
break;
}
case schema::StructNode::Member::Body::FIELD_MEMBER:
// Continue to below.
break;
}
auto field = member.getProto().getBody().getFieldMember();
auto type = field.getType().getBody();
switch (type.which()) {
case schema::Type::Body::VOID_TYPE:
return false;
#define HANDLE_TYPE(discrim, type) \
case schema::Type::Body::discrim##_TYPE: \
return reader.getDataField<type>(field.getOffset() * ELEMENTS) != 0;
HANDLE_TYPE(BOOL, bool)
HANDLE_TYPE(INT8, uint8_t)
HANDLE_TYPE(INT16, uint16_t)
HANDLE_TYPE(INT32, uint32_t)
HANDLE_TYPE(INT64, uint64_t)
HANDLE_TYPE(UINT8, uint8_t)
HANDLE_TYPE(UINT16, uint16_t)
HANDLE_TYPE(UINT32, uint32_t)
HANDLE_TYPE(UINT64, uint64_t)
HANDLE_TYPE(FLOAT32, uint32_t)
HANDLE_TYPE(FLOAT64, uint64_t)
HANDLE_TYPE(ENUM, uint16_t)
#undef HANDLE_TYPE
case schema::Type::Body::TEXT_TYPE:
case schema::Type::Body::DATA_TYPE:
case schema::Type::Body::LIST_TYPE:
case schema::Type::Body::STRUCT_TYPE:
case schema::Type::Body::OBJECT_TYPE:
case schema::Type::Body::INTERFACE_TYPE:
return !reader.isPointerFieldNull(field.getOffset() * POINTERS);
}
FAIL_CHECK("switch() missing case.", type.which());
return false;
}
bool DynamicStruct::Builder::has(StructSchema::Member member) {
PRECOND(member.getContainingStruct() == schema, "`member` is not a member of this struct.");
auto body = member.getProto().getBody();
switch (body.which()) {
case schema::StructNode::Member::Body::UNION_MEMBER: {
auto u = body.getUnionMember();
if (builder.getDataField<uint16_t>(u.getDiscriminantOffset() * ELEMENTS) != 0) {
// Union has non-default member set.
return true;
}
auto members = member.asUnion().getMembers();
if (members.size() == 0) {
// Union has no defined members. This should probably be disallowed?
return false;
}
// The union has the default member set, so now the question is whether that member is set
// to its default value. So, continue on with the function using that member.
member = members[0];
break;
}
case schema::StructNode::Member::Body::FIELD_MEMBER:
// Continue to below.
break;
}
auto field = member.getProto().getBody().getFieldMember();
auto type = field.getType().getBody();
switch (type.which()) {
case schema::Type::Body::VOID_TYPE:
return false;
#define HANDLE_TYPE(discrim, type) \
case schema::Type::Body::discrim##_TYPE: \
return builder.getDataField<type>(field.getOffset() * ELEMENTS) != 0;
HANDLE_TYPE(BOOL, bool)
HANDLE_TYPE(INT8, uint8_t)
HANDLE_TYPE(INT16, uint16_t)
HANDLE_TYPE(INT32, uint32_t)
HANDLE_TYPE(INT64, uint64_t)
HANDLE_TYPE(UINT8, uint8_t)
HANDLE_TYPE(UINT16, uint16_t)
HANDLE_TYPE(UINT32, uint32_t)
HANDLE_TYPE(UINT64, uint64_t)
HANDLE_TYPE(FLOAT32, uint32_t)
HANDLE_TYPE(FLOAT64, uint64_t)
HANDLE_TYPE(ENUM, uint16_t)
#undef HANDLE_TYPE
case schema::Type::Body::TEXT_TYPE:
case schema::Type::Body::DATA_TYPE:
case schema::Type::Body::LIST_TYPE:
case schema::Type::Body::STRUCT_TYPE:
case schema::Type::Body::OBJECT_TYPE:
case schema::Type::Body::INTERFACE_TYPE:
return !builder.isPointerFieldNull(field.getOffset() * POINTERS);
}
FAIL_CHECK("switch() missing case.", type.which());
return false;
}
void DynamicStruct::Builder::set(StructSchema::Member member, DynamicValue::Reader value) {
PRECOND(member.getContainingStruct() == schema, "`member` is not a member of this struct.");
return setImpl(builder, member, value);
......@@ -468,6 +602,12 @@ DynamicValue::Reader DynamicStruct::Reader::get(Text::Reader name) {
DynamicValue::Builder DynamicStruct::Builder::get(Text::Reader name) {
return getImpl(builder, schema.getMemberByName(name));
}
bool DynamicStruct::Reader::has(Text::Reader name) {
return has(schema.getMemberByName(name));
}
bool DynamicStruct::Builder::has(Text::Reader name) {
return has(schema.getMemberByName(name));
}
void DynamicStruct::Builder::set(Text::Reader name, DynamicValue::Reader value) {
setImpl(builder, schema.getMemberByName(name), value);
}
......
......@@ -257,8 +257,14 @@ public:
DynamicValue::Reader get(StructSchema::Member member);
// Read the given member value.
bool has(StructSchema::Member member);
// Tests whether the given member is set to its default value. For pointer values, this does
// not actually traverse the value comparing it with the default, but simply returns true if the
// pointer is non-null.
DynamicValue::Reader get(Text::Reader name);
// Shortcut to read a member by name. Throws an exception if no such member exists.
bool has(Text::Reader name);
// Shortcuts to access members by name. These throw exceptions if no such member exists.
private:
StructSchema schema;
......@@ -281,6 +287,7 @@ private:
friend class MessageBuilder;
template <typename T, ::capnproto::Kind k>
friend struct ::capnproto::ToDynamic_;
friend String internal::debugString(StructReader reader, const RawSchema& schema);
};
class DynamicStruct::Builder {
......@@ -299,6 +306,11 @@ public:
DynamicValue::Builder get(StructSchema::Member member);
// Read the given member value.
bool has(StructSchema::Member member);
// Tests whether the given member is set to its default value. For pointer values, this does
// not actually traverse the value comparing it with the default, but simply returns true if the
// pointer is non-null.
void set(StructSchema::Member member, DynamicValue::Reader value);
// Set the given member value.
......@@ -319,6 +331,7 @@ public:
// Init an object field. You must specify the type.
DynamicValue::Builder get(Text::Reader name);
bool has(Text::Reader name);
void set(Text::Reader name, DynamicValue::Reader value);
void set(Text::Reader name, std::initializer_list<DynamicValue::Reader> value);
DynamicValue::Builder init(Text::Reader name);
......@@ -331,7 +344,7 @@ public:
DynamicList::Builder initObject(Text::Reader name, ListSchema type, uint size);
Text::Builder initObjectAsText(Text::Reader name, uint size);
Data::Builder initObjectAsData(Text::Reader name, uint size);
// Shortcuts to access members by name. These throw exceptions if no such field exists.
// Shortcuts to access members by name. These throw exceptions if no such member exists.
Reader asReader();
......
......@@ -163,6 +163,15 @@ inline const RawSchema& rawSchema() {
template <typename T>
struct TypeIdFor;
String debugString(StructReader reader, const RawSchema& schema);
// Declared here so that we can declare inline debugString() 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 String debugString(StructReader reader) {
return debugString(reader, rawSchema<T>());
}
} // namespace internal
template <typename T>
......
......@@ -1836,6 +1836,10 @@ void StructBuilder::setObjectField(WirePointerCount ptrIndex, ObjectReader value
return WireHelpers::setObjectPointer(segment, pointers + ptrIndex, value);
}
bool StructBuilder::isPointerFieldNull(WirePointerCount ptrIndex) const {
return (pointers + ptrIndex)->isNull();
}
StructReader StructBuilder::asReader() const {
return StructReader(segment, data, pointers,
dataSize, pointerCount, bit0Offset, std::numeric_limits<int>::max());
......@@ -1898,6 +1902,10 @@ const word* StructReader::getTrustedPointer(WirePointerCount ptrIndex) const {
return reinterpret_cast<const word*>(pointers + ptrIndex);
}
bool StructReader::isPointerFieldNull(WirePointerCount ptrIndex) const {
return (pointers + ptrIndex)->isNull();
}
// =======================================================================================
// ListBuilder
......
......@@ -382,6 +382,8 @@ public:
void setObjectField(WirePointerCount ptrIndex, ObjectReader value) const;
// Sets a pointer field to a deep copy of the given value.
bool isPointerFieldNull(WirePointerCount ptrIndex) const;
StructReader asReader() const;
// Gets a StructReader pointing at the same memory.
......@@ -459,6 +461,8 @@ public:
// word* can actually be passed to readTrusted() to read the designated sub-object later. If
// this isn't a trusted message, throws an exception.
bool isPointerFieldNull(WirePointerCount ptrIndex) const;
private:
SegmentReader* segment; // Memory segment in which the struct resides.
......
......@@ -112,6 +112,7 @@ private:
return StructSchema(&internal::rawSchema<T>());
}
friend class Schema;
friend String internal::debugString(StructReader reader, const RawSchema& schema);
};
class StructSchema::Member {
......
......@@ -72,8 +72,8 @@ void genericInitTestMessage(Builder builder) {
subBuilder.setBoolList({false, true, false, true, true});
subBuilder.setInt8List({12, -34, -0x80, 0x7f});
subBuilder.setInt16List({1234, -5678, -0x8000, 0x7fff});
subBuilder.setInt32List({12345678, -90123456, -0x8000000, 0x7ffffff});
// gcc warns on -0x800...ll and the only work-around I could find was to do -0x7ff...ll-1.
// gcc warns on -0x800... and the only work-around I could find was to do -0x7ff...-1.
subBuilder.setInt32List({12345678, -90123456, -0x7fffffff - 1, 0x7fffffff});
subBuilder.setInt64List({123456789012345ll, -678901234567890ll, -0x7fffffffffffffffll-1, 0x7fffffffffffffffll});
subBuilder.setUInt8List({12u, 34u, 0u, 0xffu});
subBuilder.setUInt16List({1234u, 5678u, 0u, 0xffffu});
......@@ -192,8 +192,8 @@ void genericCheckTestMessage(Reader reader) {
checkList(subReader.getBoolList(), {false, true, false, true, true});
checkList(subReader.getInt8List(), {12, -34, -0x80, 0x7f});
checkList(subReader.getInt16List(), {1234, -5678, -0x8000, 0x7fff});
checkList(subReader.getInt32List(), {12345678, -90123456, -0x8000000, 0x7ffffff});
// gcc warns on -0x800...ll and the only work-around I could find was to do -0x7ff...ll-1.
// gcc warns on -0x800... and the only work-around I could find was to do -0x7ff...-1.
checkList(subReader.getInt32List(), {12345678, -90123456, -0x7fffffff - 1, 0x7fffffff});
checkList(subReader.getInt64List(), {123456789012345ll, -678901234567890ll, -0x7fffffffffffffffll-1, 0x7fffffffffffffffll});
checkList(subReader.getUInt8List(), {12u, 34u, 0u, 0xffu});
checkList(subReader.getUInt16List(), {1234u, 5678u, 0u, 0xffffu});
......
......@@ -118,7 +118,7 @@ struct TestDefaults {
boolList = [false, true, false, true, true],
int8List = [12, -34, -0x80, 0x7f],
int16List = [1234, -5678, -0x8000, 0x7fff],
int32List = [12345678, -90123456, -0x8000000, 0x7ffffff],
int32List = [12345678, -90123456, -0x80000000, 0x7fffffff],
int64List = [123456789012345, -678901234567890, -0x8000000000000000, 0x7fffffffffffffff],
uInt8List = [12, 34, 0, 0xff],
uInt16List = [1234, 5678, 0, 0xffff],
......
......@@ -25,4 +25,13 @@
namespace capnproto {
String::String(const char* value): content(newArray<char>(strlen(value) + 1)) {
strcpy(content.begin(), value);
}
String::String(const char* value, size_t length): content(newArray<char>(length + 1)) {
memcpy(content.begin(), value, length);
content[length] = '\0';
}
} // namespace capnproto
......@@ -352,9 +352,13 @@ public:
CAPNPROTO_INLINE_DPRECOND(start <= end && end <= size_, "Out-of-bounds Array::slice().");
return ArrayPtr<T>(ptr + start, end - start);
}
inline ArrayPtr<const T> slice(size_t start, size_t end) const {
CAPNPROTO_INLINE_DPRECOND(start <= end && end <= size_, "Out-of-bounds Array::slice().");
return ArrayPtr<const T>(ptr + start, end - start);
}
inline bool operator==(std::nullptr_t) { return size_ == 0; }
inline bool operator!=(std::nullptr_t) { return size_ != 0; }
inline bool operator==(std::nullptr_t) const { return size_ == 0; }
inline bool operator!=(std::nullptr_t) const { return size_ != 0; }
inline Array& operator=(std::nullptr_t) {
delete[] ptr;
......@@ -452,6 +456,37 @@ private:
Slot* endPtr;
};
// =======================================================================================
// String -- Just a NUL-terminated Array<char>.
class String {
public:
String() = default;
String(const char* value);
String(const char* value, size_t length);
inline ArrayPtr<char> asArray();
inline ArrayPtr<const char> asArray() const;
inline const char* cStr() const { return content == nullptr ? "" : content.begin(); }
inline size_t size() const { return content == nullptr ? 0 : content.size() - 1; }
inline char* begin() { return content == nullptr ? nullptr : content.begin(); }
inline char* end() { return content == nullptr ? nullptr : content.end() - 1; }
inline const char* begin() const { return content == nullptr ? nullptr : content.begin(); }
inline const char* end() const { return content == nullptr ? nullptr : content.end() - 1; }
private:
Array<char> content;
};
inline ArrayPtr<char> String::asArray() {
return content == nullptr ? ArrayPtr<char>(nullptr) : content.slice(0, content.size() - 1);
}
inline ArrayPtr<const char> String::asArray() const {
return content == nullptr ? ArrayPtr<char>(nullptr) : content.slice(0, content.size() - 1);
}
// =======================================================================================
// IDs
......
......@@ -180,6 +180,7 @@ struct Stringifier {
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 FixedArray<char, 1> operator*(char c) const {
FixedArray<char, 1> result;
......
......@@ -143,6 +143,10 @@ public:
Reader() = default;
inline explicit Reader(::capnproto::internal::StructReader base): _reader(base) {}
{{#typeStruct}}
::capnproto::String debugString() {
return ::capnproto::internal::debugString<{{typeName}}>(_reader);
}
{{#structUnions}}
// {{unionDecl}}
......@@ -189,6 +193,8 @@ public:
inline operator Reader() { return Reader(_builder.asReader()); }
inline Reader asReader() { return *this; }
{{#typeStruct}}
::capnproto::String debugString() { return asReader().debugString(); }
{{#structUnions}}
// {{unionDecl}}
......
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