Commit e7b9e4da authored by Kenton Varda's avatar Kenton Varda

Schema convenience interface.

parent 80722b03
......@@ -78,5 +78,42 @@ TEST(Blob, Data) {
EXPECT_EQ(Data::Reader("az"), builder.slice(1, 3));
}
TEST(Blob, Compare) {
EXPECT_TRUE (Data::Reader("foo") == Data::Reader("foo"));
EXPECT_FALSE(Data::Reader("foo") != Data::Reader("foo"));
EXPECT_TRUE (Data::Reader("foo") <= Data::Reader("foo"));
EXPECT_TRUE (Data::Reader("foo") >= Data::Reader("foo"));
EXPECT_FALSE(Data::Reader("foo") < Data::Reader("foo"));
EXPECT_FALSE(Data::Reader("foo") > Data::Reader("foo"));
EXPECT_FALSE(Data::Reader("foo") == Data::Reader("bar"));
EXPECT_TRUE (Data::Reader("foo") != Data::Reader("bar"));
EXPECT_FALSE(Data::Reader("foo") <= Data::Reader("bar"));
EXPECT_TRUE (Data::Reader("foo") >= Data::Reader("bar"));
EXPECT_FALSE(Data::Reader("foo") < Data::Reader("bar"));
EXPECT_TRUE (Data::Reader("foo") > Data::Reader("bar"));
EXPECT_FALSE(Data::Reader("bar") == Data::Reader("foo"));
EXPECT_TRUE (Data::Reader("bar") != Data::Reader("foo"));
EXPECT_TRUE (Data::Reader("bar") <= Data::Reader("foo"));
EXPECT_FALSE(Data::Reader("bar") >= Data::Reader("foo"));
EXPECT_TRUE (Data::Reader("bar") < Data::Reader("foo"));
EXPECT_FALSE(Data::Reader("bar") > Data::Reader("foo"));
EXPECT_FALSE(Data::Reader("foobar") == Data::Reader("foo"));
EXPECT_TRUE (Data::Reader("foobar") != Data::Reader("foo"));
EXPECT_FALSE(Data::Reader("foobar") <= Data::Reader("foo"));
EXPECT_TRUE (Data::Reader("foobar") >= Data::Reader("foo"));
EXPECT_FALSE(Data::Reader("foobar") < Data::Reader("foo"));
EXPECT_TRUE (Data::Reader("foobar") > Data::Reader("foo"));
EXPECT_FALSE(Data::Reader("foo") == Data::Reader("foobar"));
EXPECT_TRUE (Data::Reader("foo") != Data::Reader("foobar"));
EXPECT_TRUE (Data::Reader("foo") <= Data::Reader("foobar"));
EXPECT_FALSE(Data::Reader("foo") >= Data::Reader("foobar"));
EXPECT_TRUE (Data::Reader("foo") < Data::Reader("foobar"));
EXPECT_FALSE(Data::Reader("foo") > Data::Reader("foobar"));
}
} // namespace
} // namespace capnproto
......@@ -114,12 +114,8 @@ public:
typedef Text Reads;
inline Reader(): Data::Reader("", 0) {}
inline Reader(const char* text): Data::Reader(text, strlen(text)) {
CAPNPROTO_INLINE_DPRECOND(text[size()] == '\0', "Text must be NUL-terminated.");
}
inline Reader(char* text): Data::Reader(text, strlen(text)) {
CAPNPROTO_INLINE_DPRECOND(text[size()] == '\0', "Text must be NUL-terminated.");
}
inline Reader(const char* text): Data::Reader(text, strlen(text)) {}
inline Reader(char* text): Data::Reader(text, strlen(text)) {}
inline Reader(const char* text, uint size): Data::Reader(text, size) {
CAPNPROTO_INLINE_DPRECOND(text[size] == '\0', "Text must be NUL-terminated.");
}
......
......@@ -121,8 +121,29 @@ struct PointerHelpers<TrustedMessage> {
#endif
struct RawSchema {
// The generated code defines a constant RawSchema for every compiled declaration.
//
// This is an internal structure which could change in the future.
const word* encodedNode;
// Encoded SchemaNode, readable via readMessageTrusted<schema::Node>(encodedNode).
const RawSchema* const* dependencies;
// Pointers to other types on which this one depends, sorted by ID.
// TODO(someday): Make this a hashtable.
struct MemberInfo {
uint16_t unionIndex; // 0 = not in a union, >0 = parent union's index + 1
uint16_t index; // index of the member
};
const MemberInfo* membersByName;
// Indexes of members sorted by name. Used to implement name lookup.
// TODO(someday): Make this a hashtable.
uint32_t dependencyCount;
uint32_t memberCount;
// Sizes of above tables.
};
template <typename T>
......
......@@ -179,14 +179,14 @@ static typename RootType::Reader readMessageTrusted(const word* data);
// To create a trusted message, build a message using a MallocAllocator whose preferred segment
// size is larger than the message size. This guarantees that the message will be allocated as a
// single segment, meaning getSegmentsForOutput() returns a single word array. That word array
// is your message; you may pass a pointer to its first word into readTrusted() to read the
// is your message; you may pass a pointer to its first word into readMessageTrusted() to read the
// message.
//
// This can be particularly handy for embedding messages in generated code: you can
// embed the raw bytes (using AlignedData) then make a Reader for it using this. This is the way
// default values are embedded in code generated by the Cap'n Proto compiler. E.g., if you have
// a message MyMessage, you can read its default value like so:
// MyMessage::Reader reader = Message<MyMessage>::ReadTrusted(MyMessage::DEFAULT.words);
// MyMessage::Reader reader = Message<MyMessage>::readMessageTrusted(MyMessage::DEFAULT.words);
template <typename Type>
static typename Type::Reader defaultValue();
......
This diff is collapsed.
// 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.
#define CAPNPROTO_PRIVATE
#include "schema.h"
#include "message.h"
#include "logging.h"
namespace capnproto {
schema::Node::Reader Schema::getProto() const {
return readMessageTrusted<schema::Node>(raw->encodedNode);
}
Schema Schema::getDependency(uint64_t id) const {
uint lower = 0;
uint upper = raw->dependencyCount;
while (lower < upper) {
uint mid = (lower + upper) / 2;
Schema candidate(raw->dependencies[mid]);
uint64_t candidateId = candidate.getProto().getId();
if (candidateId == id) {
return candidate;
} else if (candidateId < id) {
lower = mid + 1;
} else {
upper = mid;
}
}
FAIL_PRECOND("Requested ID not found in dependency table.", id);
return Schema();
}
StructSchema Schema::asStruct() const {
PRECOND(getProto().getBody().which() == schema::Node::Body::STRUCT_NODE,
"Tried to use non-struct schema as a struct.",
getProto().getDisplayName());
return StructSchema(raw);
}
EnumSchema Schema::asEnum() const {
PRECOND(getProto().getBody().which() == schema::Node::Body::ENUM_NODE,
"Tried to use non-enum schema as an enum.",
getProto().getDisplayName());
return EnumSchema(raw);
}
InterfaceSchema Schema::asInterface() const {
PRECOND(getProto().getBody().which() == schema::Node::Body::INTERFACE_NODE,
"Tried to use non-interface schema as an interface.",
getProto().getDisplayName());
return InterfaceSchema(raw);
}
// =======================================================================================
namespace {
template <typename List>
auto findSchemaMemberByName(const internal::RawSchema* raw, Text::Reader name,
uint unionIndex, List&& list)
-> Maybe<RemoveReference<decltype(list[0])>> {
uint lower = 0;
uint upper = raw->memberCount;
while (lower < upper) {
uint mid = (lower + upper) / 2;
const internal::RawSchema::MemberInfo& member = raw->membersByName[mid];
if (member.unionIndex == unionIndex) {
auto candidate = list[member.index];
Text::Reader candidateName = candidate.getProto().getName();
if (candidateName == name) {
return candidate;
} else if (candidateName < name) {
lower = mid + 1;
} else {
upper = mid;
}
} else if (member.unionIndex < unionIndex) {
lower = mid + 1;
} else {
upper = mid;
}
}
return nullptr;
}
} // namespace
StructSchema::MemberList StructSchema::members() const {
return MemberList(*this, 0, getProto().getBody().getStructNode().getMembers());
}
Maybe<StructSchema::Member> StructSchema::findMemberByName(Text::Reader name) const {
return findSchemaMemberByName(raw, name, 0, members());
}
Maybe<StructSchema::Union> StructSchema::Member::getContainingUnion() const {
if (unionIndex == 0) return nullptr;
return parent.members()[unionIndex - 1].asUnion();
}
StructSchema::Union StructSchema::Member::asUnion() const {
PRECOND(proto.getBody().which() == schema::StructNode::Member::Body::UNION_MEMBER,
"Tried to use non-union struct member as a union.",
parent.getProto().getDisplayName(), proto.getName());
return Union(*this);
}
StructSchema::MemberList StructSchema::Union::members() const {
return MemberList(parent, index + 1, proto.getBody().getUnionMember().getMembers());
}
Maybe<StructSchema::Member> StructSchema::Union::findMemberByName(Text::Reader name) const {
return findSchemaMemberByName(parent.raw, name, index + 1, members());
}
// -------------------------------------------------------------------
EnumSchema::EnumerantList EnumSchema::enumerants() const {
return EnumerantList(*this, getProto().getBody().getEnumNode().getEnumerants());
}
Maybe<EnumSchema::Enumerant> EnumSchema::findEnumerantByName(Text::Reader name) const {
return findSchemaMemberByName(raw, name, 0, enumerants());
}
// -------------------------------------------------------------------
InterfaceSchema::MethodList InterfaceSchema::methods() const {
return MethodList(*this, getProto().getBody().getInterfaceNode().getMethods());
}
Maybe<InterfaceSchema::Method> InterfaceSchema::findMethodByName(Text::Reader name) const {
return findSchemaMemberByName(raw, name, 0, methods());
}
// =======================================================================================
ListSchema ListSchema::of(schema::Type::Body::Which primitiveType) {
switch (primitiveType) {
case schema::Type::Body::VOID_TYPE:
case schema::Type::Body::BOOL_TYPE:
case schema::Type::Body::INT8_TYPE:
case schema::Type::Body::INT16_TYPE:
case schema::Type::Body::INT32_TYPE:
case schema::Type::Body::INT64_TYPE:
case schema::Type::Body::UINT8_TYPE:
case schema::Type::Body::UINT16_TYPE:
case schema::Type::Body::UINT32_TYPE:
case schema::Type::Body::UINT64_TYPE:
case schema::Type::Body::FLOAT32_TYPE:
case schema::Type::Body::FLOAT64_TYPE:
case schema::Type::Body::TEXT_TYPE:
case schema::Type::Body::DATA_TYPE:
break;
case schema::Type::Body::STRUCT_TYPE:
case schema::Type::Body::ENUM_TYPE:
case schema::Type::Body::INTERFACE_TYPE:
case schema::Type::Body::LIST_TYPE:
FAIL_PRECOND("Must use one of the other ListSchema::of() overloads for complex types.");
break;
case schema::Type::Body::OBJECT_TYPE:
FAIL_PRECOND("List(Object) not supported.");
break;
}
return ListSchema(primitiveType);
}
ListSchema ListSchema::of(schema::Type::Reader elementType, Schema context) {
auto body = elementType.getBody();
switch (body.which()) {
case schema::Type::Body::VOID_TYPE:
case schema::Type::Body::BOOL_TYPE:
case schema::Type::Body::INT8_TYPE:
case schema::Type::Body::INT16_TYPE:
case schema::Type::Body::INT32_TYPE:
case schema::Type::Body::INT64_TYPE:
case schema::Type::Body::UINT8_TYPE:
case schema::Type::Body::UINT16_TYPE:
case schema::Type::Body::UINT32_TYPE:
case schema::Type::Body::UINT64_TYPE:
case schema::Type::Body::FLOAT32_TYPE:
case schema::Type::Body::FLOAT64_TYPE:
case schema::Type::Body::TEXT_TYPE:
case schema::Type::Body::DATA_TYPE:
return of(body.which());
case schema::Type::Body::STRUCT_TYPE:
return of(context.getDependency(body.getStructType()).asStruct());
case schema::Type::Body::ENUM_TYPE:
return of(context.getDependency(body.getEnumType()).asEnum());
case schema::Type::Body::INTERFACE_TYPE:
return of(context.getDependency(body.getInterfaceType()).asInterface());
case schema::Type::Body::LIST_TYPE:
return of(of(body.getListType(), context));
case schema::Type::Body::OBJECT_TYPE:
FAIL_PRECOND("List(Object) not supported.");
return ListSchema();
}
FAIL_CHECK("missing switch case");
return ListSchema();
}
StructSchema ListSchema::getStructElementType() const {
PRECOND(nestingDepth == 0 && elementType == schema::Type::Body::STRUCT_TYPE,
"ListSchema::getStructElementType(): The elements are not structs.");
return elementSchema.asStruct();
}
EnumSchema ListSchema::getEnumElementType() const {
PRECOND(nestingDepth == 0 && elementType == schema::Type::Body::ENUM_TYPE,
"ListSchema::getEnumElementType(): The elements are not enums.");
return elementSchema.asEnum();
}
InterfaceSchema ListSchema::getInterfaceElementType() const {
PRECOND(nestingDepth == 0 && elementType == schema::Type::Body::INTERFACE_TYPE,
"ListSchema::getInterfaceElementType(): The elements are not interfaces.");
return elementSchema.asInterface();
}
ListSchema ListSchema::getListElementType() const {
PRECOND(nestingDepth > 0,
"ListSchema::getListElementType(): The elements are not lists.");
return ListSchema(elementType, nestingDepth - 1, elementSchema);
}
} // namespace capnproto
This diff is collapsed.
......@@ -164,6 +164,18 @@ struct TestObject {
objectField @0 :Object;
}
struct TestOutOfOrder {
foo @3 :Text;
bar @2 :Text;
baz @8 :Text;
qux @0 :Text;
quux @6 :Text;
corge @4 :Text;
grault @1 :Text;
garply @7 :Text;
waldo @5 :Text;
}
struct TestUnion {
union0 @0 union {
# Pack union 0 under ideal conditions: there is no unused padding space prior to it.
......
......@@ -32,9 +32,10 @@ import qualified Data.Digest.MD5 as MD5
import qualified Data.Map as Map
import qualified Data.Set as Set
import qualified Data.List as List
import Data.Maybe(catMaybes)
import Data.Maybe(catMaybes, mapMaybe)
import Data.Binary.IEEE754(floatToWord, doubleToWord)
import Data.Map((!))
import Data.Function(on)
import Text.Printf(printf)
import Text.Hastache
import Text.Hastache.Context
......@@ -277,11 +278,47 @@ descDependencies (DescMethod d) =
concat $ typeDependencies (methodReturnType d) : map paramDependencies (methodParams d)
descDependencies _ = []
memberIndexes :: Int -> [(Int, Int)]
memberIndexes unionIndex = zip (repeat unionIndex) [0..]
memberTable (DescStruct desc) = let
-- Fields and unions of the struct.
topMembers = zip (memberIndexes 0) $ mapMaybe memberName
$ List.sortBy (compare `on` ordinal) $ structMembers desc
-- Fields of each union.
innerMembers = zipWith indexedUnionMembers [1..]
$ List.sortBy (compare `on` unionNumber) $ structUnions desc
ordinal (DescField f) = fieldNumber f
ordinal (DescUnion u) = unionNumber u
ordinal _ = 65536 -- doesn't really matter what this is; will be filtered out later
memberName (DescField f) = Just $ fieldName f
memberName (DescUnion u) = Just $ unionName u
memberName _ = Nothing
indexedUnionMembers i u = zip (memberIndexes i) $ mapMaybe memberName
$ List.sortBy (compare `on` ordinal) $ unionMembers u
in concat $ topMembers : innerMembers
memberTable (DescEnum desc) = zip (memberIndexes 0) $ map enumerantName
$ List.sortBy (compare `on` enumerantNumber) $ enumerants desc
memberTable (DescInterface desc) = zip (memberIndexes 0) $ map methodName
$ List.sortBy (compare `on` methodNumber) $ interfaceMethods desc
memberTable _ = []
outerFileContext schemaNodes = fileContext where
schemaDepContext parent i = mkStrContext context where
context "dependencyId" = MuVariable (printf "%016x" i :: String)
context s = parent s
schemaMemberByNameContext parent (ui, mi) = mkStrContext context where
context "memberUnionIndex" = MuVariable ui
context "memberIndex" = MuVariable mi
context s = parent s
schemaContext parent desc = mkStrContext context where
node = schemaNodes ! descId desc
......@@ -289,11 +326,18 @@ outerFileContext schemaNodes = fileContext where
depIds = map head $ List.group $ List.sort $ descDependencies desc
membersByName = map fst $ List.sortBy (compare `on` memberByNameKey) $ memberTable desc
memberByNameKey ((unionIndex, _), name) = (unionIndex, name)
context "schemaWordCount" = MuVariable $ div (length node + 7) 8
context "schemaBytes" = MuVariable $ delimit ",\n " codeLines
context "schemaId" = MuVariable (printf "%016x" (descId desc) :: String)
context "schemaDependencyCount" = MuVariable $ length depIds
context "schemaDependencies" =
MuList $ map (schemaDepContext context) depIds
context "schemaMemberCount" = MuVariable $ length membersByName
context "schemaMembersByName" =
MuList $ map (schemaMemberByNameContext context) membersByName
context s = parent s
enumerantContext parent desc = mkStrContext context where
......
......@@ -64,8 +64,13 @@ static const ::capnproto::internal::RawSchema* const d_{{schemaId}}[] = {
{{/schemaDependencies}}
nullptr
};
static const ::capnproto::internal::RawSchema::MemberInfo m_{{schemaId}}[] = {
{{#schemaMembersByName}}
{ {{memberUnionIndex}}, {{memberIndex}} },
{{/schemaMembersByName}}
};
const ::capnproto::internal::RawSchema s_{{schemaId}} = {
b_{{schemaId}}.words, d_{{schemaId}}
b_{{schemaId}}.words, d_{{schemaId}}, m_{{schemaId}}, {{schemaDependencyCount}}, {{schemaMemberCount}}
};
{{/typeSchema}}
{{/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