Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in / Register
Toggle navigation
C
capnproto
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Packages
Packages
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
submodule
capnproto
Commits
d7f3123c
Commit
d7f3123c
authored
May 22, 2013
by
Kenton Varda
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
SchemaLoader -- lets you load and use schemas that aren't compiled in.
parent
bf04264d
Show whitespace changes
Inline
Side-by-side
Showing
22 changed files
with
2254 additions
and
570 deletions
+2254
-570
Makefile.am
c++/Makefile.am
+3
-0
capnpc-capnp.c++
c++/src/capnproto/compiler/capnpc-capnp.c++
+94
-94
dynamic-test.c++
c++/src/capnproto/dynamic-test.c++
+53
-429
dynamic.c++
c++/src/capnproto/dynamic.c++
+0
-16
dynamic.h
c++/src/capnproto/dynamic.h
+4
-12
encoding-test.c++
c++/src/capnproto/encoding-test.c++
+28
-0
exception.c++
c++/src/capnproto/exception.c++
+11
-0
exception.h
c++/src/capnproto/exception.h
+4
-0
generated-header-support.h
c++/src/capnproto/generated-header-support.h
+8
-0
layout.c++
c++/src/capnproto/layout.c++
+1
-1
logging.h
c++/src/capnproto/logging.h
+7
-10
message.c++
c++/src/capnproto/message.c++
+16
-0
message.h
c++/src/capnproto/message.h
+35
-5
schema-loader-test.c++
c++/src/capnproto/schema-loader-test.c++
+224
-0
schema-loader.c++
c++/src/capnproto/schema-loader.c++
+1139
-0
schema-loader.h
c++/src/capnproto/schema-loader.h
+110
-0
schema.c++
c++/src/capnproto/schema.c++
+12
-0
schema.h
c++/src/capnproto/schema.h
+29
-0
test-util.c++
c++/src/capnproto/test-util.c++
+452
-0
test-util.h
c++/src/capnproto/test-util.h
+10
-0
c++-header.mustache
compiler/src/c++-header.mustache
+12
-2
c++-source.mustache
compiler/src/c++-source.mustache
+2
-1
No files found.
c++/Makefile.am
View file @
d7f3123c
...
...
@@ -71,6 +71,7 @@ includecapnp_HEADERS = \
src/capnproto/list.h
\
src/capnproto/message.h
\
src/capnproto/schema.h
\
src/capnproto/schema-loader.h
\
src/capnproto/dynamic.h
\
src/capnproto/stringify.h
\
src/capnproto/io.h
\
...
...
@@ -100,6 +101,7 @@ libcapnproto_a_SOURCES= \
src/capnproto/list.c++
\
src/capnproto/message.c++
\
src/capnproto/schema.c++
\
src/capnproto/schema-loader.c++
\
src/capnproto/dynamic.c++
\
src/capnproto/stringify.c++
\
src/capnproto/io.c++
\
...
...
@@ -146,6 +148,7 @@ capnproto_test_SOURCES = \
src/capnproto/layout-test.c++
\
src/capnproto/message-test.c++
\
src/capnproto/schema-test.c++
\
src/capnproto/schema-loader-test.c++
\
src/capnproto/dynamic-test.c++
\
src/capnproto/stringify-test.c++
\
src/capnproto/encoding-test.c++
\
...
...
c++/src/capnproto/compiler/capnpc-capnp.c++
View file @
d7f3123c
...
...
@@ -30,6 +30,9 @@
#include "../serialize.h"
#include "../logging.h"
#include "../io.h"
#include "../schema-loader.h"
#include "../dynamic.h"
#include "../stringify.h"
#include <unistd.h>
#include <unordered_map>
#include <vector>
...
...
@@ -207,18 +210,13 @@ inline Indent operator*(Stringifier, Indent i) { return i; }
// =======================================================================================
std
::
unordered_map
<
uint64_t
,
schema
::
Node
::
Reader
>
schemaMap
;
SchemaLoader
schemaLoader
;
schema
::
Node
::
Reader
findNode
(
uint64_t
id
)
{
auto
iter
=
schemaMap
.
find
(
id
);
PRECOND
(
iter
!=
schemaMap
.
end
(),
"Missing schema node."
,
hex
(
id
));
return
iter
->
second
;
}
Text
::
Reader
getUnqualifiedName
(
schema
::
Node
::
Reader
node
)
{
auto
parent
=
findNode
(
node
.
getScopeId
());
for
(
auto
nested
:
parent
.
getNestedNodes
())
{
if
(
nested
.
getId
()
==
node
.
getId
())
{
Text
::
Reader
getUnqualifiedName
(
Schema
schema
)
{
auto
proto
=
schema
.
getProto
();
auto
parent
=
schemaLoader
.
get
(
proto
.
getScopeId
());
for
(
auto
nested
:
parent
.
getProto
().
getNestedNodes
())
{
if
(
nested
.
getId
()
==
proto
.
getId
())
{
return
nested
.
getName
();
}
}
...
...
@@ -226,39 +224,42 @@ Text::Reader getUnqualifiedName(schema::Node::Reader node) {
return
"(?)"
;
}
TextBlob
nodeName
(
schema
::
Node
::
Reader
target
,
schema
::
Node
::
Reader
scope
)
{
std
::
vector
<
schema
::
Node
::
Reader
>
targetParents
;
std
::
vector
<
schema
::
Node
::
Reader
>
scopeParts
;
TextBlob
nodeName
(
Schema
target
,
Schema
scope
)
{
std
::
vector
<
Schema
>
targetParents
;
std
::
vector
<
Schema
>
scopeParts
;
{
schema
::
Node
::
Reader
parent
=
target
;
while
(
parent
.
getScopeId
()
!=
0
)
{
parent
=
findNode
(
parent
.
getScopeId
());
Schema
parent
=
target
;
while
(
parent
.
get
Proto
().
get
ScopeId
()
!=
0
)
{
parent
=
schemaLoader
.
get
(
parent
.
getProto
()
.
getScopeId
());
targetParents
.
push_back
(
parent
);
}
}
{
schema
::
Node
::
Reader
parent
=
scope
;
scopeParts
.
push_back
(
scope
);
while
(
parent
.
getScopeId
()
!=
0
)
{
parent
=
findNode
(
parent
.
getScopeId
());
Schema
parent
=
scope
;
scopeParts
.
push_back
(
parent
);
while
(
parent
.
get
Proto
().
get
ScopeId
()
!=
0
)
{
parent
=
schemaLoader
.
get
(
parent
.
getProto
()
.
getScopeId
());
scopeParts
.
push_back
(
parent
);
}
}
// Remove common scope.
while
(
!
scopeParts
.
empty
()
&&
!
targetParents
.
empty
()
&&
scopeParts
.
back
()
.
getId
()
==
targetParents
.
back
().
getId
())
{
scopeParts
.
back
()
==
targetParents
.
back
())
{
scopeParts
.
pop_back
();
targetParents
.
pop_back
();
}
// TODO(someday): This is broken in that we aren't checking for shadowing.
TextBlob
path
=
text
();
while
(
!
targetParents
.
empty
())
{
auto
part
=
targetParents
.
back
();
if
(
part
.
getScopeId
()
==
0
)
{
path
=
text
(
move
(
path
),
"import
\"
"
,
part
.
getDisplayName
(),
"
\"
."
);
auto
proto
=
part
.
getProto
();
if
(
proto
.
getScopeId
()
==
0
)
{
path
=
text
(
move
(
path
),
"import
\"
"
,
proto
.
getDisplayName
(),
"
\"
."
);
}
else
{
path
=
text
(
move
(
path
),
getUnqualifiedName
(
part
),
"."
);
}
...
...
@@ -268,7 +269,7 @@ TextBlob nodeName(schema::Node::Reader target, schema::Node::Reader scope) {
return
text
(
move
(
path
),
getUnqualifiedName
(
target
));
}
TextBlob
genType
(
schema
::
Type
::
Reader
type
,
schema
::
Node
::
Reader
scope
)
{
TextBlob
genType
(
schema
::
Type
::
Reader
type
,
Schema
scope
)
{
auto
body
=
type
.
getBody
();
switch
(
body
.
which
())
{
case
schema
:
:
Type
::
Body
::
VOID_TYPE
:
return
text
(
"Void"
);
...
...
@@ -287,10 +288,12 @@ TextBlob genType(schema::Type::Reader type, schema::Node::Reader scope) {
case
schema
:
:
Type
::
Body
::
DATA_TYPE
:
return
text
(
"Data"
);
case
schema
:
:
Type
::
Body
::
LIST_TYPE
:
return
text
(
"List("
,
genType
(
body
.
getListType
(),
scope
),
")"
);
case
schema
:
:
Type
::
Body
::
ENUM_TYPE
:
return
nodeName
(
findNode
(
body
.
getEnumType
()),
scope
);
case
schema
:
:
Type
::
Body
::
STRUCT_TYPE
:
return
nodeName
(
findNode
(
body
.
getStructType
()),
scope
);
case
schema
:
:
Type
::
Body
::
ENUM_TYPE
:
return
nodeName
(
scope
.
getDependency
(
body
.
getEnumType
()),
scope
);
case
schema
:
:
Type
::
Body
::
STRUCT_TYPE
:
return
nodeName
(
scope
.
getDependency
(
body
.
getStructType
()),
scope
);
case
schema
:
:
Type
::
Body
::
INTERFACE_TYPE
:
return
nodeName
(
findNode
(
body
.
getInterfaceType
()),
scope
);
return
nodeName
(
scope
.
getDependency
(
body
.
getInterfaceType
()),
scope
);
case
schema
:
:
Type
::
Body
::
OBJECT_TYPE
:
return
text
(
"Object"
);
}
return
text
();
...
...
@@ -336,18 +339,18 @@ bool isEmptyValue(schema::Value::Reader value) {
case
schema
:
:
Value
::
Body
::
UINT64_VALUE
:
return
body
.
getUint64Value
()
==
0
;
case
schema
:
:
Value
::
Body
::
FLOAT32_VALUE
:
return
body
.
getFloat32Value
()
==
0
;
case
schema
:
:
Value
::
Body
::
FLOAT64_VALUE
:
return
body
.
getFloat64Value
()
==
0
;
case
schema
:
:
Value
::
Body
::
TEXT_VALUE
:
return
body
.
getTextValue
().
size
()
==
0
;
case
schema
:
:
Value
::
Body
::
DATA_VALUE
:
return
body
.
getDataValue
().
size
()
==
0
;
case
schema
:
:
Value
::
Body
::
LIST_VALUE
:
return
true
;
// TODO(soon): list values
case
schema
:
:
Value
::
Body
::
TEXT_VALUE
:
return
!
body
.
hasTextValue
()
;
case
schema
:
:
Value
::
Body
::
DATA_VALUE
:
return
!
body
.
hasDataValue
()
;
case
schema
:
:
Value
::
Body
::
LIST_VALUE
:
return
!
body
.
hasListValue
();
case
schema
:
:
Value
::
Body
::
ENUM_VALUE
:
return
body
.
getEnumValue
()
==
0
;
case
schema
:
:
Value
::
Body
::
STRUCT_VALUE
:
return
true
;
// TODO(soon): struct values
case
schema
:
:
Value
::
Body
::
STRUCT_VALUE
:
return
!
body
.
hasStructValue
();
case
schema
:
:
Value
::
Body
::
INTERFACE_VALUE
:
return
true
;
case
schema
:
:
Value
::
Body
::
OBJECT_VALUE
:
return
true
;
}
return
true
;
}
TextBlob
genValue
(
schema
::
Type
::
Reader
type
,
schema
::
Value
::
Reader
value
)
{
TextBlob
genValue
(
schema
::
Type
::
Reader
type
,
schema
::
Value
::
Reader
value
,
Schema
scope
)
{
auto
body
=
value
.
getBody
();
switch
(
body
.
which
())
{
case
schema
:
:
Value
::
Body
::
VOID_VALUE
:
return
text
(
"void"
);
...
...
@@ -362,25 +365,17 @@ TextBlob genValue(schema::Type::Reader type, schema::Value::Reader value) {
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
text
(
"TODO"
);
// TODO(soon): escape strings
case
schema
:
:
Value
::
Body
::
DATA_VALUE
:
return
text
(
"TODO"
);
// TODO(soon): escape strings
case
schema
:
:
Value
::
Body
::
TEXT_VALUE
:
return
stringify
(
body
.
getTextValue
());
case
schema
:
:
Value
::
Body
::
DATA_VALUE
:
return
stringify
(
body
.
getDataValue
());
case
schema
:
:
Value
::
Body
::
LIST_VALUE
:
{
PRECOND
(
type
.
getBody
().
which
()
==
schema
::
Type
::
Body
::
LIST_TYPE
,
"type/value mismatch"
);
// TODO(soon): Requires dynamic message reading.
return
text
(
"TODO"
);
// int i = 0;
// return text("[",
// FOR_EACH(body.getListValue(), element) {
// return text(i++ > 0 ? ", " : "",
// genValue(type.getBody().getListType(), element));
// },
// "]");
auto
value
=
body
.
getListValue
<
DynamicList
>
(
ListSchema
::
of
(
type
.
getBody
().
getListType
(),
scope
));
return
text
(
stringify
(
value
));
}
case
schema
:
:
Value
::
Body
::
ENUM_VALUE
:
{
PRECOND
(
type
.
getBody
().
which
()
==
schema
::
Type
::
Body
::
ENUM_TYPE
,
"type/value mismatch"
);
auto
enumNode
=
findNode
(
type
.
getBody
().
getEnumType
());
PRECOND
(
enumNode
.
getBody
().
which
()
==
schema
::
Node
::
Body
::
ENUM_NODE
,
"schema.Type claimed to be an enum, but referred to some other node type."
);
auto
enumNode
=
scope
.
getDependency
(
type
.
getBody
().
getEnumType
()).
asEnum
().
getProto
();
auto
enumType
=
enumNode
.
getBody
().
getEnumNode
();
auto
enumerants
=
enumType
.
getEnumerants
();
PRECOND
(
body
.
getEnumValue
()
<
enumerants
.
size
(),
...
...
@@ -389,8 +384,9 @@ TextBlob genValue(schema::Type::Reader type, schema::Value::Reader value) {
}
case
schema
:
:
Value
::
Body
::
STRUCT_VALUE
:
{
PRECOND
(
type
.
getBody
().
which
()
==
schema
::
Type
::
Body
::
STRUCT_TYPE
,
"type/value mismatch"
);
// TODO(soon): Requires dynamic message reading.
return
text
(
"TODO"
);
auto
value
=
body
.
getStructValue
<
DynamicStruct
>
(
scope
.
getDependency
(
type
.
getBody
().
getStructType
()).
asStruct
());
return
text
(
stringify
(
value
));
}
case
schema
:
:
Value
::
Body
::
INTERFACE_VALUE
:
{
return
text
(
""
);
...
...
@@ -403,23 +399,24 @@ TextBlob genValue(schema::Type::Reader type, schema::Value::Reader value) {
}
TextBlob
genAnnotation
(
schema
::
Annotation
::
Reader
annotation
,
schema
::
Node
::
Reader
scope
,
Schema
scope
,
const
char
*
prefix
=
" "
,
const
char
*
suffix
=
""
)
{
auto
decl
=
findNode
(
annotation
.
getId
());
auto
body
=
decl
.
getBody
();
auto
decl
=
schemaLoader
.
get
(
annotation
.
getId
());
auto
body
=
decl
.
get
Proto
().
get
Body
();
PRECOND
(
body
.
which
()
==
schema
::
Node
::
Body
::
ANNOTATION_NODE
);
auto
annDecl
=
body
.
getAnnotationNode
();
// TODO: Don't use displayName.
return
text
(
prefix
,
"$"
,
nodeName
(
decl
,
scope
),
"("
,
genValue
(
annDecl
.
getType
(),
annotation
.
getValue
()),
")"
,
suffix
);
genValue
(
annDecl
.
getType
(),
annotation
.
getValue
()
,
scope
),
")"
,
suffix
);
}
TextBlob
genAnnotations
(
List
<
schema
::
Annotation
>::
Reader
list
,
schema
::
Node
::
Reader
scope
)
{
TextBlob
genAnnotations
(
List
<
schema
::
Annotation
>::
Reader
list
,
Schema
scope
)
{
return
FOR_EACH
(
list
,
ann
)
{
return
genAnnotation
(
ann
,
scope
);
};
}
TextBlob
genAnnotations
(
schema
::
Node
::
Reader
node
)
{
return
genAnnotations
(
node
.
getAnnotations
(),
findNode
(
node
.
getScopeId
()));
TextBlob
genAnnotations
(
Schema
schema
)
{
auto
proto
=
schema
.
getProto
();
return
genAnnotations
(
proto
.
getAnnotations
(),
schemaLoader
.
get
(
proto
.
getScopeId
()));
}
const
char
*
elementSizeName
(
schema
::
ElementSize
size
)
{
...
...
@@ -437,7 +434,7 @@ const char* elementSizeName(schema::ElementSize size) {
}
TextBlob
genStructMember
(
schema
::
StructNode
::
Member
::
Reader
member
,
schema
::
Node
::
Reader
scope
,
Indent
indent
,
int
unionTag
=
-
1
)
{
Schema
scope
,
Indent
indent
,
int
unionTag
=
-
1
)
{
switch
(
member
.
getBody
().
which
())
{
case
schema
:
:
StructNode
::
Member
::
Body
::
FIELD_MEMBER
:
{
auto
field
=
member
.
getBody
().
getFieldMember
();
...
...
@@ -445,7 +442,7 @@ TextBlob genStructMember(schema::StructNode::Member::Reader member,
return
text
(
indent
,
member
.
getName
(),
" @"
,
member
.
getOrdinal
(),
" :"
,
genType
(
field
.
getType
(),
scope
),
isEmptyValue
(
field
.
getDefaultValue
())
?
text
(
""
)
:
text
(
" = "
,
genValue
(
field
.
getType
(),
field
.
getDefaultValue
())),
text
(
" = "
,
genValue
(
field
.
getType
(),
field
.
getDefaultValue
()
,
scope
)),
genAnnotations
(
member
.
getAnnotations
(),
scope
),
"; # "
,
size
==
-
1
?
text
(
"ptr["
,
field
.
getOffset
(),
"]"
)
:
text
(
"bits["
,
field
.
getOffset
()
*
size
,
", "
,
...
...
@@ -469,22 +466,23 @@ TextBlob genStructMember(schema::StructNode::Member::Reader member,
return
text
();
}
TextBlob
genNestedDecls
(
schema
::
Node
::
Reader
node
,
Indent
indent
);
TextBlob
genNestedDecls
(
Schema
schema
,
Indent
indent
);
TextBlob
genDecl
(
schema
::
Node
::
Reader
node
,
Text
::
Reader
name
,
uint64_t
scopeId
,
Indent
indent
)
{
if
(
node
.
getScopeId
()
!=
scopeId
)
{
TextBlob
genDecl
(
Schema
schema
,
Text
::
Reader
name
,
uint64_t
scopeId
,
Indent
indent
)
{
auto
proto
=
schema
.
getProto
();
if
(
proto
.
getScopeId
()
!=
scopeId
)
{
// This appears to be an alias for something declared elsewhere.
FAIL_PRECOND
(
"Aliases not implemented."
);
}
switch
(
node
.
getBody
().
which
())
{
switch
(
proto
.
getBody
().
which
())
{
case
schema
:
:
Node
::
Body
::
FILE_NODE
:
FAIL_PRECOND
(
"Encountered nested file node."
);
break
;
case
schema
:
:
Node
::
Body
::
STRUCT_NODE
:
{
auto
body
=
node
.
getBody
().
getStructNode
();
auto
body
=
proto
.
getBody
().
getStructNode
();
return
text
(
indent
,
"struct "
,
name
,
" @0x"
,
hex
(
node
.
getId
()),
genAnnotations
(
node
),
" { # "
,
indent
,
"struct "
,
name
,
" @0x"
,
hex
(
proto
.
getId
()),
genAnnotations
(
schema
),
" { # "
,
body
.
getDataSectionWordSize
()
*
8
,
" bytes, "
,
body
.
getPointerSectionSize
(),
" ptrs"
,
body
.
getPreferredListEncoding
()
==
schema
::
ElementSize
::
INLINE_COMPOSITE
...
...
@@ -492,28 +490,28 @@ TextBlob genDecl(schema::Node::Reader node, Text::Reader name, uint64_t scopeId,
:
text
(
", packed as "
,
elementSizeName
(
body
.
getPreferredListEncoding
())),
"
\n
"
,
FOR_EACH
(
body
.
getMembers
(),
member
)
{
return
genStructMember
(
member
,
node
,
indent
.
next
());
return
genStructMember
(
member
,
schema
,
indent
.
next
());
},
genNestedDecls
(
node
,
indent
.
next
()),
genNestedDecls
(
schema
,
indent
.
next
()),
indent
,
"}
\n
"
);
}
case
schema
:
:
Node
::
Body
::
ENUM_NODE
:
{
auto
body
=
node
.
getBody
().
getEnumNode
();
auto
body
=
proto
.
getBody
().
getEnumNode
();
uint
i
=
0
;
return
text
(
indent
,
"enum "
,
name
,
" @0x"
,
hex
(
node
.
getId
()),
genAnnotations
(
node
),
" {
\n
"
,
indent
,
"enum "
,
name
,
" @0x"
,
hex
(
proto
.
getId
()),
genAnnotations
(
schema
),
" {
\n
"
,
FOR_EACH
(
body
.
getEnumerants
(),
enumerant
)
{
return
text
(
indent
.
next
(),
enumerant
.
getName
(),
" @"
,
i
++
,
genAnnotations
(
enumerant
.
getAnnotations
(),
node
),
";
\n
"
);
genAnnotations
(
enumerant
.
getAnnotations
(),
schema
),
";
\n
"
);
},
genNestedDecls
(
node
,
indent
.
next
()),
genNestedDecls
(
schema
,
indent
.
next
()),
indent
,
"}
\n
"
);
}
case
schema
:
:
Node
::
Body
::
INTERFACE_NODE
:
{
auto
body
=
node
.
getBody
().
getInterfaceNode
();
auto
body
=
proto
.
getBody
().
getInterfaceNode
();
uint
i
=
0
;
return
text
(
indent
,
"interface "
,
name
,
" @0x"
,
hex
(
node
.
getId
()),
genAnnotations
(
node
),
" {
\n
"
,
indent
,
"interface "
,
name
,
" @0x"
,
hex
(
proto
.
getId
()),
genAnnotations
(
schema
),
" {
\n
"
,
FOR_EACH
(
body
.
getMethods
(),
method
)
{
int
j
=
0
;
return
text
(
...
...
@@ -523,26 +521,26 @@ TextBlob genDecl(schema::Node::Reader node, Text::Reader name, uint64_t scopeId,
!
isEmptyValue
(
param
.
getDefaultValue
());
return
text
(
j
++
>
0
?
", "
:
""
,
param
.
getName
(),
": "
,
genType
(
param
.
getType
(),
node
),
param
.
getName
(),
": "
,
genType
(
param
.
getType
(),
schema
),
hasDefault
?
text
(
" = "
,
genValue
(
param
.
getType
(),
param
.
getDefaultValue
()))
?
text
(
" = "
,
genValue
(
param
.
getType
(),
param
.
getDefaultValue
()
,
schema
))
:
text
(),
genAnnotations
(
param
.
getAnnotations
(),
node
));
genAnnotations
(
param
.
getAnnotations
(),
schema
));
},
") :"
,
genType
(
method
.
getReturnType
(),
node
),
genAnnotations
(
method
.
getAnnotations
(),
node
),
";
\n
"
);
") :"
,
genType
(
method
.
getReturnType
(),
schema
),
genAnnotations
(
method
.
getAnnotations
(),
schema
),
";
\n
"
);
},
genNestedDecls
(
node
,
indent
.
next
()),
genNestedDecls
(
schema
,
indent
.
next
()),
indent
,
"}
\n
"
);
}
case
schema
:
:
Node
::
Body
::
CONST_NODE
:
{
auto
body
=
node
.
getBody
().
getConstNode
();
auto
body
=
proto
.
getBody
().
getConstNode
();
return
text
(
indent
,
"const "
,
name
,
" @0x"
,
hex
(
node
.
getId
()),
" :"
,
genType
(
body
.
getType
(),
node
),
" = "
,
genValue
(
body
.
getType
(),
body
.
getValue
()),
";
\n
"
);
indent
,
"const "
,
name
,
" @0x"
,
hex
(
proto
.
getId
()),
" :"
,
genType
(
body
.
getType
(),
schema
),
" = "
,
genValue
(
body
.
getType
(),
body
.
getValue
()
,
schema
),
";
\n
"
);
}
case
schema
:
:
Node
::
Body
::
ANNOTATION_NODE
:
{
auto
body
=
node
.
getBody
().
getAnnotationNode
();
auto
body
=
proto
.
getBody
().
getAnnotationNode
();
CappedArray
<
const
char
*
,
11
>
targets
;
uint
i
=
0
;
if
(
body
.
getTargetsFile
())
targets
[
i
++
]
=
"file"
;
...
...
@@ -563,29 +561,31 @@ TextBlob genDecl(schema::Node::Reader node, Text::Reader name, uint64_t scopeId,
targets
.
setSize
(
i
);
}
return
text
(
indent
,
"annotation "
,
name
,
" @0x"
,
hex
(
node
.
getId
()),
indent
,
"annotation "
,
name
,
" @0x"
,
hex
(
proto
.
getId
()),
" ("
,
strArray
(
targets
,
", "
),
") :"
,
genType
(
body
.
getType
(),
node
),
genAnnotations
(
node
),
";
\n
"
);
genType
(
body
.
getType
(),
schema
),
genAnnotations
(
schema
),
";
\n
"
);
}
}
return
text
();
}
TextBlob
genNestedDecls
(
schema
::
Node
::
Reader
node
,
Indent
indent
)
{
return
FOR_EACH
(
node
.
getNestedNodes
(),
nested
)
{
return
genDecl
(
findNode
(
nested
.
getId
()),
nested
.
getName
(),
node
.
getId
(),
indent
);
TextBlob
genNestedDecls
(
Schema
schema
,
Indent
indent
)
{
uint64_t
id
=
schema
.
getProto
().
getId
();
return
FOR_EACH
(
schema
.
getProto
().
getNestedNodes
(),
nested
)
{
return
genDecl
(
schemaLoader
.
get
(
nested
.
getId
()),
nested
.
getName
(),
id
,
indent
);
};
}
TextBlob
genFile
(
schema
::
Node
::
Reader
file
)
{
auto
body
=
file
.
getBody
();
TextBlob
genFile
(
Schema
file
)
{
auto
proto
=
file
.
getProto
();
auto
body
=
proto
.
getBody
();
PRECOND
(
body
.
which
()
==
schema
::
Node
::
Body
::
FILE_NODE
,
"Expected a file node."
,
body
.
which
());
return
text
(
"# "
,
file
.
getDisplayName
(),
"
\n
"
,
"@0x"
,
hex
(
file
.
getId
()),
";
\n
"
,
FOR_EACH
(
file
.
getAnnotations
(),
ann
)
{
return
genAnnotation
(
ann
,
file
,
""
,
";
\n
"
);
},
"# "
,
proto
.
getDisplayName
(),
"
\n
"
,
"@0x"
,
hex
(
proto
.
getId
()),
";
\n
"
,
FOR_EACH
(
proto
.
getAnnotations
(),
ann
)
{
return
genAnnotation
(
ann
,
file
,
""
,
";
\n
"
);
},
genNestedDecls
(
file
,
Indent
(
0
)));
}
...
...
@@ -596,14 +596,14 @@ int main(int argc, char* argv[]) {
auto
request
=
reader
.
getRoot
<
schema
::
CodeGeneratorRequest
>
();
for
(
auto
node
:
request
.
getNodes
())
{
schema
Map
[
node
.
getId
()]
=
node
;
schema
Loader
.
load
(
node
)
;
}
FdOutputStream
rawOut
(
STDOUT_FILENO
);
BufferedOutputStreamWrapper
out
(
rawOut
);
for
(
auto
fileId
:
request
.
getRequestedFiles
())
{
genFile
(
findNode
(
fileId
)).
writeTo
(
out
);
genFile
(
schemaLoader
.
get
(
fileId
)).
writeTo
(
out
);
}
return
0
;
...
...
c++/src/capnproto/dynamic-test.c++
View file @
d7f3123c
...
...
@@ -55,428 +55,30 @@ namespace capnproto {
namespace
internal
{
namespace
{
void
dynamicInitTestMessage
(
DynamicStruct
::
Builder
builder
)
{
builder
.
set
(
"voidField"
,
Void
::
VOID
);
builder
.
set
(
"boolField"
,
true
);
builder
.
set
(
"int8Field"
,
-
123
);
builder
.
set
(
"int16Field"
,
-
12345
);
builder
.
set
(
"int32Field"
,
-
12345678
);
builder
.
set
(
"int64Field"
,
-
123456789012345ll
);
builder
.
set
(
"uInt8Field"
,
234u
);
builder
.
set
(
"uInt16Field"
,
45678u
);
builder
.
set
(
"uInt32Field"
,
3456789012u
);
builder
.
set
(
"uInt64Field"
,
12345678901234567890ull
);
builder
.
set
(
"float32Field"
,
1234.5
);
builder
.
set
(
"float64Field"
,
-
123e45
);
builder
.
set
(
"textField"
,
"foo"
);
builder
.
set
(
"dataField"
,
"bar"
);
{
auto
subBuilder
=
builder
.
init
(
"structField"
).
as
<
DynamicStruct
>
();
subBuilder
.
set
(
"voidField"
,
Void
::
VOID
);
subBuilder
.
set
(
"boolField"
,
true
);
subBuilder
.
set
(
"int8Field"
,
-
12
);
subBuilder
.
set
(
"int16Field"
,
3456
);
subBuilder
.
set
(
"int32Field"
,
-
78901234
);
subBuilder
.
set
(
"int64Field"
,
56789012345678ll
);
subBuilder
.
set
(
"uInt8Field"
,
90u
);
subBuilder
.
set
(
"uInt16Field"
,
1234u
);
subBuilder
.
set
(
"uInt32Field"
,
56789012u
);
subBuilder
.
set
(
"uInt64Field"
,
345678901234567890ull
);
subBuilder
.
set
(
"float32Field"
,
-
1.25e-10
);
subBuilder
.
set
(
"float64Field"
,
345
);
subBuilder
.
set
(
"textField"
,
"baz"
);
subBuilder
.
set
(
"dataField"
,
"qux"
);
{
auto
subSubBuilder
=
subBuilder
.
init
(
"structField"
).
as
<
DynamicStruct
>
();
subSubBuilder
.
set
(
"textField"
,
"nested"
);
subSubBuilder
.
init
(
"structField"
).
as
<
DynamicStruct
>
().
set
(
"textField"
,
"really nested"
);
}
subBuilder
.
set
(
"enumField"
,
TestEnum
::
BAZ
);
subBuilder
.
set
(
"voidList"
,
{
Void
::
VOID
,
Void
::
VOID
,
Void
::
VOID
});
subBuilder
.
set
(
"boolList"
,
{
false
,
true
,
false
,
true
,
true
});
subBuilder
.
set
(
"int8List"
,
{
12
,
-
34
,
-
0x80
,
0x7f
});
subBuilder
.
set
(
"int16List"
,
{
1234
,
-
5678
,
-
0x8000
,
0x7fff
});
// 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
});
subBuilder
.
set
(
"uInt32List"
,
{
12345678u
,
90123456u
,
0u
,
0xffffffffu
});
subBuilder
.
set
(
"uInt64List"
,
{
123456789012345ull
,
678901234567890ull
,
0ull
,
0xffffffffffffffffull
});
subBuilder
.
set
(
"float32List"
,
{
0
,
1234567
,
1e37
,
-
1e37
,
1e-37
,
-
1e-37
});
subBuilder
.
set
(
"float64List"
,
{
0
,
123456789012345
,
1e306
,
-
1e306
,
1e-306
,
-
1e-306
});
subBuilder
.
set
(
"textList"
,
{
"quux"
,
"corge"
,
"grault"
});
subBuilder
.
set
(
"dataList"
,
{
"garply"
,
"waldo"
,
"fred"
});
{
auto
listBuilder
=
subBuilder
.
init
(
"structList"
,
3
).
as
<
DynamicList
>
();
listBuilder
[
0
].
as
<
DynamicStruct
>
().
set
(
"textField"
,
"x structlist 1"
);
listBuilder
[
1
].
as
<
DynamicStruct
>
().
set
(
"textField"
,
"x structlist 2"
);
listBuilder
[
2
].
as
<
DynamicStruct
>
().
set
(
"textField"
,
"x structlist 3"
);
}
subBuilder
.
set
(
"enumList"
,
{
TestEnum
::
QUX
,
"bar"
,
"grault"
});
}
builder
.
set
(
"enumField"
,
"corge"
);
builder
.
init
(
"voidList"
,
6
);
builder
.
set
(
"boolList"
,
{
true
,
false
,
false
,
true
});
builder
.
set
(
"int8List"
,
{
111
,
-
111
});
builder
.
set
(
"int16List"
,
{
11111
,
-
11111
});
builder
.
set
(
"int32List"
,
{
111111111
,
-
111111111
});
builder
.
set
(
"int64List"
,
{
1111111111111111111ll
,
-
1111111111111111111ll
});
builder
.
set
(
"uInt8List"
,
{
111u
,
222u
});
builder
.
set
(
"uInt16List"
,
{
33333u
,
44444u
});
builder
.
set
(
"uInt32List"
,
{
3333333333u
});
builder
.
set
(
"uInt64List"
,
{
11111111111111111111ull
});
builder
.
set
(
"float32List"
,
{
5555.5
,
std
::
numeric_limits
<
float
>::
infinity
(),
-
std
::
numeric_limits
<
float
>::
infinity
(),
std
::
numeric_limits
<
float
>::
quiet_NaN
()});
builder
.
set
(
"float64List"
,
{
7777.75
,
std
::
numeric_limits
<
double
>::
infinity
(),
-
std
::
numeric_limits
<
double
>::
infinity
(),
std
::
numeric_limits
<
double
>::
quiet_NaN
()});
builder
.
set
(
"textList"
,
{
"plugh"
,
"xyzzy"
,
"thud"
});
builder
.
set
(
"dataList"
,
{
"oops"
,
"exhausted"
,
"rfc3092"
});
{
auto
listBuilder
=
builder
.
init
(
"structList"
,
3
).
as
<
DynamicList
>
();
listBuilder
[
0
].
as
<
DynamicStruct
>
().
set
(
"textField"
,
"structlist 1"
);
listBuilder
[
1
].
as
<
DynamicStruct
>
().
set
(
"textField"
,
"structlist 2"
);
listBuilder
[
2
].
as
<
DynamicStruct
>
().
set
(
"textField"
,
"structlist 3"
);
}
builder
.
set
(
"enumList"
,
{
TestEnum
::
FOO
,
TestEnum
::
GARPLY
});
}
template
<
typename
T
>
void
expectPrimitiveEq
(
T
a
,
T
b
)
{
EXPECT_EQ
(
a
,
b
);
}
void
expectPrimitiveEq
(
float
a
,
float
b
)
{
EXPECT_FLOAT_EQ
(
a
,
b
);
}
void
expectPrimitiveEq
(
double
a
,
double
b
)
{
EXPECT_DOUBLE_EQ
(
a
,
b
);
}
void
expectPrimitiveEq
(
Text
::
Reader
a
,
Text
::
Builder
b
)
{
EXPECT_EQ
(
a
,
b
);
}
void
expectPrimitiveEq
(
Data
::
Reader
a
,
Data
::
Builder
b
)
{
EXPECT_EQ
(
a
,
b
);
}
// Hack because as<>() is a template-parameter-dependent lookup everywhere below...
#define as template as
template
<
typename
Element
,
typename
T
>
void
checkList
(
T
reader
,
std
::
initializer_list
<
ReaderFor
<
Element
>>
expected
)
{
auto
list
=
reader
.
as
<
DynamicList
>
();
auto
list
=
reader
.
template
as
<
DynamicList
>
();
ASSERT_EQ
(
expected
.
size
(),
list
.
size
());
for
(
uint
i
=
0
;
i
<
expected
.
size
();
i
++
)
{
expectPrimitiveEq
(
expected
.
begin
()[
i
],
list
[
i
].
as
<
Element
>
());
EXPECT_EQ
(
expected
.
begin
()[
i
],
list
[
i
].
template
as
<
Element
>
());
}
auto
typed
=
reader
.
as
<
List
<
Element
>>
();
auto
typed
=
reader
.
template
as
<
List
<
Element
>>
();
ASSERT_EQ
(
expected
.
size
(),
typed
.
size
());
for
(
uint
i
=
0
;
i
<
expected
.
size
();
i
++
)
{
expectPrimitiveEq
(
expected
.
begin
()[
i
],
typed
[
i
]);
}
}
inline
bool
isNaN
(
float
f
)
{
return
f
!=
f
;
}
inline
bool
isNaN
(
double
f
)
{
return
f
!=
f
;
}
template
<
typename
Reader
>
void
dynamicCheckTestMessage
(
Reader
reader
)
{
EXPECT_EQ
(
Void
::
VOID
,
reader
.
get
(
"voidField"
).
as
<
Void
>
());
EXPECT_EQ
(
true
,
reader
.
get
(
"boolField"
).
as
<
bool
>
());
EXPECT_EQ
(
-
123
,
reader
.
get
(
"int8Field"
).
as
<
int8_t
>
());
EXPECT_EQ
(
-
12345
,
reader
.
get
(
"int16Field"
).
as
<
int16_t
>
());
EXPECT_EQ
(
-
12345678
,
reader
.
get
(
"int32Field"
).
as
<
int32_t
>
());
EXPECT_EQ
(
-
123456789012345ll
,
reader
.
get
(
"int64Field"
).
as
<
int64_t
>
());
EXPECT_EQ
(
234u
,
reader
.
get
(
"uInt8Field"
).
as
<
uint8_t
>
());
EXPECT_EQ
(
45678u
,
reader
.
get
(
"uInt16Field"
).
as
<
uint16_t
>
());
EXPECT_EQ
(
3456789012u
,
reader
.
get
(
"uInt32Field"
).
as
<
uint32_t
>
());
EXPECT_EQ
(
12345678901234567890ull
,
reader
.
get
(
"uInt64Field"
).
as
<
uint64_t
>
());
EXPECT_FLOAT_EQ
(
1234.5
f
,
reader
.
get
(
"float32Field"
).
as
<
float
>
());
EXPECT_DOUBLE_EQ
(
-
123e45
,
reader
.
get
(
"float64Field"
).
as
<
double
>
());
EXPECT_EQ
(
"foo"
,
reader
.
get
(
"textField"
).
as
<
Text
>
());
EXPECT_EQ
(
"bar"
,
reader
.
get
(
"dataField"
).
as
<
Data
>
());
{
auto
subReader
=
reader
.
get
(
"structField"
).
as
<
DynamicStruct
>
();
EXPECT_EQ
(
Void
::
VOID
,
subReader
.
get
(
"voidField"
).
as
<
Void
>
());
EXPECT_EQ
(
true
,
subReader
.
get
(
"boolField"
).
as
<
bool
>
());
EXPECT_EQ
(
-
12
,
subReader
.
get
(
"int8Field"
).
as
<
int8_t
>
());
EXPECT_EQ
(
3456
,
subReader
.
get
(
"int16Field"
).
as
<
int16_t
>
());
EXPECT_EQ
(
-
78901234
,
subReader
.
get
(
"int32Field"
).
as
<
int32_t
>
());
EXPECT_EQ
(
56789012345678ll
,
subReader
.
get
(
"int64Field"
).
as
<
int64_t
>
());
EXPECT_EQ
(
90u
,
subReader
.
get
(
"uInt8Field"
).
as
<
uint8_t
>
());
EXPECT_EQ
(
1234u
,
subReader
.
get
(
"uInt16Field"
).
as
<
uint16_t
>
());
EXPECT_EQ
(
56789012u
,
subReader
.
get
(
"uInt32Field"
).
as
<
uint32_t
>
());
EXPECT_EQ
(
345678901234567890ull
,
subReader
.
get
(
"uInt64Field"
).
as
<
uint64_t
>
());
EXPECT_FLOAT_EQ
(
-
1.25e-10
f
,
subReader
.
get
(
"float32Field"
).
as
<
float
>
());
EXPECT_DOUBLE_EQ
(
345
,
subReader
.
get
(
"float64Field"
).
as
<
double
>
());
EXPECT_EQ
(
"baz"
,
subReader
.
get
(
"textField"
).
as
<
Text
>
());
EXPECT_EQ
(
"qux"
,
subReader
.
get
(
"dataField"
).
as
<
Data
>
());
{
auto
subSubReader
=
subReader
.
get
(
"structField"
).
as
<
DynamicStruct
>
();
EXPECT_EQ
(
"nested"
,
subSubReader
.
get
(
"textField"
).
as
<
Text
>
());
EXPECT_EQ
(
"really nested"
,
subSubReader
.
get
(
"structField"
).
as
<
DynamicStruct
>
()
.
get
(
"textField"
).
as
<
Text
>
());
}
EXPECT_EQ
(
TestEnum
::
BAZ
,
subReader
.
get
(
"enumField"
).
as
<
TestEnum
>
());
checkList
<
Void
>
(
subReader
.
get
(
"voidList"
),
{
Void
::
VOID
,
Void
::
VOID
,
Void
::
VOID
});
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
});
// 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
});
checkList
<
uint32_t
>
(
subReader
.
get
(
"uInt32List"
),
{
12345678u
,
90123456u
,
0u
,
0xffffffffu
});
checkList
<
uint64_t
>
(
subReader
.
get
(
"uInt64List"
),
{
123456789012345ull
,
678901234567890ull
,
0ull
,
0xffffffffffffffffull
});
checkList
<
float
>
(
subReader
.
get
(
"float32List"
),
{
0.0
f
,
1234567.0
f
,
1e37
f
,
-
1e37
f
,
1e-37
f
,
-
1e-37
f
});
checkList
<
double
>
(
subReader
.
get
(
"float64List"
),
{
0.0
,
123456789012345.0
,
1e306
,
-
1e306
,
1e-306
,
-
1e-306
});
checkList
<
Text
>
(
subReader
.
get
(
"textList"
),
{
"quux"
,
"corge"
,
"grault"
});
checkList
<
Data
>
(
subReader
.
get
(
"dataList"
),
{
"garply"
,
"waldo"
,
"fred"
});
{
auto
listReader
=
subReader
.
get
(
"structList"
).
as
<
DynamicList
>
();
ASSERT_EQ
(
3u
,
listReader
.
size
());
EXPECT_EQ
(
"x structlist 1"
,
listReader
[
0
].
as
<
DynamicStruct
>
().
get
(
"textField"
).
as
<
Text
>
());
EXPECT_EQ
(
"x structlist 2"
,
listReader
[
1
].
as
<
DynamicStruct
>
().
get
(
"textField"
).
as
<
Text
>
());
EXPECT_EQ
(
"x structlist 3"
,
listReader
[
2
].
as
<
DynamicStruct
>
().
get
(
"textField"
).
as
<
Text
>
());
}
checkList
<
TestEnum
>
(
subReader
.
get
(
"enumList"
),
{
TestEnum
::
QUX
,
TestEnum
::
BAR
,
TestEnum
::
GRAULT
});
}
EXPECT_EQ
(
TestEnum
::
CORGE
,
reader
.
get
(
"enumField"
).
as
<
TestEnum
>
());
EXPECT_EQ
(
6u
,
reader
.
get
(
"voidList"
).
as
<
DynamicList
>
().
size
());
checkList
<
bool
>
(
reader
.
get
(
"boolList"
),
{
true
,
false
,
false
,
true
});
checkList
<
int8_t
>
(
reader
.
get
(
"int8List"
),
{
111
,
-
111
});
checkList
<
int16_t
>
(
reader
.
get
(
"int16List"
),
{
11111
,
-
11111
});
checkList
<
int32_t
>
(
reader
.
get
(
"int32List"
),
{
111111111
,
-
111111111
});
checkList
<
int64_t
>
(
reader
.
get
(
"int64List"
),
{
1111111111111111111ll
,
-
1111111111111111111ll
});
checkList
<
uint8_t
>
(
reader
.
get
(
"uInt8List"
),
{
111u
,
222u
});
checkList
<
uint16_t
>
(
reader
.
get
(
"uInt16List"
),
{
33333u
,
44444u
});
checkList
<
uint32_t
>
(
reader
.
get
(
"uInt32List"
),
{
3333333333u
});
checkList
<
uint64_t
>
(
reader
.
get
(
"uInt64List"
),
{
11111111111111111111ull
});
{
auto
listReader
=
reader
.
get
(
"float32List"
).
as
<
DynamicList
>
();
ASSERT_EQ
(
4u
,
listReader
.
size
());
EXPECT_EQ
(
5555.5
f
,
listReader
[
0
].
as
<
float
>
());
EXPECT_EQ
(
std
::
numeric_limits
<
float
>::
infinity
(),
listReader
[
1
].
as
<
float
>
());
EXPECT_EQ
(
-
std
::
numeric_limits
<
float
>::
infinity
(),
listReader
[
2
].
as
<
float
>
());
EXPECT_TRUE
(
isNaN
(
listReader
[
3
].
as
<
float
>
()));
}
{
auto
listReader
=
reader
.
get
(
"float64List"
).
as
<
DynamicList
>
();
ASSERT_EQ
(
4u
,
listReader
.
size
());
EXPECT_EQ
(
7777.75
,
listReader
[
0
].
as
<
double
>
());
EXPECT_EQ
(
std
::
numeric_limits
<
double
>::
infinity
(),
listReader
[
1
].
as
<
double
>
());
EXPECT_EQ
(
-
std
::
numeric_limits
<
double
>::
infinity
(),
listReader
[
2
].
as
<
double
>
());
EXPECT_TRUE
(
isNaN
(
listReader
[
3
].
as
<
double
>
()));
}
checkList
<
Text
>
(
reader
.
get
(
"textList"
),
{
"plugh"
,
"xyzzy"
,
"thud"
});
checkList
<
Data
>
(
reader
.
get
(
"dataList"
),
{
"oops"
,
"exhausted"
,
"rfc3092"
});
{
auto
listReader
=
reader
.
get
(
"structList"
).
as
<
DynamicList
>
();
ASSERT_EQ
(
3u
,
listReader
.
size
());
EXPECT_EQ
(
"structlist 1"
,
listReader
[
0
].
as
<
DynamicStruct
>
().
get
(
"textField"
).
as
<
Text
>
());
EXPECT_EQ
(
"structlist 2"
,
listReader
[
1
].
as
<
DynamicStruct
>
().
get
(
"textField"
).
as
<
Text
>
());
EXPECT_EQ
(
"structlist 3"
,
listReader
[
2
].
as
<
DynamicStruct
>
().
get
(
"textField"
).
as
<
Text
>
());
}
checkList
<
TestEnum
>
(
reader
.
get
(
"enumList"
),
{
TestEnum
::
FOO
,
TestEnum
::
GARPLY
});
}
template
<
typename
Reader
>
void
dynamicCheckTestMessageAllZero
(
Reader
reader
)
{
EXPECT_EQ
(
Void
::
VOID
,
reader
.
get
(
"voidField"
).
as
<
Void
>
());
EXPECT_EQ
(
false
,
reader
.
get
(
"boolField"
).
as
<
bool
>
());
EXPECT_EQ
(
0
,
reader
.
get
(
"int8Field"
).
as
<
int8_t
>
());
EXPECT_EQ
(
0
,
reader
.
get
(
"int16Field"
).
as
<
int16_t
>
());
EXPECT_EQ
(
0
,
reader
.
get
(
"int32Field"
).
as
<
int32_t
>
());
EXPECT_EQ
(
0
,
reader
.
get
(
"int64Field"
).
as
<
int64_t
>
());
EXPECT_EQ
(
0u
,
reader
.
get
(
"uInt8Field"
).
as
<
uint8_t
>
());
EXPECT_EQ
(
0u
,
reader
.
get
(
"uInt16Field"
).
as
<
uint16_t
>
());
EXPECT_EQ
(
0u
,
reader
.
get
(
"uInt32Field"
).
as
<
uint32_t
>
());
EXPECT_EQ
(
0u
,
reader
.
get
(
"uInt64Field"
).
as
<
uint64_t
>
());
EXPECT_FLOAT_EQ
(
0
,
reader
.
get
(
"float32Field"
).
as
<
float
>
());
EXPECT_DOUBLE_EQ
(
0
,
reader
.
get
(
"float64Field"
).
as
<
double
>
());
EXPECT_EQ
(
""
,
reader
.
get
(
"textField"
).
as
<
Text
>
());
EXPECT_EQ
(
""
,
reader
.
get
(
"dataField"
).
as
<
Data
>
());
{
auto
subReader
=
reader
.
get
(
"structField"
).
as
<
DynamicStruct
>
();
EXPECT_EQ
(
Void
::
VOID
,
subReader
.
get
(
"voidField"
).
as
<
Void
>
());
EXPECT_EQ
(
false
,
subReader
.
get
(
"boolField"
).
as
<
bool
>
());
EXPECT_EQ
(
0
,
subReader
.
get
(
"int8Field"
).
as
<
int8_t
>
());
EXPECT_EQ
(
0
,
subReader
.
get
(
"int16Field"
).
as
<
int16_t
>
());
EXPECT_EQ
(
0
,
subReader
.
get
(
"int32Field"
).
as
<
int32_t
>
());
EXPECT_EQ
(
0
,
subReader
.
get
(
"int64Field"
).
as
<
int64_t
>
());
EXPECT_EQ
(
0u
,
subReader
.
get
(
"uInt8Field"
).
as
<
uint8_t
>
());
EXPECT_EQ
(
0u
,
subReader
.
get
(
"uInt16Field"
).
as
<
uint16_t
>
());
EXPECT_EQ
(
0u
,
subReader
.
get
(
"uInt32Field"
).
as
<
uint32_t
>
());
EXPECT_EQ
(
0u
,
subReader
.
get
(
"uInt64Field"
).
as
<
uint64_t
>
());
EXPECT_FLOAT_EQ
(
0
,
subReader
.
get
(
"float32Field"
).
as
<
float
>
());
EXPECT_DOUBLE_EQ
(
0
,
subReader
.
get
(
"float64Field"
).
as
<
double
>
());
EXPECT_EQ
(
""
,
subReader
.
get
(
"textField"
).
as
<
Text
>
());
EXPECT_EQ
(
""
,
subReader
.
get
(
"dataField"
).
as
<
Data
>
());
{
auto
subSubReader
=
subReader
.
get
(
"structField"
).
as
<
DynamicStruct
>
();
EXPECT_EQ
(
""
,
subSubReader
.
get
(
"textField"
).
as
<
Text
>
());
EXPECT_EQ
(
""
,
subSubReader
.
get
(
"structField"
).
as
<
DynamicStruct
>
()
.
get
(
"textField"
).
as
<
Text
>
());
}
EXPECT_EQ
(
0u
,
subReader
.
get
(
"voidList"
).
as
<
DynamicList
>
().
size
());
EXPECT_EQ
(
0u
,
subReader
.
get
(
"boolList"
).
as
<
DynamicList
>
().
size
());
EXPECT_EQ
(
0u
,
subReader
.
get
(
"int8List"
).
as
<
DynamicList
>
().
size
());
EXPECT_EQ
(
0u
,
subReader
.
get
(
"int16List"
).
as
<
DynamicList
>
().
size
());
EXPECT_EQ
(
0u
,
subReader
.
get
(
"int32List"
).
as
<
DynamicList
>
().
size
());
EXPECT_EQ
(
0u
,
subReader
.
get
(
"int64List"
).
as
<
DynamicList
>
().
size
());
EXPECT_EQ
(
0u
,
subReader
.
get
(
"uInt8List"
).
as
<
DynamicList
>
().
size
());
EXPECT_EQ
(
0u
,
subReader
.
get
(
"uInt16List"
).
as
<
DynamicList
>
().
size
());
EXPECT_EQ
(
0u
,
subReader
.
get
(
"uInt32List"
).
as
<
DynamicList
>
().
size
());
EXPECT_EQ
(
0u
,
subReader
.
get
(
"uInt64List"
).
as
<
DynamicList
>
().
size
());
EXPECT_EQ
(
0u
,
subReader
.
get
(
"float32List"
).
as
<
DynamicList
>
().
size
());
EXPECT_EQ
(
0u
,
subReader
.
get
(
"float64List"
).
as
<
DynamicList
>
().
size
());
EXPECT_EQ
(
0u
,
subReader
.
get
(
"textList"
).
as
<
DynamicList
>
().
size
());
EXPECT_EQ
(
0u
,
subReader
.
get
(
"dataList"
).
as
<
DynamicList
>
().
size
());
EXPECT_EQ
(
0u
,
subReader
.
get
(
"structList"
).
as
<
DynamicList
>
().
size
());
}
EXPECT_EQ
(
0u
,
reader
.
get
(
"voidList"
).
as
<
DynamicList
>
().
size
());
EXPECT_EQ
(
0u
,
reader
.
get
(
"boolList"
).
as
<
DynamicList
>
().
size
());
EXPECT_EQ
(
0u
,
reader
.
get
(
"int8List"
).
as
<
DynamicList
>
().
size
());
EXPECT_EQ
(
0u
,
reader
.
get
(
"int16List"
).
as
<
DynamicList
>
().
size
());
EXPECT_EQ
(
0u
,
reader
.
get
(
"int32List"
).
as
<
DynamicList
>
().
size
());
EXPECT_EQ
(
0u
,
reader
.
get
(
"int64List"
).
as
<
DynamicList
>
().
size
());
EXPECT_EQ
(
0u
,
reader
.
get
(
"uInt8List"
).
as
<
DynamicList
>
().
size
());
EXPECT_EQ
(
0u
,
reader
.
get
(
"uInt16List"
).
as
<
DynamicList
>
().
size
());
EXPECT_EQ
(
0u
,
reader
.
get
(
"uInt32List"
).
as
<
DynamicList
>
().
size
());
EXPECT_EQ
(
0u
,
reader
.
get
(
"uInt64List"
).
as
<
DynamicList
>
().
size
());
EXPECT_EQ
(
0u
,
reader
.
get
(
"float32List"
).
as
<
DynamicList
>
().
size
());
EXPECT_EQ
(
0u
,
reader
.
get
(
"float64List"
).
as
<
DynamicList
>
().
size
());
EXPECT_EQ
(
0u
,
reader
.
get
(
"textList"
).
as
<
DynamicList
>
().
size
());
EXPECT_EQ
(
0u
,
reader
.
get
(
"dataList"
).
as
<
DynamicList
>
().
size
());
EXPECT_EQ
(
0u
,
reader
.
get
(
"structList"
).
as
<
DynamicList
>
().
size
());
}
#undef as
void
dynamicInitListDefaults
(
DynamicStruct
::
Builder
builder
)
{
auto
lists
=
builder
.
init
(
"lists"
).
as
<
DynamicStruct
>
();
lists
.
init
(
"list0"
,
2
);
lists
.
init
(
"list1"
,
4
);
lists
.
init
(
"list8"
,
2
);
lists
.
init
(
"list16"
,
2
);
lists
.
init
(
"list32"
,
2
);
lists
.
init
(
"list64"
,
2
);
lists
.
init
(
"listP"
,
2
);
lists
.
get
(
"list0"
).
as
<
DynamicList
>
()[
0
].
as
<
DynamicStruct
>
().
set
(
"f"
,
Void
::
VOID
);
lists
.
get
(
"list0"
).
as
<
DynamicList
>
()[
1
].
as
<
DynamicStruct
>
().
set
(
"f"
,
Void
::
VOID
);
lists
.
get
(
"list1"
).
as
<
DynamicList
>
()[
0
].
as
<
DynamicStruct
>
().
set
(
"f"
,
true
);
lists
.
get
(
"list1"
).
as
<
DynamicList
>
()[
1
].
as
<
DynamicStruct
>
().
set
(
"f"
,
false
);
lists
.
get
(
"list1"
).
as
<
DynamicList
>
()[
2
].
as
<
DynamicStruct
>
().
set
(
"f"
,
true
);
lists
.
get
(
"list1"
).
as
<
DynamicList
>
()[
3
].
as
<
DynamicStruct
>
().
set
(
"f"
,
true
);
lists
.
get
(
"list8"
).
as
<
DynamicList
>
()[
0
].
as
<
DynamicStruct
>
().
set
(
"f"
,
123u
);
lists
.
get
(
"list8"
).
as
<
DynamicList
>
()[
1
].
as
<
DynamicStruct
>
().
set
(
"f"
,
45u
);
lists
.
get
(
"list16"
).
as
<
DynamicList
>
()[
0
].
as
<
DynamicStruct
>
().
set
(
"f"
,
12345u
);
lists
.
get
(
"list16"
).
as
<
DynamicList
>
()[
1
].
as
<
DynamicStruct
>
().
set
(
"f"
,
6789u
);
lists
.
get
(
"list32"
).
as
<
DynamicList
>
()[
0
].
as
<
DynamicStruct
>
().
set
(
"f"
,
123456789u
);
lists
.
get
(
"list32"
).
as
<
DynamicList
>
()[
1
].
as
<
DynamicStruct
>
().
set
(
"f"
,
234567890u
);
lists
.
get
(
"list64"
).
as
<
DynamicList
>
()[
0
].
as
<
DynamicStruct
>
().
set
(
"f"
,
1234567890123456u
);
lists
.
get
(
"list64"
).
as
<
DynamicList
>
()[
1
].
as
<
DynamicStruct
>
().
set
(
"f"
,
2345678901234567u
);
lists
.
get
(
"listP"
).
as
<
DynamicList
>
()[
0
].
as
<
DynamicStruct
>
().
set
(
"f"
,
"foo"
);
lists
.
get
(
"listP"
).
as
<
DynamicList
>
()[
1
].
as
<
DynamicStruct
>
().
set
(
"f"
,
"bar"
);
{
auto
l
=
lists
.
init
(
"int32ListList"
,
3
).
as
<
DynamicList
>
();
l
.
init
(
0
,
3
).
as
<
DynamicList
>
().
copyFrom
({
1
,
2
,
3
});
l
.
init
(
1
,
2
).
as
<
DynamicList
>
().
copyFrom
({
4
,
5
});
l
.
init
(
2
,
1
).
as
<
DynamicList
>
().
copyFrom
({
12341234
});
}
{
auto
l
=
lists
.
init
(
"textListList"
,
3
).
as
<
DynamicList
>
();
l
.
init
(
0
,
2
).
as
<
DynamicList
>
().
copyFrom
({
"foo"
,
"bar"
});
l
.
init
(
1
,
1
).
as
<
DynamicList
>
().
copyFrom
({
"baz"
});
l
.
init
(
2
,
2
).
as
<
DynamicList
>
().
copyFrom
({
"qux"
,
"corge"
});
}
{
auto
l
=
lists
.
init
(
"structListList"
,
2
).
as
<
DynamicList
>
();
auto
e
=
l
.
init
(
0
,
2
).
as
<
DynamicList
>
();
e
[
0
].
as
<
TestAllTypes
>
().
setInt32Field
(
123
);
e
[
1
].
as
<
TestAllTypes
>
().
setInt32Field
(
456
);
e
=
l
.
init
(
1
,
1
).
as
<
DynamicList
>
();
e
[
0
].
as
<
TestAllTypes
>
().
setInt32Field
(
789
);
}
}
// Hack because as<>() is a template-parameter-dependent lookup everywhere below...
#define as template as
template
<
typename
Reader
>
void
dynamicCheckListDefaults
(
Reader
reader
)
{
auto
lists
=
reader
.
get
(
"lists"
).
as
<
DynamicStruct
>
();
ASSERT_EQ
(
2u
,
lists
.
get
(
"list0"
).
as
<
DynamicList
>
().
size
());
ASSERT_EQ
(
4u
,
lists
.
get
(
"list1"
).
as
<
DynamicList
>
().
size
());
ASSERT_EQ
(
2u
,
lists
.
get
(
"list8"
).
as
<
DynamicList
>
().
size
());
ASSERT_EQ
(
2u
,
lists
.
get
(
"list16"
).
as
<
DynamicList
>
().
size
());
ASSERT_EQ
(
2u
,
lists
.
get
(
"list32"
).
as
<
DynamicList
>
().
size
());
ASSERT_EQ
(
2u
,
lists
.
get
(
"list64"
).
as
<
DynamicList
>
().
size
());
ASSERT_EQ
(
2u
,
lists
.
get
(
"listP"
).
as
<
DynamicList
>
().
size
());
EXPECT_EQ
(
Void
::
VOID
,
lists
.
get
(
"list0"
).
as
<
DynamicList
>
()[
0
].
as
<
DynamicStruct
>
().
get
(
"f"
).
as
<
Void
>
());
EXPECT_EQ
(
Void
::
VOID
,
lists
.
get
(
"list0"
).
as
<
DynamicList
>
()[
1
].
as
<
DynamicStruct
>
().
get
(
"f"
).
as
<
Void
>
());
EXPECT_TRUE
(
lists
.
get
(
"list1"
).
as
<
DynamicList
>
()[
0
].
as
<
DynamicStruct
>
().
get
(
"f"
).
as
<
bool
>
());
EXPECT_FALSE
(
lists
.
get
(
"list1"
).
as
<
DynamicList
>
()[
1
].
as
<
DynamicStruct
>
().
get
(
"f"
).
as
<
bool
>
());
EXPECT_TRUE
(
lists
.
get
(
"list1"
).
as
<
DynamicList
>
()[
2
].
as
<
DynamicStruct
>
().
get
(
"f"
).
as
<
bool
>
());
EXPECT_TRUE
(
lists
.
get
(
"list1"
).
as
<
DynamicList
>
()[
3
].
as
<
DynamicStruct
>
().
get
(
"f"
).
as
<
bool
>
());
EXPECT_EQ
(
123u
,
lists
.
get
(
"list8"
).
as
<
DynamicList
>
()[
0
].
as
<
DynamicStruct
>
().
get
(
"f"
).
as
<
uint8_t
>
());
EXPECT_EQ
(
45u
,
lists
.
get
(
"list8"
).
as
<
DynamicList
>
()[
1
].
as
<
DynamicStruct
>
().
get
(
"f"
).
as
<
uint8_t
>
());
EXPECT_EQ
(
12345u
,
lists
.
get
(
"list16"
).
as
<
DynamicList
>
()[
0
].
as
<
DynamicStruct
>
().
get
(
"f"
).
as
<
uint16_t
>
());
EXPECT_EQ
(
6789u
,
lists
.
get
(
"list16"
).
as
<
DynamicList
>
()[
1
].
as
<
DynamicStruct
>
().
get
(
"f"
).
as
<
uint16_t
>
());
EXPECT_EQ
(
123456789u
,
lists
.
get
(
"list32"
).
as
<
DynamicList
>
()[
0
].
as
<
DynamicStruct
>
().
get
(
"f"
).
as
<
uint32_t
>
());
EXPECT_EQ
(
234567890u
,
lists
.
get
(
"list32"
).
as
<
DynamicList
>
()[
1
].
as
<
DynamicStruct
>
().
get
(
"f"
).
as
<
uint32_t
>
());
EXPECT_EQ
(
1234567890123456u
,
lists
.
get
(
"list64"
).
as
<
DynamicList
>
()[
0
].
as
<
DynamicStruct
>
().
get
(
"f"
).
as
<
uint64_t
>
());
EXPECT_EQ
(
2345678901234567u
,
lists
.
get
(
"list64"
).
as
<
DynamicList
>
()[
1
].
as
<
DynamicStruct
>
().
get
(
"f"
).
as
<
uint64_t
>
());
EXPECT_EQ
(
"foo"
,
lists
.
get
(
"listP"
).
as
<
DynamicList
>
()[
0
].
as
<
DynamicStruct
>
().
get
(
"f"
).
as
<
Text
>
());
EXPECT_EQ
(
"bar"
,
lists
.
get
(
"listP"
).
as
<
DynamicList
>
()[
1
].
as
<
DynamicStruct
>
().
get
(
"f"
).
as
<
Text
>
());
{
auto
l
=
lists
.
get
(
"int32ListList"
).
as
<
DynamicList
>
();
ASSERT_EQ
(
3u
,
l
.
size
());
checkList
<
int32_t
>
(
l
[
0
],
{
1
,
2
,
3
});
checkList
<
int32_t
>
(
l
[
1
],
{
4
,
5
});
checkList
<
int32_t
>
(
l
[
2
],
{
12341234
});
}
{
auto
l
=
lists
.
get
(
"textListList"
).
as
<
DynamicList
>
();
ASSERT_EQ
(
3u
,
l
.
size
());
checkList
<
Text
>
(
l
[
0
],
{
"foo"
,
"bar"
});
checkList
<
Text
>
(
l
[
1
],
{
"baz"
});
checkList
<
Text
>
(
l
[
2
],
{
"qux"
,
"corge"
});
}
{
auto
l
=
lists
.
get
(
"structListList"
).
as
<
DynamicList
>
();
ASSERT_EQ
(
2u
,
l
.
size
());
auto
e
=
l
[
0
].
as
<
DynamicList
>
();
ASSERT_EQ
(
2u
,
e
.
size
());
EXPECT_EQ
(
123
,
e
[
0
].
as
<
TestAllTypes
>
().
getInt32Field
());
EXPECT_EQ
(
456
,
e
[
1
].
as
<
TestAllTypes
>
().
getInt32Field
());
e
=
l
[
1
].
as
<
DynamicList
>
();
ASSERT_EQ
(
1u
,
e
.
size
());
EXPECT_EQ
(
789
,
e
[
0
].
as
<
TestAllTypes
>
().
getInt32Field
());
EXPECT_EQ
(
expected
.
begin
()[
i
],
typed
[
i
]);
}
}
#undef as
TEST
(
DynamicApi
,
Build
)
{
MallocMessageBuilder
builder
;
auto
root
=
builder
.
initRoot
<
DynamicStruct
>
(
Schema
::
from
<
TestAllTypes
>
());
dynamicInit
TestMessage
(
root
);
initDynamic
TestMessage
(
root
);
checkTestMessage
(
root
.
asReader
().
as
<
TestAllTypes
>
());
dynamicCheck
TestMessage
(
root
.
asReader
());
dynamicCheck
TestMessage
(
root
);
checkDynamic
TestMessage
(
root
.
asReader
());
checkDynamic
TestMessage
(
root
);
}
TEST
(
DynamicApi
,
Read
)
{
...
...
@@ -485,9 +87,9 @@ TEST(DynamicApi, Read) {
initTestMessage
(
root
);
dynamicCheck
TestMessage
(
toDynamic
(
root
.
asReader
()));
dynamicCheck
TestMessage
(
toDynamic
(
root
).
asReader
());
dynamicCheck
TestMessage
(
toDynamic
(
root
));
checkDynamic
TestMessage
(
toDynamic
(
root
.
asReader
()));
checkDynamic
TestMessage
(
toDynamic
(
root
).
asReader
());
checkDynamic
TestMessage
(
toDynamic
(
root
));
}
TEST
(
DynamicApi
,
Defaults
)
{
...
...
@@ -495,7 +97,7 @@ TEST(DynamicApi, Defaults) {
ArrayPtr
<
const
word
>
segments
[
1
]
=
{
arrayPtr
(
nullRoot
.
words
,
1
)};
SegmentArrayMessageReader
reader
(
arrayPtr
(
segments
,
1
));
auto
root
=
reader
.
getRoot
<
DynamicStruct
>
(
Schema
::
from
<
TestDefaults
>
());
dynamicCheck
TestMessage
(
root
);
checkDynamic
TestMessage
(
root
);
}
TEST
(
DynamicApi
,
DefaultsBuilder
)
{
...
...
@@ -503,24 +105,24 @@ TEST(DynamicApi, DefaultsBuilder) {
auto
root
=
builder
.
initRoot
<
DynamicStruct
>
(
Schema
::
from
<
TestDefaults
>
());
checkTestMessage
(
root
.
asReader
().
as
<
TestDefaults
>
());
dynamicCheck
TestMessage
(
root
.
asReader
());
checkDynamic
TestMessage
(
root
.
asReader
());
// This will initialize the whole message, replacing null pointers with copies of defaults.
dynamicCheck
TestMessage
(
root
);
checkDynamic
TestMessage
(
root
);
// Check again now that the message is initialized.
checkTestMessage
(
root
.
asReader
().
as
<
TestDefaults
>
());
dynamicCheck
TestMessage
(
root
.
asReader
());
dynamicCheck
TestMessage
(
root
);
checkDynamic
TestMessage
(
root
.
asReader
());
checkDynamic
TestMessage
(
root
);
}
TEST
(
DynamicApi
,
Zero
)
{
MallocMessageBuilder
builder
;
auto
root
=
builder
.
initRoot
<
DynamicStruct
>
(
Schema
::
from
<
TestAllTypes
>
());
dynamicCheck
TestMessageAllZero
(
root
.
asReader
());
checkDynamic
TestMessageAllZero
(
root
.
asReader
());
checkTestMessageAllZero
(
root
.
asReader
().
as
<
TestAllTypes
>
());
dynamicCheck
TestMessageAllZero
(
root
);
checkDynamic
TestMessageAllZero
(
root
);
checkTestMessageAllZero
(
root
.
asReader
().
as
<
TestAllTypes
>
());
}
...
...
@@ -528,11 +130,11 @@ TEST(DynamicApi, ListListsBuild) {
MallocMessageBuilder
builder
;
auto
root
=
builder
.
initRoot
<
DynamicStruct
>
(
Schema
::
from
<
TestListDefaults
>
());
dynamicInitListDefaul
ts
(
root
);
initDynamicTestLis
ts
(
root
);
checkTestMessage
(
root
.
asReader
().
as
<
TestListDefaults
>
());
dynamicCheckListDefaul
ts
(
root
.
asReader
());
dynamicCheckListDefaul
ts
(
root
);
checkDynamicTestLis
ts
(
root
.
asReader
());
checkDynamicTestLis
ts
(
root
);
}
TEST
(
DynamicApi
,
ListListsRead
)
{
...
...
@@ -541,21 +143,21 @@ TEST(DynamicApi, ListListsRead) {
initTestMessage
(
root
);
dynamicCheckListDefaul
ts
(
toDynamic
(
root
.
asReader
()));
dynamicCheckListDefaul
ts
(
toDynamic
(
root
).
asReader
());
dynamicCheckListDefaul
ts
(
toDynamic
(
root
));
checkDynamicTestLis
ts
(
toDynamic
(
root
.
asReader
()));
checkDynamicTestLis
ts
(
toDynamic
(
root
).
asReader
());
checkDynamicTestLis
ts
(
toDynamic
(
root
));
}
TEST
(
DynamicApi
,
GenericObjects
)
{
MallocMessageBuilder
builder
;
auto
root
=
builder
.
getRoot
<
test
::
TestObject
>
();
dynamicInit
TestMessage
(
root
.
initObjectField
<
DynamicStruct
>
(
Schema
::
from
<
TestAllTypes
>
()));
initDynamic
TestMessage
(
root
.
initObjectField
<
DynamicStruct
>
(
Schema
::
from
<
TestAllTypes
>
()));
checkTestMessage
(
root
.
asReader
().
getObjectField
<
TestAllTypes
>
());
dynamicCheck
TestMessage
(
checkDynamic
TestMessage
(
root
.
asReader
().
getObjectField
<
DynamicStruct
>
(
Schema
::
from
<
TestAllTypes
>
()));
dynamicCheck
TestMessage
(
root
.
getObjectField
<
DynamicStruct
>
(
Schema
::
from
<
TestAllTypes
>
()));
checkDynamic
TestMessage
(
root
.
getObjectField
<
DynamicStruct
>
(
Schema
::
from
<
TestAllTypes
>
()));
{
{
...
...
@@ -586,14 +188,14 @@ TEST(DynamicApi, DynamicGenericObjects) {
MallocMessageBuilder
builder
;
auto
root
=
builder
.
getRoot
<
DynamicStruct
>
(
Schema
::
from
<
test
::
TestObject
>
());
dynamicInit
TestMessage
(
root
.
initObject
(
"objectField"
,
Schema
::
from
<
TestAllTypes
>
()));
initDynamic
TestMessage
(
root
.
initObject
(
"objectField"
,
Schema
::
from
<
TestAllTypes
>
()));
checkTestMessage
(
root
.
asReader
().
as
<
test
::
TestObject
>
().
getObjectField
<
TestAllTypes
>
());
dynamicCheck
TestMessage
(
checkDynamic
TestMessage
(
root
.
asReader
().
get
(
"objectField"
).
as
<
DynamicObject
>
().
as
(
Schema
::
from
<
TestAllTypes
>
()));
dynamicCheck
TestMessage
(
checkDynamic
TestMessage
(
root
.
get
(
"objectField"
).
as
<
DynamicObject
>
().
as
(
Schema
::
from
<
TestAllTypes
>
()));
dynamicCheck
TestMessage
(
checkDynamic
TestMessage
(
root
.
getObject
(
"objectField"
,
Schema
::
from
<
TestAllTypes
>
()));
{
...
...
@@ -743,7 +345,7 @@ TEST(DynamicApi, LateUnion) {
TEST
(
DynamicApi
,
Has
)
{
MallocMessageBuilder
builder
;
auto
root
=
builder
.
initRoot
<
DynamicStruct
>
(
Schema
::
from
<
test
::
TestDefaults
>
());
auto
root
=
builder
.
initRoot
<
DynamicStruct
>
(
Schema
::
from
<
TestDefaults
>
());
EXPECT_FALSE
(
root
.
has
(
"int32Field"
));
root
.
set
(
"int32Field"
,
123
);
...
...
@@ -756,6 +358,28 @@ TEST(DynamicApi, Has) {
EXPECT_TRUE
(
root
.
has
(
"structField"
));
}
TEST
(
DynamicApi
,
HasWhenEmpty
)
{
AlignedData
<
1
>
nullRoot
=
{{
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
}};
ArrayPtr
<
const
word
>
segments
[
1
]
=
{
arrayPtr
(
nullRoot
.
words
,
1
)};
SegmentArrayMessageReader
reader
(
arrayPtr
(
segments
,
1
));
auto
root
=
reader
.
getRoot
<
DynamicStruct
>
(
Schema
::
from
<
TestDefaults
>
());
EXPECT_FALSE
(
root
.
has
(
"voidField"
));
EXPECT_FALSE
(
root
.
has
(
"int32Field"
));
EXPECT_FALSE
(
root
.
has
(
"structField"
));
EXPECT_FALSE
(
root
.
has
(
"int32List"
));
}
TEST
(
DynamicApi
,
SetEnumFromNative
)
{
MallocMessageBuilder
builder
;
auto
root
=
builder
.
initRoot
<
DynamicStruct
>
(
Schema
::
from
<
TestAllTypes
>
());
root
.
set
(
"enumField"
,
TestEnum
::
BAZ
);
root
.
set
(
"enumList"
,
{
TestEnum
::
BAR
,
TestEnum
::
FOO
});
EXPECT_EQ
(
TestEnum
::
BAZ
,
root
.
get
(
"enumField"
).
as
<
TestEnum
>
());
checkList
<
TestEnum
>
(
root
.
get
(
"enumList"
),
{
TestEnum
::
BAR
,
TestEnum
::
FOO
});
}
}
// namespace
}
// namespace internal
}
// namespace capnproto
c++/src/capnproto/dynamic.c++
View file @
d7f3123c
...
...
@@ -282,13 +282,6 @@ void DynamicUnion::Builder::setObjectDiscriminant(StructSchema::Member member) {
// =======================================================================================
void
DynamicStruct
::
Reader
::
verifySchema
(
StructSchema
expected
)
{
PRECOND
(
schema
==
expected
,
"Type mismatch when using DynamicStruct::Reader::as()."
);
}
void
DynamicStruct
::
Builder
::
verifySchema
(
StructSchema
expected
)
{
PRECOND
(
schema
==
expected
,
"Type mismatch when using DynamicStruct::Builder::as()."
);
}
DynamicValue
::
Reader
DynamicStruct
::
Reader
::
get
(
StructSchema
::
Member
member
)
{
PRECOND
(
member
.
getContainingStruct
()
==
schema
,
"`member` is not a member of this struct."
);
return
getImpl
(
reader
,
member
);
...
...
@@ -1320,15 +1313,6 @@ DynamicList::Reader DynamicList::Builder::asReader() {
return
DynamicList
::
Reader
(
schema
,
builder
.
asReader
());
}
void
DynamicList
::
Reader
::
verifySchema
(
ListSchema
expectedSchema
)
{
PRECOND
(
schema
==
expectedSchema
,
"Type mismatch when using DynamicList::Reader::as()."
);
}
void
DynamicList
::
Builder
::
verifySchema
(
ListSchema
expectedSchema
)
{
PRECOND
(
schema
==
expectedSchema
,
"Type mismatch when using DynamicList::Reader::as()."
);
}
// =======================================================================================
namespace
{
...
...
c++/src/capnproto/dynamic.h
View file @
d7f3123c
...
...
@@ -275,8 +275,6 @@ private:
inline
Reader
(
StructSchema
schema
,
internal
::
StructReader
reader
)
:
schema
(
schema
),
reader
(
reader
)
{}
void
verifySchema
(
StructSchema
expected
);
static
DynamicValue
::
Reader
getImpl
(
internal
::
StructReader
reader
,
StructSchema
::
Member
member
);
template
<
typename
T
>
...
...
@@ -357,8 +355,6 @@ private:
inline
Builder
(
StructSchema
schema
,
internal
::
StructBuilder
builder
)
:
schema
(
schema
),
builder
(
builder
)
{}
void
verifySchema
(
StructSchema
expected
);
static
DynamicValue
::
Builder
getImpl
(
internal
::
StructBuilder
builder
,
StructSchema
::
Member
member
);
static
DynamicStruct
::
Builder
getObjectImpl
(
...
...
@@ -425,8 +421,6 @@ private:
Reader
(
ListSchema
schema
,
internal
::
ListReader
reader
)
:
schema
(
schema
),
reader
(
reader
)
{}
void
verifySchema
(
ListSchema
expectedSchema
);
template
<
typename
T
>
friend
struct
internal
::
PointerHelpers
;
friend
struct
DynamicStruct
;
...
...
@@ -469,8 +463,6 @@ private:
Builder
(
ListSchema
schema
,
internal
::
ListBuilder
builder
)
:
schema
(
schema
),
builder
(
builder
)
{}
void
verifySchema
(
ListSchema
expectedSchema
);
template
<
typename
T
>
friend
struct
internal
::
PointerHelpers
;
friend
struct
DynamicStruct
;
...
...
@@ -880,14 +872,14 @@ template <typename T>
typename
T
::
Reader
DynamicStruct
::
Reader
::
as
()
{
static_assert
(
kind
<
T
>
()
==
Kind
::
STRUCT
,
"DynamicStruct::Reader::as<T>() can only convert to struct types."
);
verifySchema
(
Schema
::
from
<
T
>
()
);
schema
.
requireUsableAs
<
T
>
(
);
return
typename
T
::
Reader
(
reader
);
}
template
<
typename
T
>
typename
T
::
Builder
DynamicStruct
::
Builder
::
as
()
{
static_assert
(
kind
<
T
>
()
==
Kind
::
STRUCT
,
"DynamicStruct::Builder::as<T>() can only convert to struct types."
);
verifySchema
(
Schema
::
from
<
T
>
()
);
schema
.
requireUsableAs
<
T
>
(
);
return
typename
T
::
Builder
(
builder
);
}
...
...
@@ -910,14 +902,14 @@ template <typename T>
typename
T
::
Reader
DynamicList
::
Reader
::
as
()
{
static_assert
(
kind
<
T
>
()
==
Kind
::
LIST
,
"DynamicStruct::Reader::as<T>() can only convert to list types."
);
verifySchema
(
Schema
::
from
<
T
>
()
);
schema
.
requireUsableAs
<
T
>
(
);
return
typename
T
::
Reader
(
reader
);
}
template
<
typename
T
>
typename
T
::
Builder
DynamicList
::
Builder
::
as
()
{
static_assert
(
kind
<
T
>
()
==
Kind
::
LIST
,
"DynamicStruct::Builder::as<T>() can only convert to list types."
);
verifySchema
(
Schema
::
from
<
T
>
()
);
schema
.
requireUsableAs
<
T
>
(
);
return
typename
T
::
Builder
(
builder
);
}
...
...
c++/src/capnproto/encoding-test.c++
View file @
d7f3123c
...
...
@@ -1307,6 +1307,34 @@ TEST(Encoding, ZeroOldObject) {
checkTestMessageAllZero
(
oldSub2
);
}
TEST
(
Encoding
,
Has
)
{
MallocMessageBuilder
builder
;
auto
root
=
builder
.
initRoot
<
TestAllTypes
>
();
EXPECT_FALSE
(
root
.
hasTextField
());
EXPECT_FALSE
(
root
.
hasDataField
());
EXPECT_FALSE
(
root
.
hasStructField
());
EXPECT_FALSE
(
root
.
hasInt32List
());
EXPECT_FALSE
(
root
.
asReader
().
hasTextField
());
EXPECT_FALSE
(
root
.
asReader
().
hasDataField
());
EXPECT_FALSE
(
root
.
asReader
().
hasStructField
());
EXPECT_FALSE
(
root
.
asReader
().
hasInt32List
());
initTestMessage
(
root
);
EXPECT_TRUE
(
root
.
hasTextField
());
EXPECT_TRUE
(
root
.
hasDataField
());
EXPECT_TRUE
(
root
.
hasStructField
());
EXPECT_TRUE
(
root
.
hasInt32List
());
EXPECT_TRUE
(
root
.
asReader
().
hasTextField
());
EXPECT_TRUE
(
root
.
asReader
().
hasDataField
());
EXPECT_TRUE
(
root
.
asReader
().
hasStructField
());
EXPECT_TRUE
(
root
.
asReader
().
hasInt32List
());
}
}
// namespace
}
// namespace internal
}
// namespace capnproto
c++/src/capnproto/exception.c++
View file @
d7f3123c
...
...
@@ -66,10 +66,21 @@ Exception::Exception(const Exception& other) noexcept
:
file
(
other
.
file
),
line
(
other
.
line
),
nature
(
other
.
nature
),
durability
(
other
.
durability
),
description
(
str
(
other
.
description
)),
traceCount
(
other
.
traceCount
)
{
memcpy
(
trace
,
other
.
trace
,
sizeof
(
trace
[
0
])
*
traceCount
);
if
(
other
.
context
!=
nullptr
)
{
context
=
heap
<
Context
>
(
**
other
.
context
);
}
}
Exception
::~
Exception
()
noexcept
{}
Exception
::
Context
::
Context
(
const
Context
&
other
)
noexcept
:
file
(
other
.
file
),
line
(
other
.
line
),
description
(
str
(
other
.
description
))
{
if
(
other
.
next
!=
nullptr
)
{
next
=
heap
<
Context
>
(
**
other
.
next
);
}
}
void
Exception
::
wrapContext
(
const
char
*
file
,
int
line
,
Array
<
char
>&&
description
)
{
context
=
heap
<
Context
>
(
file
,
line
,
move
(
description
),
move
(
context
));
}
...
...
c++/src/capnproto/exception.h
View file @
d7f3123c
...
...
@@ -65,6 +65,7 @@ public:
Exception
(
Nature
nature
,
Durability
durability
,
const
char
*
file
,
int
line
,
Array
<
char
>
description
=
nullptr
)
noexcept
;
Exception
(
const
Exception
&
other
)
noexcept
;
Exception
(
Exception
&&
other
)
=
default
;
~
Exception
()
noexcept
;
const
char
*
getFile
()
const
{
return
file
;
}
...
...
@@ -83,6 +84,7 @@ public:
Context
(
const
char
*
file
,
int
line
,
Array
<
char
>&&
description
,
Maybe
<
Own
<
Context
>>&&
next
)
:
file
(
file
),
line
(
line
),
description
(
move
(
description
)),
next
(
move
(
next
))
{}
Context
(
const
Context
&
other
)
noexcept
;
};
inline
Maybe
<
const
Context
&>
getContext
()
const
{
...
...
@@ -125,6 +127,7 @@ class ExceptionCallback {
public
:
ExceptionCallback
();
CAPNPROTO_DISALLOW_COPY
(
ExceptionCallback
);
virtual
~
ExceptionCallback
();
virtual
void
onRecoverableException
(
Exception
&&
exception
);
...
...
@@ -160,6 +163,7 @@ public:
public
:
ScopedRegistration
(
ExceptionCallback
&
callback
);
CAPNPROTO_DISALLOW_COPY
(
ScopedRegistration
);
~
ScopedRegistration
();
inline
ExceptionCallback
&
getCallback
()
{
return
callback
;
}
...
...
c++/src/capnproto/generated-header-support.h
View file @
d7f3123c
...
...
@@ -141,6 +141,9 @@ struct RawSchema {
struct
MemberInfo
{
uint16_t
unionIndex
;
// 0 = not in a union, >0 = parent union's index + 1
uint16_t
index
;
// index of the member
MemberInfo
()
=
default
;
inline
MemberInfo
(
uint16_t
unionIndex
,
uint16_t
index
)
:
unionIndex
(
unionIndex
),
index
(
index
)
{}
};
const
MemberInfo
*
membersByName
;
...
...
@@ -150,6 +153,11 @@ struct RawSchema {
uint32_t
dependencyCount
;
uint32_t
memberCount
;
// Sizes of above tables.
const
RawSchema
*
canCastTo
;
// Points to the RawSchema of a compiled-in type to which it is safe to cast any DynamicValue
// with this schema. This is null for all compiled-in types; it is only set by SchemaLoader on
// dynamically-loaded types.
};
template
<
typename
T
>
...
...
c++/src/capnproto/layout.c++
View file @
d7f3123c
...
...
@@ -1956,7 +1956,7 @@ const word* StructReader::getUncheckedPointer(WirePointerCount ptrIndex) const {
}
bool
StructReader
::
isPointerFieldNull
(
WirePointerCount
ptrIndex
)
const
{
return
(
pointers
+
ptrIndex
)
->
isNull
();
return
ptrIndex
>=
pointerCount
||
(
pointers
+
ptrIndex
)
->
isNull
();
}
WordCount64
StructReader
::
totalSize
()
const
{
...
...
c++/src/capnproto/logging.h
View file @
d7f3123c
...
...
@@ -156,6 +156,7 @@ public:
class
Context
:
public
ExceptionCallback
{
public
:
Context
();
CAPNPROTO_DISALLOW_COPY
(
Context
);
virtual
~
Context
();
virtual
void
addTo
(
Exception
&
exception
)
=
0
;
...
...
@@ -171,20 +172,16 @@ public:
template
<
typename
Func
>
class
ContextImpl
:
public
Context
{
public
:
inline
ContextImpl
(
Func
&&
func
)
:
func
(
capnproto
::
move
(
func
))
{}
inline
ContextImpl
(
Func
&
func
)
:
func
(
func
)
{}
CAPNPROTO_DISALLOW_COPY
(
ContextImpl
);
void
addTo
(
Exception
&
exception
)
override
{
func
(
exception
);
}
private
:
Func
func
;
Func
&
func
;
};
template
<
typename
Func
>
static
ContextImpl
<
RemoveReference
<
Func
>>
context
(
Func
&&
func
)
{
return
ContextImpl
<
RemoveReference
<
Func
>>
(
capnproto
::
forward
<
Func
>
(
func
));
}
template
<
typename
...
Params
>
static
void
addContextTo
(
Exception
&
exception
,
const
char
*
file
,
int
line
,
const
char
*
macroArgs
,
Params
&&
...
params
);
...
...
@@ -271,11 +268,11 @@ ArrayPtr<const char> operator*(const Stringifier&, Log::Severity severity);
} while (false)
#define CONTEXT(...) \
auto _capnpLoggingContext = ::capnproto::Log::context( \
[&](::capnproto::Exception& exception) { \
auto _capnpContextFunc = [&](::capnproto::Exception& exception) { \
return ::capnproto::Log::addContextTo(exception, \
__FILE__, __LINE__, #__VA_ARGS__, ##__VA_ARGS__); \
})
}; \
::capnproto::Log::ContextImpl<decltype(_capnpContextFunc)> _capnpContext(_capnpContextFunc)
#ifdef NDEBUG
#define DLOG(...) do {} while (false)
...
...
c++/src/capnproto/message.c++
View file @
d7f3123c
...
...
@@ -212,4 +212,20 @@ ArrayPtr<word> MallocMessageBuilder::allocateSegment(uint minimumSize) {
return
arrayPtr
(
reinterpret_cast
<
word
*>
(
result
),
size
);
}
// -------------------------------------------------------------------
FlatMessageBuilder
::
FlatMessageBuilder
(
ArrayPtr
<
word
>
array
)
:
array
(
array
),
allocated
(
false
)
{}
FlatMessageBuilder
::~
FlatMessageBuilder
()
{}
void
FlatMessageBuilder
::
requireFilled
()
{
PRECOND
(
getSegmentsForOutput
()[
0
].
end
()
==
array
.
end
(),
"FlatMessageBuilder's buffer was too large."
);
}
ArrayPtr
<
word
>
FlatMessageBuilder
::
allocateSegment
(
uint
minimumSize
)
{
PRECOND
(
!
allocated
,
"FlatMessageBuilder's buffer was not large enough."
);
allocated
=
true
;
return
array
;
}
}
// namespace capnproto
c++/src/capnproto/message.h
View file @
d7f3123c
...
...
@@ -175,7 +175,7 @@ private:
};
template
<
typename
RootType
>
static
typename
RootType
::
Reader
readMessageUnchecked
(
const
word
*
data
);
typename
RootType
::
Reader
readMessageUnchecked
(
const
word
*
data
);
// IF THE INPUT IS INVALID, THIS MAY CRASH, CORRUPT MEMORY, CREATE A SECURITY HOLE IN YOUR APP,
// MURDER YOUR FIRST-BORN CHILD, AND/OR BRING ABOUT ETERNAL DAMNATION ON ALL OF HUMANITY. DO NOT
// USE UNLESS YOU UNDERSTAND THE CONSEQUENCES.
...
...
@@ -198,12 +198,16 @@ static typename RootType::Reader readMessageUnchecked(const word* data);
// MyMessage::Reader reader = Message<MyMessage>::readMessageUnchecked(MyMessage::DEFAULT.words);
//
// To sanitize a message from an untrusted source such that it can be safely passed to
// readMessageUnchecked(), construct a MessageBuilder whose first segment is large enough to store
// the message, and then use MessageBuilder::setRoot() to copy the message in. The process of
// copying the message implicitly validates all pointers.
// readMessageUnchecked(), use copyToUnchecked().
template
<
typename
Reader
>
void
copyToUnchecked
(
Reader
&&
reader
,
ArrayPtr
<
word
>
uncheckedBuffer
);
// Copy the content of the given reader into the given buffer, such that it can safely be passed to
// readMessageUnchecked(). The buffer's size must be exactly reader.totalSizeInWords() + 1,
// otherwise an exception will be thrown.
template
<
typename
Type
>
static
typename
Type
::
Reader
defaultValue
();
typename
Type
::
Reader
defaultValue
();
// Get a default instance of the given struct or list type.
//
// TODO(cleanup): Find a better home for this function?
...
...
@@ -296,6 +300,25 @@ private:
std
::
unique_ptr
<
MoreSegments
>
moreSegments
;
};
class
FlatMessageBuilder
:
public
MessageBuilder
{
// A message builder implementation which allocates from a single flat array, throwing an
// exception if it runs out of space.
public
:
explicit
FlatMessageBuilder
(
ArrayPtr
<
word
>
array
);
CAPNPROTO_DISALLOW_COPY
(
FlatMessageBuilder
);
virtual
~
FlatMessageBuilder
();
void
requireFilled
();
// Throws an exception if the flat array is not exactly full.
virtual
ArrayPtr
<
word
>
allocateSegment
(
uint
minimumSize
)
override
;
private
:
ArrayPtr
<
word
>
array
;
bool
allocated
;
};
// =======================================================================================
// implementation details
...
...
@@ -333,6 +356,13 @@ typename RootType::Reader readMessageUnchecked(const word* data) {
return
typename
RootType
::
Reader
(
internal
::
StructReader
::
readRootUnchecked
(
data
));
}
template
<
typename
Reader
>
void
copyToUnchecked
(
Reader
&&
reader
,
ArrayPtr
<
word
>
uncheckedBuffer
)
{
FlatMessageBuilder
builder
(
uncheckedBuffer
);
builder
.
setRoot
(
capnproto
::
forward
<
Reader
>
(
reader
));
builder
.
requireFilled
();
}
template
<
typename
Type
>
static
typename
Type
::
Reader
defaultValue
()
{
// TODO(soon): Correctly handle lists. Maybe primitives too?
...
...
c++/src/capnproto/schema-loader-test.c++
0 → 100644
View file @
d7f3123c
// 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-loader.h"
#include <gtest/gtest.h>
#include "test-util.h"
#include "logging.h"
namespace
capnproto
{
namespace
internal
{
namespace
{
TEST
(
SchemaLoader
,
Load
)
{
SchemaLoader
loader
;
Schema
struct32Schema
=
loader
.
load
(
Schema
::
from
<
test
::
TestLists
::
Struct32
>
().
getProto
());
auto
nativeSchema
=
Schema
::
from
<
test
::
TestLists
>
();
Schema
testListsSchema
=
loader
.
load
(
nativeSchema
.
getProto
());
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_FALSE
(
testListsSchema
==
nativeSchema
);
EXPECT_FALSE
(
struct32Schema
==
Schema
::
from
<
test
::
TestLists
::
Struct32
>
());
EXPECT_FALSE
(
struct8Schema
==
Schema
::
from
<
test
::
TestLists
::
Struct8
>
());
EXPECT_FALSE
(
structPSchema
==
Schema
::
from
<
test
::
TestLists
::
StructP
>
());
EXPECT_TRUE
(
testListsSchema
.
getDependency
(
typeId
<
test
::
TestLists
::
Struct32
>
())
==
struct32Schema
);
EXPECT_TRUE
(
testListsSchema
.
getDependency
(
typeId
<
test
::
TestLists
::
Struct8
>
())
==
struct8Schema
);
EXPECT_TRUE
(
testListsSchema
.
getDependency
(
typeId
<
test
::
TestLists
::
StructP
>
())
==
structPSchema
);
auto
struct16Schema
=
testListsSchema
.
getDependency
(
typeId
<
test
::
TestLists
::
Struct16
>
());
EXPECT_EQ
(
0u
,
struct16Schema
.
getProto
().
getBody
().
getStructNode
().
getMembers
().
size
());
}
TEST
(
SchemaLoader
,
Use
)
{
SchemaLoader
loader
;
StructSchema
schema
=
loader
.
load
(
Schema
::
from
<
TestAllTypes
>
().
getProto
()).
asStruct
();
// Also have to load TestEnum.
loader
.
load
(
Schema
::
from
<
TestEnum
>
().
getProto
());
{
MallocMessageBuilder
builder
;
auto
root
=
builder
.
getRoot
<
DynamicStruct
>
(
schema
);
initDynamicTestMessage
(
root
);
checkDynamicTestMessage
(
root
.
asReader
());
// Can't convert to TestAllTypes because we didn't use loadCompiledTypeAndDependencies().
EXPECT_ANY_THROW
(
root
.
as
<
TestAllTypes
>
());
// But if we reinterpret the raw bytes, it works.
checkTestMessage
(
builder
.
getRoot
<
TestAllTypes
>
());
}
loader
.
loadCompiledTypeAndDependencies
<
TestAllTypes
>
();
{
MallocMessageBuilder
builder
;
auto
root
=
builder
.
getRoot
<
DynamicStruct
>
(
schema
);
initDynamicTestMessage
(
root
);
// Now we can actually cast.
checkTestMessage
(
root
.
as
<
TestAllTypes
>
());
}
// Let's also test TestListDefaults, but as we do so, let's load the compiled types first, to
// make sure the opposite order works.
loader
.
loadCompiledTypeAndDependencies
<
TestListDefaults
>
();
StructSchema
testListsSchema
=
loader
.
get
(
typeId
<
TestListDefaults
>
()).
asStruct
();
EXPECT_TRUE
(
testListsSchema
!=
Schema
::
from
<
TestListDefaults
>
());
{
MallocMessageBuilder
builder
;
auto
root
=
builder
.
getRoot
<
DynamicStruct
>
(
testListsSchema
);
initDynamicTestLists
(
root
);
checkDynamicTestLists
(
root
.
asReader
());
checkTestMessage
(
root
.
as
<
TestListDefaults
>
());
}
EXPECT_TRUE
(
loader
.
load
(
Schema
::
from
<
TestListDefaults
>
().
getProto
())
==
testListsSchema
);
{
MallocMessageBuilder
builder
;
auto
root
=
builder
.
getRoot
<
DynamicStruct
>
(
testListsSchema
);
initDynamicTestLists
(
root
);
checkTestMessage
(
root
.
as
<
TestListDefaults
>
());
}
// Finally, let's test some unions.
StructSchema
unionSchema
=
loader
.
load
(
Schema
::
from
<
TestUnion
>
().
getProto
()).
asStruct
();
{
MallocMessageBuilder
builder
;
auto
root
=
builder
.
getRoot
<
DynamicStruct
>
(
unionSchema
);
root
.
get
(
"union0"
).
as
<
DynamicUnion
>
().
set
(
"u0f1s16"
,
123
);
root
.
get
(
"union1"
).
as
<
DynamicUnion
>
().
set
(
"u1f0sp"
,
"hello"
);
auto
reader
=
builder
.
getRoot
<
TestUnion
>
().
asReader
();
EXPECT_EQ
(
123
,
reader
.
getUnion0
().
getU0f1s16
());
EXPECT_EQ
(
"hello"
,
reader
.
getUnion1
().
getU1f0sp
());
}
}
template
<
typename
T
>
Schema
loadUnderAlternateTypeId
(
SchemaLoader
&
loader
,
uint64_t
id
)
{
MallocMessageBuilder
schemaBuilder
;
schemaBuilder
.
setRoot
(
Schema
::
from
<
T
>
().
getProto
());
auto
root
=
schemaBuilder
.
getRoot
<
schema
::
Node
>
();
root
.
setId
(
id
);
if
(
root
.
getBody
().
which
()
==
schema
::
Node
::
Body
::
STRUCT_NODE
)
{
// If the struct contains any self-referential members, change their type IDs as well.
auto
members
=
root
.
getBody
().
getStructNode
().
getMembers
();
for
(
auto
member
:
members
)
{
if
(
member
.
getBody
().
which
()
==
schema
::
StructNode
::
Member
::
Body
::
FIELD_MEMBER
)
{
auto
type
=
member
.
getBody
().
getFieldMember
().
getType
().
getBody
();
if
(
type
.
which
()
==
schema
::
Type
::
Body
::
STRUCT_TYPE
&&
type
.
getStructType
()
==
typeId
<
T
>
())
{
type
.
setStructType
(
id
);
}
}
}
}
return
loader
.
load
(
root
);
}
TEST
(
SchemaLoader
,
Upgrade
)
{
SchemaLoader
loader
;
loader
.
loadCompiledTypeAndDependencies
<
test
::
TestOldVersion
>
();
StructSchema
schema
=
loader
.
get
(
typeId
<
test
::
TestOldVersion
>
()).
asStruct
();
EXPECT_STREQ
(
Schema
::
from
<
test
::
TestOldVersion
>
().
getProto
().
debugString
().
cStr
(),
schema
.
getProto
().
debugString
().
cStr
());
loadUnderAlternateTypeId
<
test
::
TestNewVersion
>
(
loader
,
typeId
<
test
::
TestOldVersion
>
());
// The new version replaced the old.
EXPECT_STREQ
(
Schema
::
from
<
test
::
TestNewVersion
>
().
getProto
().
getDisplayName
(),
schema
.
getProto
().
getDisplayName
());
// But it is still usable as the old version.
schema
.
requireUsableAs
<
test
::
TestOldVersion
>
();
}
TEST
(
SchemaLoader
,
Downgrade
)
{
SchemaLoader
loader
;
loader
.
loadCompiledTypeAndDependencies
<
test
::
TestNewVersion
>
();
StructSchema
schema
=
loader
.
get
(
typeId
<
test
::
TestNewVersion
>
()).
asStruct
();
EXPECT_STREQ
(
Schema
::
from
<
test
::
TestNewVersion
>
().
getProto
().
debugString
().
cStr
(),
schema
.
getProto
().
debugString
().
cStr
());
loadUnderAlternateTypeId
<
test
::
TestOldVersion
>
(
loader
,
typeId
<
test
::
TestNewVersion
>
());
// We kept the new version, because the replacement was older.
EXPECT_STREQ
(
Schema
::
from
<
test
::
TestNewVersion
>
().
getProto
().
getDisplayName
(),
schema
.
getProto
().
getDisplayName
());
schema
.
requireUsableAs
<
test
::
TestNewVersion
>
();
}
TEST
(
SchemaLoader
,
Incompatible
)
{
SchemaLoader
loader
;
loader
.
loadCompiledTypeAndDependencies
<
test
::
TestListDefaults
>
();
EXPECT_ANY_THROW
(
loadUnderAlternateTypeId
<
test
::
TestAllTypes
>
(
loader
,
typeId
<
test
::
TestListDefaults
>
()));
}
TEST
(
SchemaLoader
,
Enumerate
)
{
SchemaLoader
loader
;
loader
.
loadCompiledTypeAndDependencies
<
TestAllTypes
>
();
auto
list
=
loader
.
getAllLoaded
();
ASSERT_EQ
(
2u
,
list
.
size
());
if
(
list
[
0
]
==
loader
.
get
(
typeId
<
TestAllTypes
>
()))
{
EXPECT_TRUE
(
list
[
1
]
==
loader
.
get
(
typeId
<
TestEnum
>
()));
}
else
{
EXPECT_TRUE
(
list
[
0
]
==
loader
.
get
(
typeId
<
TestEnum
>
()));
EXPECT_TRUE
(
list
[
1
]
==
loader
.
get
(
typeId
<
TestAllTypes
>
()));
}
}
// TODO(test): More extensively test upgrade/downgrade checks.
}
// namespace
}
// namespace internal
}
// namespace capnproto
c++/src/capnproto/schema-loader.c++
0 → 100644
View file @
d7f3123c
// 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-loader.h"
#include <unordered_map>
#include <map>
#include "message.h"
#include "arena.h"
#include "logging.h"
#include "exception.h"
namespace
capnproto
{
class
SchemaLoader
::
Impl
{
public
:
Impl
();
internal
::
RawSchema
*
load
(
schema
::
Node
::
Reader
reader
);
internal
::
RawSchema
*
loadNative
(
const
internal
::
RawSchema
*
nativeSchema
);
internal
::
RawSchema
*
loadEmpty
(
uint64_t
id
,
Text
::
Reader
name
,
schema
::
Node
::
Body
::
Which
kind
);
// Create a dummy empty schema of the given kind for the given id and load it.
internal
::
RawSchema
*
tryGet
(
uint64_t
typeId
)
const
;
Array
<
Schema
>
getAllLoaded
()
const
;
template
<
typename
T
>
T
*
allocate
(
size_t
count
=
1
)
{
ByteCount
bytes
=
count
*
sizeof
(
T
)
*
BYTES
;
static_assert
(
sizeof
(
word
)
==
8
,
"This code assumes 64-bit words."
);
WordCount
words
=
(
bytes
+
7
*
BYTES
)
/
BYTES_PER_WORD
;
while
(
true
)
{
word
*
result
=
segment
->
allocate
(
words
);
if
(
result
!=
nullptr
)
{
return
reinterpret_cast
<
T
*>
(
result
);
}
segment
=
arena
->
getSegmentWithAvailable
(
words
);
}
}
private
:
MallocMessageBuilder
allocator
;
internal
::
BuilderArena
*
arena
;
internal
::
SegmentBuilder
*
segment
;
// HACK: We don't actually use these to build messages, we use them to allocate memory that
// should be freed when the loader is freed. We're reusing BuilderArena as a convenient
// implementation of the general concept of arenas.
// TODO(cleanup): Develop a stand-alone Arena class that can be used for things like this.
std
::
unordered_map
<
uint64_t
,
internal
::
RawSchema
*>
schemas
;
};
// =======================================================================================
class
SchemaLoader
::
Validator
{
public
:
Validator
(
SchemaLoader
::
Impl
&
loader
)
:
loader
(
loader
)
{}
bool
validate
(
schema
::
Node
::
Reader
node
)
{
isValid
=
true
;
nodeName
=
node
.
getDisplayName
();
dependencies
.
clear
();
CONTEXT
(
"validating schema node"
,
nodeName
,
node
.
getBody
().
which
());
switch
(
node
.
getBody
().
which
())
{
case
schema
:
:
Node
::
Body
::
FILE_NODE
:
validate
(
node
.
getBody
().
getFileNode
());
break
;
case
schema
:
:
Node
::
Body
::
STRUCT_NODE
:
validate
(
node
.
getBody
().
getStructNode
());
break
;
case
schema
:
:
Node
::
Body
::
ENUM_NODE
:
validate
(
node
.
getBody
().
getEnumNode
());
break
;
case
schema
:
:
Node
::
Body
::
INTERFACE_NODE
:
validate
(
node
.
getBody
().
getInterfaceNode
());
break
;
case
schema
:
:
Node
::
Body
::
CONST_NODE
:
validate
(
node
.
getBody
().
getConstNode
());
break
;
case
schema
:
:
Node
::
Body
::
ANNOTATION_NODE
:
validate
(
node
.
getBody
().
getAnnotationNode
());
break
;
}
// We accept and pass through node types we don't recognize.
return
isValid
;
}
const
internal
::
RawSchema
**
makeDependencyArray
(
uint32_t
*
count
)
{
*
count
=
dependencies
.
size
();
const
internal
::
RawSchema
**
result
=
loader
.
allocate
<
const
internal
::
RawSchema
*>
(
*
count
);
uint
pos
=
0
;
for
(
auto
&
dep
:
dependencies
)
{
result
[
pos
++
]
=
dep
.
second
;
}
DCHECK
(
pos
==
*
count
);
return
result
;
}
const
internal
::
RawSchema
::
MemberInfo
*
makeMemberInfoArray
(
uint32_t
*
count
)
{
*
count
=
members
.
size
();
internal
::
RawSchema
::
MemberInfo
*
result
=
loader
.
allocate
<
internal
::
RawSchema
::
MemberInfo
>
(
*
count
);
uint
pos
=
0
;
for
(
auto
&
member
:
members
)
{
result
[
pos
++
]
=
internal
::
RawSchema
::
MemberInfo
(
member
.
first
.
first
,
member
.
second
);
}
DCHECK
(
pos
==
*
count
);
return
result
;
}
private
:
SchemaLoader
::
Impl
&
loader
;
Text
::
Reader
nodeName
;
bool
isValid
;
std
::
map
<
uint64_t
,
internal
::
RawSchema
*>
dependencies
;
// Maps (unionIndex, name) -> index for each member.
std
::
map
<
std
::
pair
<
uint
,
Text
::
Reader
>
,
uint
>
members
;
#define VALIDATE_SCHEMA(condition, ...) \
VALIDATE_INPUT(condition, ##__VA_ARGS__) { isValid = false; return; }
#define FAIL_VALIDATE_SCHEMA(...) \
FAIL_VALIDATE_INPUT(__VA_ARGS__) { isValid = false; return; }
void
validate
(
schema
::
FileNode
::
Reader
fileNode
)
{
// Nothing needs validation.
}
void
validate
(
schema
::
StructNode
::
Reader
structNode
)
{
uint
dataSizeInBits
;
uint
pointerCount
;
switch
(
structNode
.
getPreferredListEncoding
())
{
case
schema
:
:
ElementSize
::
EMPTY
:
dataSizeInBits
=
0
;
pointerCount
=
0
;
break
;
case
schema
:
:
ElementSize
::
BIT
:
dataSizeInBits
=
1
;
pointerCount
=
0
;
break
;
case
schema
:
:
ElementSize
::
BYTE
:
dataSizeInBits
=
8
;
pointerCount
=
0
;
break
;
case
schema
:
:
ElementSize
::
TWO_BYTES
:
dataSizeInBits
=
16
;
pointerCount
=
0
;
break
;
case
schema
:
:
ElementSize
::
FOUR_BYTES
:
dataSizeInBits
=
32
;
pointerCount
=
0
;
break
;
case
schema
:
:
ElementSize
::
EIGHT_BYTES
:
dataSizeInBits
=
64
;
pointerCount
=
0
;
break
;
case
schema
:
:
ElementSize
::
POINTER
:
dataSizeInBits
=
0
;
pointerCount
=
1
;
break
;
case
schema
:
:
ElementSize
::
INLINE_COMPOSITE
:
dataSizeInBits
=
structNode
.
getDataSectionWordSize
()
*
64
;
pointerCount
=
structNode
.
getPointerSectionSize
();
break
;
default:
FAIL_VALIDATE_SCHEMA
(
"Invalid preferredListEncoding."
);
dataSizeInBits
=
0
;
pointerCount
=
0
;
break
;
}
VALIDATE_SCHEMA
(
structNode
.
getDataSectionWordSize
()
==
(
dataSizeInBits
+
63
)
/
64
&&
structNode
.
getPointerSectionSize
()
==
pointerCount
,
"Struct size does not match preferredListEncoding."
);
uint
ordinalCount
=
0
;
auto
members
=
structNode
.
getMembers
();
for
(
auto
member
:
members
)
{
++
ordinalCount
;
if
(
member
.
getBody
().
which
()
==
schema
::
StructNode
::
Member
::
Body
::
UNION_MEMBER
)
{
ordinalCount
+=
member
.
getBody
().
getUnionMember
().
getMembers
().
size
();
}
}
CAPNPROTO_STACK_ARRAY
(
bool
,
sawCodeOrder
,
members
.
size
(),
256
);
memset
(
sawCodeOrder
.
begin
(),
0
,
sawCodeOrder
.
size
()
*
sizeof
(
sawCodeOrder
[
0
]));
CAPNPROTO_STACK_ARRAY
(
bool
,
sawOrdinal
,
ordinalCount
,
256
);
memset
(
sawOrdinal
.
begin
(),
0
,
sawOrdinal
.
size
()
*
sizeof
(
sawOrdinal
[
0
]));
uint
index
=
0
;
for
(
auto
member
:
members
)
{
CONTEXT
(
"validating struct member"
,
member
.
getName
());
validate
(
member
,
sawCodeOrder
,
sawOrdinal
,
dataSizeInBits
,
pointerCount
,
0
,
index
++
);
}
}
void
validateMemberName
(
Text
::
Reader
name
,
uint
unionIndex
,
uint
index
)
{
bool
isNewName
=
members
.
insert
(
std
::
make_pair
(
std
::
pair
<
uint
,
Text
::
Reader
>
(
unionIndex
,
name
),
index
)).
second
;
VALIDATE_SCHEMA
(
isNewName
,
"duplicate name"
,
name
);
}
void
validate
(
schema
::
StructNode
::
Member
::
Reader
member
,
ArrayPtr
<
bool
>
sawCodeOrder
,
ArrayPtr
<
bool
>
sawOrdinal
,
uint
dataSizeInBits
,
uint
pointerCount
,
uint
unionIndex
,
uint
index
)
{
validateMemberName
(
member
.
getName
(),
unionIndex
,
index
);
VALIDATE_SCHEMA
(
member
.
getOrdinal
()
<
sawOrdinal
.
size
()
&&
!
sawOrdinal
[
member
.
getOrdinal
()],
"Invalid ordinal."
);
sawOrdinal
[
member
.
getOrdinal
()]
=
true
;
VALIDATE_SCHEMA
(
member
.
getCodeOrder
()
<
sawCodeOrder
.
size
()
&&
!
sawCodeOrder
[
member
.
getCodeOrder
()],
"Invalid codeOrder."
);
sawCodeOrder
[
member
.
getCodeOrder
()]
=
true
;
switch
(
member
.
getBody
().
which
())
{
case
schema
:
:
StructNode
::
Member
::
Body
::
FIELD_MEMBER
:
{
auto
field
=
member
.
getBody
().
getFieldMember
();
uint
fieldBits
;
bool
fieldIsPointer
;
validate
(
field
.
getType
(),
field
.
getDefaultValue
(),
&
fieldBits
,
&
fieldIsPointer
);
VALIDATE_SCHEMA
(
fieldBits
*
(
field
.
getOffset
()
+
1
)
<=
dataSizeInBits
&&
fieldIsPointer
*
(
field
.
getOffset
()
+
1
)
<=
pointerCount
,
"field offset out-of-bounds"
,
field
.
getOffset
(),
dataSizeInBits
,
pointerCount
);
break
;
}
case
schema
:
:
StructNode
::
Member
::
Body
::
UNION_MEMBER
:
{
auto
u
=
member
.
getBody
().
getUnionMember
();
VALIDATE_SCHEMA
((
u
.
getDiscriminantOffset
()
+
1
)
*
16
<=
dataSizeInBits
,
"Schema invalid: Union discriminant out-of-bounds."
);
auto
uMembers
=
u
.
getMembers
();
CAPNPROTO_STACK_ARRAY
(
bool
,
uSawCodeOrder
,
uMembers
.
size
(),
256
);
memset
(
uSawCodeOrder
.
begin
(),
0
,
uSawCodeOrder
.
size
()
*
sizeof
(
uSawCodeOrder
[
0
]));
uint
subIndex
=
0
;
for
(
auto
uMember
:
uMembers
)
{
CONTEXT
(
"validating union member"
,
uMember
.
getName
());
VALIDATE_SCHEMA
(
uMember
.
getBody
().
which
()
==
schema
::
StructNode
::
Member
::
Body
::
FIELD_MEMBER
,
"Union members must be fields."
);
validate
(
uMember
,
uSawCodeOrder
,
sawOrdinal
,
dataSizeInBits
,
pointerCount
,
index
+
1
,
subIndex
++
);
}
break
;
}
}
}
void
validate
(
schema
::
EnumNode
::
Reader
enumNode
)
{
auto
enumerants
=
enumNode
.
getEnumerants
();
CAPNPROTO_STACK_ARRAY
(
bool
,
sawCodeOrder
,
enumerants
.
size
(),
256
);
memset
(
sawCodeOrder
.
begin
(),
0
,
sawCodeOrder
.
size
()
*
sizeof
(
sawCodeOrder
[
0
]));
uint
index
=
0
;
for
(
auto
enumerant
:
enumerants
)
{
validateMemberName
(
enumerant
.
getName
(),
0
,
index
++
);
VALIDATE_SCHEMA
(
enumerant
.
getCodeOrder
()
<
enumerants
.
size
()
&&
!
sawCodeOrder
[
enumerant
.
getCodeOrder
()],
"invalid codeOrder"
,
enumerant
.
getName
());
sawCodeOrder
[
enumerant
.
getCodeOrder
()]
=
true
;
}
}
void
validate
(
schema
::
InterfaceNode
::
Reader
interfaceNode
)
{
auto
methods
=
interfaceNode
.
getMethods
();
CAPNPROTO_STACK_ARRAY
(
bool
,
sawCodeOrder
,
methods
.
size
(),
256
);
memset
(
sawCodeOrder
.
begin
(),
0
,
sawCodeOrder
.
size
()
*
sizeof
(
sawCodeOrder
[
0
]));
uint
index
=
0
;
for
(
auto
method
:
methods
)
{
CONTEXT
(
"validating method"
,
method
.
getName
());
validateMemberName
(
method
.
getName
(),
0
,
index
++
);
VALIDATE_SCHEMA
(
method
.
getCodeOrder
()
<
methods
.
size
()
&&
!
sawCodeOrder
[
method
.
getCodeOrder
()],
"invalid codeOrder"
);
sawCodeOrder
[
method
.
getCodeOrder
()]
=
true
;
auto
params
=
method
.
getParams
();
for
(
auto
param
:
params
)
{
CONTEXT
(
"validating parameter"
,
param
.
getName
());
uint
dummy1
;
bool
dummy2
;
validate
(
param
.
getType
(),
param
.
getDefaultValue
(),
&
dummy1
,
&
dummy2
);
}
VALIDATE_SCHEMA
(
method
.
getRequiredParamCount
()
<=
params
.
size
(),
"invalid requiredParamCount"
);
validate
(
method
.
getReturnType
());
}
}
void
validate
(
schema
::
ConstNode
::
Reader
constNode
)
{
uint
dummy1
;
bool
dummy2
;
validate
(
constNode
.
getType
(),
constNode
.
getValue
(),
&
dummy1
,
&
dummy2
);
}
void
validate
(
schema
::
AnnotationNode
::
Reader
annotationNode
)
{
validate
(
annotationNode
.
getType
());
}
void
validate
(
schema
::
Type
::
Reader
type
,
schema
::
Value
::
Reader
value
,
uint
*
dataSizeInBits
,
bool
*
isPointer
)
{
validate
(
type
);
schema
::
Value
::
Body
::
Which
expectedValueType
=
schema
::
Value
::
Body
::
VOID_VALUE
;
bool
hadCase
=
false
;
switch
(
type
.
getBody
().
which
())
{
#define HANDLE_TYPE(name, bits, ptr) \
case schema::Type::Body::name##_TYPE: \
expectedValueType = schema::Value::Body::name##_VALUE; \
*dataSizeInBits = bits; *isPointer = ptr; \
hadCase = true; \
break;
HANDLE_TYPE
(
VOID
,
0
,
false
)
HANDLE_TYPE
(
BOOL
,
1
,
false
)
HANDLE_TYPE
(
INT8
,
8
,
false
)
HANDLE_TYPE
(
INT16
,
16
,
false
)
HANDLE_TYPE
(
INT32
,
32
,
false
)
HANDLE_TYPE
(
INT64
,
64
,
false
)
HANDLE_TYPE
(
UINT8
,
8
,
false
)
HANDLE_TYPE
(
UINT16
,
16
,
false
)
HANDLE_TYPE
(
UINT32
,
32
,
false
)
HANDLE_TYPE
(
UINT64
,
64
,
false
)
HANDLE_TYPE
(
FLOAT32
,
32
,
false
)
HANDLE_TYPE
(
FLOAT64
,
64
,
false
)
HANDLE_TYPE
(
TEXT
,
0
,
true
)
HANDLE_TYPE
(
DATA
,
0
,
true
)
HANDLE_TYPE
(
LIST
,
0
,
true
)
HANDLE_TYPE
(
ENUM
,
16
,
false
)
HANDLE_TYPE
(
STRUCT
,
0
,
true
)
HANDLE_TYPE
(
INTERFACE
,
0
,
true
)
HANDLE_TYPE
(
OBJECT
,
0
,
true
)
#undef HANDLE_TYPE
}
if
(
hadCase
)
{
VALIDATE_SCHEMA
(
value
.
getBody
().
which
()
==
expectedValueType
,
"Value did not match type."
);
}
}
void
validate
(
schema
::
Type
::
Reader
type
)
{
switch
(
type
.
getBody
().
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
:
case
schema
:
:
Type
::
Body
::
OBJECT_TYPE
:
break
;
case
schema
:
:
Type
::
Body
::
STRUCT_TYPE
:
validateTypeId
(
type
.
getBody
().
getStructType
(),
schema
::
Node
::
Body
::
STRUCT_NODE
);
break
;
case
schema
:
:
Type
::
Body
::
ENUM_TYPE
:
validateTypeId
(
type
.
getBody
().
getEnumType
(),
schema
::
Node
::
Body
::
ENUM_NODE
);
break
;
case
schema
:
:
Type
::
Body
::
INTERFACE_TYPE
:
validateTypeId
(
type
.
getBody
().
getInterfaceType
(),
schema
::
Node
::
Body
::
INTERFACE_NODE
);
break
;
case
schema
:
:
Type
::
Body
::
LIST_TYPE
:
validate
(
type
.
getBody
().
getListType
());
break
;
}
// We intentionally allow unknown types.
}
void
validateTypeId
(
uint64_t
id
,
schema
::
Node
::
Body
::
Which
expectedKind
)
{
internal
::
RawSchema
*
existing
=
loader
.
tryGet
(
id
);
if
(
existing
!=
nullptr
)
{
auto
node
=
readMessageUnchecked
<
schema
::
Node
>
(
existing
->
encodedNode
);
VALIDATE_SCHEMA
(
node
.
getBody
().
which
()
==
expectedKind
,
"expected a different kind of node for this ID"
,
id
,
expectedKind
,
node
.
getBody
().
which
(),
node
.
getDisplayName
());
dependencies
.
insert
(
std
::
make_pair
(
id
,
existing
));
return
;
}
// TODO(cleanup): str() really needs to return something NUL-terminated...
dependencies
.
insert
(
std
::
make_pair
(
id
,
loader
.
loadEmpty
(
id
,
str
(
"(unknown type used by "
,
nodeName
,
")"
,
'\0'
).
begin
(),
expectedKind
)));
}
#undef VALIDATE_SCHEMA
#undef FAIL_VALIDATE_SCHEMA
};
// =======================================================================================
class
SchemaLoader
::
CompatibilityChecker
{
public
:
CompatibilityChecker
(
SchemaLoader
::
Impl
&
loader
)
:
loader
(
loader
)
{}
bool
shouldReplace
(
schema
::
Node
::
Reader
existingNode
,
schema
::
Node
::
Reader
replacement
,
bool
replacementIsNative
)
{
CONTEXT
(
"checking compatibility with previously-loaded node of the same id"
,
existingNode
.
getDisplayName
());
DPRECOND
(
existingNode
.
getId
()
==
replacement
.
getId
());
nodeName
=
existingNode
.
getDisplayName
();
compatibility
=
EQUIVALENT
;
checkCompatibility
(
existingNode
,
replacement
);
// Prefer the newer schema. If neither is newer, prefer native types, otherwise prefer the
// existing type.
return
replacementIsNative
?
compatibility
!=
OLDER
:
compatibility
==
NEWER
;
}
private
:
SchemaLoader
::
Impl
&
loader
;
Text
::
Reader
nodeName
;
enum
Compatibility
{
EQUIVALENT
,
OLDER
,
NEWER
,
INCOMPATIBLE
};
Compatibility
compatibility
;
#define VALIDATE_SCHEMA(condition, ...) \
VALIDATE_INPUT(condition, ##__VA_ARGS__) { compatibility = INCOMPATIBLE; return; }
#define FAIL_VALIDATE_SCHEMA(...) \
FAIL_VALIDATE_INPUT(__VA_ARGS__) { compatibility = INCOMPATIBLE; return; }
void
replacementIsNewer
()
{
switch
(
compatibility
)
{
case
EQUIVALENT
:
compatibility
=
NEWER
;
break
;
case
OLDER
:
FAIL_VALIDATE_SCHEMA
(
"Schema node contains some changes that are upgrades and some "
"that are downgrades. All changes must be in the same direction for compatibility."
);
break
;
case
NEWER
:
break
;
case
INCOMPATIBLE
:
break
;
}
}
void
replacementIsOlder
()
{
switch
(
compatibility
)
{
case
EQUIVALENT
:
compatibility
=
OLDER
;
break
;
case
OLDER
:
break
;
case
NEWER
:
FAIL_VALIDATE_SCHEMA
(
"Schema node contains some changes that are upgrades and some "
"that are downgrades. All changes must be in the same direction for compatibility."
);
break
;
case
INCOMPATIBLE
:
break
;
}
}
void
checkCompatibility
(
schema
::
Node
::
Reader
node
,
schema
::
Node
::
Reader
replacement
)
{
// Returns whether `replacement` is equivalent, older than, newer than, or incompatible with
// `node`. If exceptions are enabled, this will throw an exception on INCOMPATIBLE.
VALIDATE_SCHEMA
(
node
.
getBody
().
which
()
==
replacement
.
getBody
().
which
(),
"kind of declaration changed"
);
// No need to check compatibility of the non-body parts of the node:
// - Arbitrary renaming and moving between scopes is allowed.
// - Annotations are ignored for compatibility purposes.
switch
(
node
.
getBody
().
which
())
{
case
schema
:
:
Node
::
Body
::
FILE_NODE
:
checkCompatibility
(
node
.
getBody
().
getFileNode
(),
replacement
.
getBody
().
getFileNode
());
break
;
case
schema
:
:
Node
::
Body
::
STRUCT_NODE
:
checkCompatibility
(
node
.
getBody
().
getStructNode
(),
replacement
.
getBody
().
getStructNode
());
break
;
case
schema
:
:
Node
::
Body
::
ENUM_NODE
:
checkCompatibility
(
node
.
getBody
().
getEnumNode
(),
replacement
.
getBody
().
getEnumNode
());
break
;
case
schema
:
:
Node
::
Body
::
INTERFACE_NODE
:
checkCompatibility
(
node
.
getBody
().
getInterfaceNode
(),
replacement
.
getBody
().
getInterfaceNode
());
break
;
case
schema
:
:
Node
::
Body
::
CONST_NODE
:
checkCompatibility
(
node
.
getBody
().
getConstNode
(),
replacement
.
getBody
().
getConstNode
());
break
;
case
schema
:
:
Node
::
Body
::
ANNOTATION_NODE
:
checkCompatibility
(
node
.
getBody
().
getAnnotationNode
(),
replacement
.
getBody
().
getAnnotationNode
());
break
;
}
}
void
checkCompatibility
(
schema
::
FileNode
::
Reader
file
,
schema
::
FileNode
::
Reader
replacement
)
{
// Nothing to compare.
}
void
checkCompatibility
(
schema
::
StructNode
::
Reader
structNode
,
schema
::
StructNode
::
Reader
replacement
)
{
if
(
replacement
.
getDataSectionWordSize
()
>
structNode
.
getDataSectionWordSize
())
{
replacementIsNewer
();
}
else
if
(
replacement
.
getDataSectionWordSize
()
<
structNode
.
getDataSectionWordSize
())
{
replacementIsOlder
();
}
if
(
replacement
.
getPointerSectionSize
()
>
structNode
.
getPointerSectionSize
())
{
replacementIsNewer
();
}
else
if
(
replacement
.
getPointerSectionSize
()
<
structNode
.
getPointerSectionSize
())
{
replacementIsOlder
();
}
// We can do a simple comparison of preferredListEncoding here because the only case where it
// isn't correct to compare this way is when one side is BIT/BYTE/*_BYTES while the other side
// is POINTER, and if that were the case then the above comparisons would already have failed
// or one of the nodes would have failed validation.
if
(
replacement
.
getPreferredListEncoding
()
>
structNode
.
getPreferredListEncoding
())
{
replacementIsNewer
();
}
else
if
(
replacement
.
getPreferredListEncoding
()
<
structNode
.
getPreferredListEncoding
())
{
replacementIsOlder
();
}
// The shared members should occupy corresponding positions in the member lists, since the
// lists are sorted by ordinal.
auto
members
=
structNode
.
getMembers
();
auto
replacementMembers
=
replacement
.
getMembers
();
uint
count
=
std
::
min
(
members
.
size
(),
replacementMembers
.
size
());
if
(
replacementMembers
.
size
()
>
members
.
size
())
{
replacementIsNewer
();
}
else
if
(
replacementMembers
.
size
()
<
members
.
size
())
{
replacementIsOlder
();
}
for
(
uint
i
=
0
;
i
<
count
;
i
++
)
{
checkCompatibility
(
members
[
i
],
replacementMembers
[
i
]);
}
}
void
checkCompatibility
(
schema
::
StructNode
::
Member
::
Reader
member
,
schema
::
StructNode
::
Member
::
Reader
replacement
)
{
CONTEXT
(
"comparing struct member"
,
member
.
getName
());
switch
(
member
.
getBody
().
which
())
{
case
schema
:
:
StructNode
::
Member
::
Body
::
FIELD_MEMBER
:
{
auto
field
=
member
.
getBody
().
getFieldMember
();
auto
replacementField
=
replacement
.
getBody
().
getFieldMember
();
checkCompatibility
(
field
.
getType
(),
replacementField
.
getType
(),
NO_UPGRADE_TO_STRUCT
);
checkDefaultCompatibility
(
field
.
getDefaultValue
(),
replacementField
.
getDefaultValue
());
VALIDATE_SCHEMA
(
field
.
getOffset
()
==
replacementField
.
getOffset
(),
"field position changed"
);
break
;
}
case
schema
:
:
StructNode
::
Member
::
Body
::
UNION_MEMBER
:
{
auto
existingUnion
=
member
.
getBody
().
getUnionMember
();
auto
replacementUnion
=
replacement
.
getBody
().
getUnionMember
();
VALIDATE_SCHEMA
(
existingUnion
.
getDiscriminantOffset
()
==
replacementUnion
.
getDiscriminantOffset
(),
"union discriminant position changed"
);
auto
members
=
existingUnion
.
getMembers
();
auto
replacementMembers
=
replacementUnion
.
getMembers
();
uint
count
=
std
::
min
(
members
.
size
(),
replacementMembers
.
size
());
if
(
replacementMembers
.
size
()
>
members
.
size
())
{
replacementIsNewer
();
}
else
if
(
replacementMembers
.
size
()
<
members
.
size
())
{
replacementIsOlder
();
}
for
(
uint
i
=
0
;
i
<
count
;
i
++
)
{
checkCompatibility
(
members
[
i
],
replacementMembers
[
i
]);
}
break
;
}
}
}
void
checkCompatibility
(
schema
::
EnumNode
::
Reader
enumNode
,
schema
::
EnumNode
::
Reader
replacement
)
{
uint
size
=
enumNode
.
getEnumerants
().
size
();
uint
replacementSize
=
replacement
.
getEnumerants
().
size
();
if
(
replacementSize
>
size
)
{
replacementIsNewer
();
}
else
if
(
replacementSize
<
size
)
{
replacementIsOlder
();
}
}
void
checkCompatibility
(
schema
::
InterfaceNode
::
Reader
interfaceNode
,
schema
::
InterfaceNode
::
Reader
replacement
)
{
auto
methods
=
interfaceNode
.
getMethods
();
auto
replacementMethods
=
replacement
.
getMethods
();
if
(
replacementMethods
.
size
()
>
methods
.
size
())
{
replacementIsNewer
();
}
else
if
(
replacementMethods
.
size
()
<
methods
.
size
())
{
replacementIsOlder
();
}
uint
count
=
std
::
min
(
methods
.
size
(),
replacementMethods
.
size
());
for
(
uint
i
=
0
;
i
<
count
;
i
++
)
{
checkCompatibility
(
methods
[
i
],
replacementMethods
[
i
]);
}
}
void
checkCompatibility
(
schema
::
InterfaceNode
::
Method
::
Reader
method
,
schema
::
InterfaceNode
::
Method
::
Reader
replacement
)
{
CONTEXT
(
"comparing method"
,
method
.
getName
());
auto
params
=
method
.
getParams
();
auto
replacementParams
=
replacement
.
getParams
();
if
(
replacementParams
.
size
()
>
params
.
size
())
{
replacementIsNewer
();
}
else
if
(
replacementParams
.
size
()
<
params
.
size
())
{
replacementIsOlder
();
}
uint
count
=
std
::
min
(
params
.
size
(),
replacementParams
.
size
());
for
(
uint
i
=
0
;
i
<
count
;
i
++
)
{
auto
param
=
params
[
i
];
auto
replacementParam
=
replacementParams
[
i
];
CONTEXT
(
"comparing parameter"
,
param
.
getName
());
checkCompatibility
(
param
.
getType
(),
replacementParam
.
getType
(),
NO_UPGRADE_TO_STRUCT
);
checkDefaultCompatibility
(
param
.
getDefaultValue
(),
replacementParam
.
getDefaultValue
());
}
// Before checking that the required parameter counts are equal, check if the user added new
// parameters without defaulting them, as this is the most common reason for this error and we
// can provide a nicer error message.
VALIDATE_SCHEMA
(
replacement
.
getRequiredParamCount
()
<=
count
&&
method
.
getRequiredParamCount
()
<=
count
,
"Updated method signature contains additional parameters that lack default values"
);
VALIDATE_SCHEMA
(
replacement
.
getRequiredParamCount
()
==
method
.
getRequiredParamCount
(),
"Updated method signature has different number of required parameters (parameters without "
"default values)"
);
checkCompatibility
(
method
.
getReturnType
(),
replacement
.
getReturnType
(),
ALLOW_UPGRADE_TO_STRUCT
);
}
void
checkCompatibility
(
schema
::
ConstNode
::
Reader
constNode
,
schema
::
ConstNode
::
Reader
replacement
)
{
// Who cares? These don't appear on the wire.
}
void
checkCompatibility
(
schema
::
AnnotationNode
::
Reader
annotationNode
,
schema
::
AnnotationNode
::
Reader
replacement
)
{
// Who cares? These don't appear on the wire.
}
enum
UpgradeToStructMode
{
ALLOW_UPGRADE_TO_STRUCT
,
NO_UPGRADE_TO_STRUCT
};
void
checkCompatibility
(
schema
::
Type
::
Reader
type
,
schema
::
Type
::
Reader
replacement
,
UpgradeToStructMode
upgradeToStructMode
)
{
if
(
replacement
.
getBody
().
which
()
!=
type
.
getBody
().
which
())
{
// Check for allowed "upgrade" to Data or Object.
if
(
replacement
.
getBody
().
which
()
==
schema
::
Type
::
Body
::
DATA_TYPE
&&
canUpgradeToData
(
type
))
{
replacementIsNewer
();
return
;
}
else
if
(
type
.
getBody
().
which
()
==
schema
::
Type
::
Body
::
DATA_TYPE
&&
canUpgradeToData
(
replacement
))
{
replacementIsOlder
();
return
;
}
else
if
(
replacement
.
getBody
().
which
()
==
schema
::
Type
::
Body
::
OBJECT_TYPE
&&
canUpgradeToObject
(
type
))
{
replacementIsNewer
();
return
;
}
else
if
(
type
.
getBody
().
which
()
==
schema
::
Type
::
Body
::
OBJECT_TYPE
&&
canUpgradeToObject
(
replacement
))
{
replacementIsOlder
();
return
;
}
if
(
upgradeToStructMode
==
ALLOW_UPGRADE_TO_STRUCT
)
{
if
(
type
.
getBody
().
which
()
==
schema
::
Type
::
Body
::
STRUCT_TYPE
)
{
checkUpgradeToStruct
(
replacement
,
type
.
getBody
().
getStructType
());
return
;
}
else
if
(
replacement
.
getBody
().
which
()
==
schema
::
Type
::
Body
::
STRUCT_TYPE
)
{
checkUpgradeToStruct
(
type
,
replacement
.
getBody
().
getStructType
());
return
;
}
}
FAIL_VALIDATE_SCHEMA
(
"a type was changed"
);
}
switch
(
type
.
getBody
().
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
:
case
schema
:
:
Type
::
Body
::
OBJECT_TYPE
:
return
;
case
schema
:
:
Type
::
Body
::
LIST_TYPE
:
return
checkCompatibility
(
type
.
getBody
().
getListType
(),
replacement
.
getBody
().
getListType
(),
ALLOW_UPGRADE_TO_STRUCT
);
case
schema
:
:
Type
::
Body
::
ENUM_TYPE
:
VALIDATE_SCHEMA
(
replacement
.
getBody
().
getEnumType
()
==
type
.
getBody
().
getEnumType
(),
"type changed enum type"
);
return
;
case
schema
:
:
Type
::
Body
::
STRUCT_TYPE
:
// TODO(someday): If the IDs don't match, we should compare the two structs for
// compatibility. This is tricky, though, because the new type's target may not yet be
// loaded. In that case we could take the old type, make a copy of it, assign the new
// ID to the copy, and load() that. That forces any struct type loaded for that ID to
// be compatible. However, that has another problem, which is that it could be that the
// whole reason the type was replaced was to fork that type, and so an incompatibility
// could be very much expected. This could be a rat hole...
VALIDATE_SCHEMA
(
replacement
.
getBody
().
getStructType
()
==
type
.
getBody
().
getStructType
(),
"type changed to incompatible struct type"
);
return
;
case
schema
:
:
Type
::
Body
::
INTERFACE_TYPE
:
VALIDATE_SCHEMA
(
replacement
.
getBody
().
getInterfaceType
()
==
type
.
getBody
().
getInterfaceType
(),
"type changed to incompatible interface type"
);
return
;
}
// We assume unknown types (from newer versions of Cap'n Proto?) are equivalent.
}
void
checkUpgradeToStruct
(
schema
::
Type
::
Reader
type
,
uint64_t
structTypeId
)
{
// We can't just look up the target struct and check it because it may not have been loaded
// yet. Instead, we contrive a struct that looks like what we want and load() that, which
// guarantees that any incompatibility will be caught either now or when the real version of
// that struct is loaded.
word
scratch
[
32
];
memset
(
scratch
,
0
,
sizeof
(
scratch
));
MallocMessageBuilder
builder
(
arrayPtr
(
scratch
,
sizeof
(
scratch
)));
auto
node
=
builder
.
initRoot
<
schema
::
Node
>
();
node
.
setId
(
structTypeId
);
// TODO(cleanup): str() really needs to return something NUL-terminated...
node
.
setDisplayName
(
str
(
"(unknown type used in "
,
nodeName
,
")"
,
'\0'
).
begin
());
auto
structNode
=
node
.
getBody
().
initStructNode
();
switch
(
type
.
getBody
().
which
())
{
case
schema
:
:
Type
::
Body
::
VOID_TYPE
:
structNode
.
setDataSectionWordSize
(
0
);
structNode
.
setPointerSectionSize
(
0
);
structNode
.
setPreferredListEncoding
(
schema
::
ElementSize
::
EMPTY
);
break
;
case
schema
:
:
Type
::
Body
::
BOOL_TYPE
:
structNode
.
setDataSectionWordSize
(
1
);
structNode
.
setPointerSectionSize
(
0
);
structNode
.
setPreferredListEncoding
(
schema
::
ElementSize
::
BIT
);
break
;
case
schema
:
:
Type
::
Body
::
INT8_TYPE
:
case
schema
:
:
Type
::
Body
::
UINT8_TYPE
:
structNode
.
setDataSectionWordSize
(
1
);
structNode
.
setPointerSectionSize
(
0
);
structNode
.
setPreferredListEncoding
(
schema
::
ElementSize
::
BYTE
);
break
;
case
schema
:
:
Type
::
Body
::
INT16_TYPE
:
case
schema
:
:
Type
::
Body
::
UINT16_TYPE
:
case
schema
:
:
Type
::
Body
::
ENUM_TYPE
:
structNode
.
setDataSectionWordSize
(
1
);
structNode
.
setPointerSectionSize
(
0
);
structNode
.
setPreferredListEncoding
(
schema
::
ElementSize
::
TWO_BYTES
);
break
;
case
schema
:
:
Type
::
Body
::
INT32_TYPE
:
case
schema
:
:
Type
::
Body
::
UINT32_TYPE
:
case
schema
:
:
Type
::
Body
::
FLOAT32_TYPE
:
structNode
.
setDataSectionWordSize
(
1
);
structNode
.
setPointerSectionSize
(
0
);
structNode
.
setPreferredListEncoding
(
schema
::
ElementSize
::
FOUR_BYTES
);
break
;
case
schema
:
:
Type
::
Body
::
INT64_TYPE
:
case
schema
:
:
Type
::
Body
::
UINT64_TYPE
:
case
schema
:
:
Type
::
Body
::
FLOAT64_TYPE
:
structNode
.
setDataSectionWordSize
(
1
);
structNode
.
setPointerSectionSize
(
0
);
structNode
.
setPreferredListEncoding
(
schema
::
ElementSize
::
EIGHT_BYTES
);
break
;
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
::
INTERFACE_TYPE
:
case
schema
:
:
Type
::
Body
::
OBJECT_TYPE
:
structNode
.
setDataSectionWordSize
(
0
);
structNode
.
setPointerSectionSize
(
1
);
structNode
.
setPreferredListEncoding
(
schema
::
ElementSize
::
POINTER
);
break
;
}
auto
member
=
structNode
.
initMembers
(
1
)[
0
];
member
.
setName
(
"member0"
);
member
.
setOrdinal
(
0
);
member
.
setCodeOrder
(
0
);
member
.
getBody
().
initFieldMember
().
setType
(
type
);
loader
.
load
(
node
);
}
bool
canUpgradeToData
(
schema
::
Type
::
Reader
type
)
{
if
(
type
.
getBody
().
which
()
==
schema
::
Type
::
Body
::
TEXT_TYPE
)
{
return
true
;
}
else
if
(
type
.
getBody
().
which
()
==
schema
::
Type
::
Body
::
LIST_TYPE
)
{
switch
(
type
.
getBody
().
getListType
().
getBody
().
which
())
{
case
schema
:
:
Type
::
Body
::
INT8_TYPE
:
case
schema
:
:
Type
::
Body
::
UINT8_TYPE
:
return
true
;
default:
return
false
;
}
}
else
{
return
false
;
}
}
bool
canUpgradeToObject
(
schema
::
Type
::
Reader
type
)
{
switch
(
type
.
getBody
().
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
::
ENUM_TYPE
:
return
false
;
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
::
INTERFACE_TYPE
:
case
schema
:
:
Type
::
Body
::
OBJECT_TYPE
:
return
true
;
}
// Be lenient with unknown types.
return
true
;
}
void
checkDefaultCompatibility
(
schema
::
Value
::
Reader
value
,
schema
::
Value
::
Reader
replacement
)
{
// Note that we test default compatibility only after testing type compatibility, and default
// values have already been validated as matching their types, so this should pass.
RECOVERABLE_CHECK
(
value
.
getBody
().
which
()
==
replacement
.
getBody
().
which
())
{
compatibility
=
INCOMPATIBLE
;
return
;
}
switch
(
value
.
getBody
().
which
())
{
#define HANDLE_TYPE(discrim, name) \
case schema::Value::Body::discrim##_VALUE: \
VALIDATE_SCHEMA(value.getBody().get##name##Value() == \
replacement.getBody().get##name##Value(), \
"default value changed"); \
break;
HANDLE_TYPE
(
VOID
,
Void
);
HANDLE_TYPE
(
BOOL
,
Bool
);
HANDLE_TYPE
(
INT8
,
Int8
);
HANDLE_TYPE
(
INT16
,
Int16
);
HANDLE_TYPE
(
INT32
,
Int32
);
HANDLE_TYPE
(
INT64
,
Int64
);
HANDLE_TYPE
(
UINT8
,
Uint8
);
HANDLE_TYPE
(
UINT16
,
Uint16
);
HANDLE_TYPE
(
UINT32
,
Uint32
);
HANDLE_TYPE
(
UINT64
,
Uint64
);
HANDLE_TYPE
(
FLOAT32
,
Float32
);
HANDLE_TYPE
(
FLOAT64
,
Float64
);
HANDLE_TYPE
(
ENUM
,
Enum
);
#undef HANDLE_TYPE
case
schema
:
:
Value
::
Body
::
TEXT_VALUE
:
case
schema
:
:
Value
::
Body
::
DATA_VALUE
:
case
schema
:
:
Value
::
Body
::
LIST_VALUE
:
case
schema
:
:
Value
::
Body
::
STRUCT_VALUE
:
case
schema
:
:
Value
::
Body
::
INTERFACE_VALUE
:
case
schema
:
:
Value
::
Body
::
OBJECT_VALUE
:
// It's not a big deal if default values for pointers change, and it would be difficult for
// us to compare these defaults here, so just let it slide.
break
;
}
}
};
// =======================================================================================
SchemaLoader
::
Impl
::
Impl
()
:
arena
(
allocator
.
arena
()),
segment
(
allocator
.
getRootSegment
())
{}
internal
::
RawSchema
*
SchemaLoader
::
Impl
::
load
(
schema
::
Node
::
Reader
reader
)
{
// Make a copy of the node which can be used unchecked.
size_t
size
=
reader
.
totalSizeInWords
()
+
1
;
word
*
validated
=
allocate
<
word
>
(
size
);
copyToUnchecked
(
reader
,
arrayPtr
(
validated
,
size
));
// Validate the copy.
Validator
validator
(
*
this
);
auto
validatedReader
=
readMessageUnchecked
<
schema
::
Node
>
(
validated
);
if
(
!
validator
.
validate
(
validatedReader
))
{
// Not valid. Construct an empty schema of the same type and return that.
return
loadEmpty
(
validatedReader
.
getId
(),
validatedReader
.
getDisplayName
(),
validatedReader
.
getBody
().
which
());
}
// Check if we already have a schema for this ID.
internal
::
RawSchema
*&
slot
=
schemas
[
validatedReader
.
getId
()];
if
(
slot
==
nullptr
)
{
// Nope, allocate a new RawSchema.
slot
=
allocate
<
internal
::
RawSchema
>
();
}
else
{
// Yes, check if it is compatible and figure out which schema is newer.
auto
existing
=
readMessageUnchecked
<
schema
::
Node
>
(
slot
->
encodedNode
);
CompatibilityChecker
checker
(
*
this
);
if
(
!
checker
.
shouldReplace
(
existing
,
validatedReader
,
false
))
{
// The new schema does not appear to be any newer than the existing one, so keep the existing.
return
slot
;
}
}
// Initialize the RawSchema.
slot
->
encodedNode
=
validated
;
slot
->
dependencies
=
validator
.
makeDependencyArray
(
&
slot
->
dependencyCount
);
slot
->
membersByName
=
validator
.
makeMemberInfoArray
(
&
slot
->
memberCount
);
return
slot
;
}
internal
::
RawSchema
*
SchemaLoader
::
Impl
::
loadNative
(
const
internal
::
RawSchema
*
nativeSchema
)
{
auto
reader
=
readMessageUnchecked
<
schema
::
Node
>
(
nativeSchema
->
encodedNode
);
internal
::
RawSchema
*&
slot
=
schemas
[
reader
.
getId
()];
if
(
slot
==
nullptr
)
{
slot
=
allocate
<
internal
::
RawSchema
>
();
}
else
if
(
slot
->
canCastTo
!=
nullptr
)
{
PRECOND
(
slot
->
canCastTo
==
nativeSchema
,
"two different compiled-in type have the same type ID"
,
reader
.
getId
(),
reader
.
getDisplayName
(),
readMessageUnchecked
<
schema
::
Node
>
(
slot
->
canCastTo
->
encodedNode
).
getDisplayName
());
// Already loaded.
return
slot
;
}
else
{
auto
existing
=
readMessageUnchecked
<
schema
::
Node
>
(
slot
->
encodedNode
);
auto
native
=
readMessageUnchecked
<
schema
::
Node
>
(
nativeSchema
->
encodedNode
);
CompatibilityChecker
checker
(
*
this
);
if
(
!
checker
.
shouldReplace
(
existing
,
native
,
true
))
{
// The existing schema is newer, so just make sure the dependencies are loaded.
slot
->
canCastTo
=
nativeSchema
;
for
(
uint
i
=
0
;
i
<
nativeSchema
->
dependencyCount
;
i
++
)
{
loadNative
(
nativeSchema
->
dependencies
[
i
]);
}
return
slot
;
}
}
// Set the slot to a copy of the native schema.
*
slot
=
*
nativeSchema
;
// Indicate that casting is safe.
slot
->
canCastTo
=
nativeSchema
;
// Except that we need to set the dependency list to point at other loader-owned RawSchemas.
const
internal
::
RawSchema
**
dependencies
=
allocate
<
const
internal
::
RawSchema
*>
(
slot
->
dependencyCount
);
for
(
uint
i
=
0
;
i
<
nativeSchema
->
dependencyCount
;
i
++
)
{
dependencies
[
i
]
=
loadNative
(
nativeSchema
->
dependencies
[
i
]);
}
slot
->
dependencies
=
dependencies
;
return
slot
;
}
internal
::
RawSchema
*
SchemaLoader
::
Impl
::
loadEmpty
(
uint64_t
id
,
Text
::
Reader
name
,
schema
::
Node
::
Body
::
Which
kind
)
{
word
scratch
[
32
];
memset
(
scratch
,
0
,
sizeof
(
scratch
));
MallocMessageBuilder
builder
(
arrayPtr
(
scratch
,
sizeof
(
scratch
)));
auto
node
=
builder
.
initRoot
<
schema
::
Node
>
();
node
.
setId
(
id
);
node
.
setDisplayName
(
name
);
switch
(
kind
)
{
case
schema
:
:
Node
::
Body
::
STRUCT_NODE
:
node
.
getBody
().
initStructNode
();
break
;
case
schema
:
:
Node
::
Body
::
ENUM_NODE
:
node
.
getBody
().
initEnumNode
();
break
;
case
schema
:
:
Node
::
Body
::
INTERFACE_NODE
:
node
.
getBody
().
initInterfaceNode
();
break
;
case
schema
:
:
Node
::
Body
::
FILE_NODE
:
case
schema
:
:
Node
::
Body
::
CONST_NODE
:
case
schema
:
:
Node
::
Body
::
ANNOTATION_NODE
:
FAIL_PRECOND
(
"Not a type."
);
break
;
}
return
load
(
node
);
}
internal
::
RawSchema
*
SchemaLoader
::
Impl
::
tryGet
(
uint64_t
typeId
)
const
{
auto
iter
=
schemas
.
find
(
typeId
);
if
(
iter
==
schemas
.
end
())
{
return
nullptr
;
}
else
{
return
iter
->
second
;
}
}
Array
<
Schema
>
SchemaLoader
::
Impl
::
getAllLoaded
()
const
{
Array
<
Schema
>
result
=
newArray
<
Schema
>
(
schemas
.
size
());
size_t
i
=
0
;
for
(
auto
&
schema
:
schemas
)
{
result
[
i
++
]
=
Schema
(
schema
.
second
);
}
return
result
;
}
// =======================================================================================
SchemaLoader
::
SchemaLoader
()
:
impl
(
heap
<
Impl
>
())
{}
SchemaLoader
::~
SchemaLoader
()
{}
Schema
SchemaLoader
::
get
(
uint64_t
id
)
const
{
internal
::
RawSchema
*
raw
=
impl
->
tryGet
(
id
);
PRECOND
(
raw
!=
nullptr
,
"no schema node loaded for id"
,
id
);
return
Schema
(
raw
);
}
Maybe
<
Schema
>
SchemaLoader
::
tryGet
(
uint64_t
id
)
const
{
internal
::
RawSchema
*
raw
=
impl
->
tryGet
(
id
);
if
(
raw
==
nullptr
)
{
return
nullptr
;
}
else
{
return
Schema
(
raw
);
}
}
Schema
SchemaLoader
::
load
(
schema
::
Node
::
Reader
reader
)
{
return
Schema
(
impl
->
load
(
reader
));
}
Array
<
Schema
>
SchemaLoader
::
getAllLoaded
()
const
{
return
impl
->
getAllLoaded
();
}
void
SchemaLoader
::
loadNative
(
const
internal
::
RawSchema
*
nativeSchema
)
{
impl
->
loadNative
(
nativeSchema
);
}
}
// namespace capnproto
c++/src/capnproto/schema-loader.h
0 → 100644
View file @
d7f3123c
// 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_SCHEMA_LOADER_H_
#define CAPNPROTO_SCHEMA_LOADER_H_
#include "schema.h"
namespace
capnproto
{
class
SchemaLoader
{
public
:
SchemaLoader
();
~
SchemaLoader
();
CAPNPROTO_DISALLOW_COPY
(
SchemaLoader
);
Schema
get
(
uint64_t
id
)
const
;
// Gets the schema for the given ID, throwing an exception if it isn't present.
//
// The returned schema may be invalidated if load() is called with a new schema for the same ID.
// In general, you should not call load() while a schema from this loader is in-use.
Maybe
<
Schema
>
tryGet
(
uint64_t
id
)
const
;
// Like get() but doesn't throw.
Schema
load
(
schema
::
Node
::
Reader
reader
);
// Loads the given schema node. Validates the node and throws an exception if invalid. This
// makes a copy of the schema, so the object passed in can be destroyed after this returns.
//
// If the node has any dependencies which are not already loaded, they will be initialized as
// stubs -- empty schemas of whichever kind is expected.
//
// If another schema for the given reader has already been seen, the loader will inspect both
// schemas to determine which one is newer, and use that that one. If the two versions are
// found to be incompatible, an exception is thrown. If the two versions differ but are
// compatible and the loader cannot determine which is newer (e.g., the only changes are renames),
// the existing schema will be preferred. Note that in any case, the loader will end up keeping
// around copies of both schemas, so you shouldn't repeatedly reload schemas into the same loader.
//
// The following properties of the schema node are validated:
// - Struct size and preferred list encoding are valid and consistent.
// - Struct members are fields or unions.
// - Union members are fields.
// - Field offsets are in-bounds.
// - Ordinals and codeOrders are sequential starting from zero.
// - Values are of the right union case to match their types.
//
// You should assume anything not listed above is NOT validated. In particular, things that are
// not validated now, but could be in the future, include but are not limited to:
// - Names.
// - Annotation values. (This is hard because the annotation declaration is not always
// available.)
// - Content of default/constant values of pointer type. (Validating these would require knowing
// their schema, but even if the schemas are available at validation time, they could be
// updated by a subsequent load(), invalidating existing values. Instead, these values are
// validated at the time they are used, as usual for Cap'n Proto objects.)
//
// Also note that unknown types are not considered invalid. Instead, the dynamic API returns
// a DynamicValue with type UNKNOWN for these.
template
<
typename
T
>
void
loadCompiledTypeAndDependencies
();
// Load the schema for the given compiled-in type and all of its dependencies.
//
// If you want to be able to cast a DynamicValue built from this SchemaLoader to the compiled-in
// type using as<T>(), you must call this method before constructing the DynamicValue. Otherwise,
// as<T>() will throw an exception complaining about type mismatch.
Array
<
Schema
>
getAllLoaded
()
const
;
// Get a complete list of all loaded schema nodes. It is particularly useful to call this after
// loadCompiledTypeAndDependencies<T>() in order to get a flat list of all of T's transitive
// dependencies.
private
:
class
Validator
;
class
CompatibilityChecker
;
class
Impl
;
Own
<
Impl
>
impl
;
void
loadNative
(
const
internal
::
RawSchema
*
nativeSchema
);
};
template
<
typename
T
>
inline
void
SchemaLoader
::
loadCompiledTypeAndDependencies
()
{
loadNative
(
&
internal
::
rawSchema
<
T
>
());
}
}
// namespace capnproto
#endif // CAPNPROTO_SCHEMA_LOADER_H_
c++/src/capnproto/schema.c++
View file @
d7f3123c
...
...
@@ -76,6 +76,12 @@ InterfaceSchema Schema::asInterface() const {
return
InterfaceSchema
(
raw
);
}
void
Schema
::
requireUsableAs
(
const
internal
::
RawSchema
*
expected
)
{
PRECOND
(
raw
==
expected
||
(
raw
!=
nullptr
&&
expected
!=
nullptr
&&
raw
->
canCastTo
==
expected
),
"This schema is not compatible with the requested native type."
);
}
// =======================================================================================
namespace
{
...
...
@@ -285,4 +291,10 @@ ListSchema ListSchema::getListElementType() const {
return
ListSchema
(
elementType
,
nestingDepth
-
1
,
elementSchema
);
}
void
ListSchema
::
requireUsableAs
(
ListSchema
expected
)
{
PRECOND
(
elementType
==
expected
.
elementType
&&
nestingDepth
==
expected
.
nestingDepth
,
"This schema is not compatible with the requested native type."
);
elementSchema
.
requireUsableAs
(
expected
.
elementSchema
.
raw
);
}
}
// namespace capnproto
c++/src/capnproto/schema.h
View file @
d7f3123c
...
...
@@ -75,6 +75,14 @@ public:
// you want to check if two Schemas represent the same type (but possibly different versions of
// it), compare their IDs instead.
template
<
typename
T
>
void
requireUsableAs
();
// Throws an exception if a value with this Schema cannot safely be cast to a native value of
// the given type. This passes if either:
// - *this == from<T>()
// - This schema was loaded with SchemaLoader, the type ID matches typeId<T>(), and
// loadCompiledTypeAndDependencies<T>() was called on the SchemaLoader.
private
:
const
internal
::
RawSchema
*
raw
;
...
...
@@ -84,9 +92,13 @@ private:
return
Schema
(
&
internal
::
rawSchema
<
T
>
());
}
void
requireUsableAs
(
const
internal
::
RawSchema
*
expected
);
friend
class
StructSchema
;
friend
class
EnumSchema
;
friend
class
InterfaceSchema
;
friend
class
ListSchema
;
friend
class
SchemaLoader
;
};
// -------------------------------------------------------------------
...
...
@@ -356,6 +368,9 @@ public:
inline
bool
operator
==
(
const
ListSchema
&
other
)
const
;
inline
bool
operator
!=
(
const
ListSchema
&
other
)
const
{
return
!
(
*
this
==
other
);
}
template
<
typename
T
>
void
requireUsableAs
();
private
:
schema
::
Type
::
Body
::
Which
elementType
;
uint8_t
nestingDepth
;
// 0 for T, 1 for List(T), 2 for List(List(T)), ...
...
...
@@ -375,6 +390,8 @@ private:
return
FromImpl
<
T
>::
get
();
}
void
requireUsableAs
(
ListSchema
expected
);
friend
class
Schema
;
};
...
...
@@ -396,6 +413,11 @@ template <> inline schema::Type::Body::Which Schema::from<double>() { return sch
template
<>
inline
schema
::
Type
::
Body
::
Which
Schema
::
from
<
Text
>
()
{
return
schema
::
Type
::
Body
::
TEXT_TYPE
;
}
template
<>
inline
schema
::
Type
::
Body
::
Which
Schema
::
from
<
Data
>
()
{
return
schema
::
Type
::
Body
::
DATA_TYPE
;
}
template
<
typename
T
>
inline
void
Schema
::
requireUsableAs
()
{
requireUsableAs
(
&
internal
::
rawSchema
<
T
>
());
}
inline
bool
StructSchema
::
Member
::
operator
==
(
const
Member
&
other
)
const
{
return
parent
==
other
.
parent
&&
unionIndex
==
other
.
unionIndex
&&
index
==
other
.
index
;
}
...
...
@@ -429,6 +451,13 @@ inline bool ListSchema::operator==(const ListSchema& other) const {
elementSchema
==
other
.
elementSchema
;
}
template
<
typename
T
>
inline
void
ListSchema
::
requireUsableAs
()
{
static_assert
(
kind
<
T
>
()
==
Kind
::
LIST
,
"ListSchema::requireUsableAs<T>() requires T is a list type."
);
requireUsableAs
(
Schema
::
from
<
T
>
());
}
template
<
typename
T
>
struct
ListSchema
::
FromImpl
<
List
<
T
>>
{
static
inline
ListSchema
get
()
{
return
of
(
Schema
::
from
<
T
>
());
}
...
...
c++/src/capnproto/test-util.c++
View file @
d7f3123c
...
...
@@ -122,6 +122,98 @@ void genericInitTestMessage(Builder builder) {
builder
.
setEnumList
({
TestEnum
::
FOO
,
TestEnum
::
GARPLY
});
}
void
dynamicInitTestMessage
(
DynamicStruct
::
Builder
builder
)
{
builder
.
set
(
"voidField"
,
Void
::
VOID
);
builder
.
set
(
"boolField"
,
true
);
builder
.
set
(
"int8Field"
,
-
123
);
builder
.
set
(
"int16Field"
,
-
12345
);
builder
.
set
(
"int32Field"
,
-
12345678
);
builder
.
set
(
"int64Field"
,
-
123456789012345ll
);
builder
.
set
(
"uInt8Field"
,
234u
);
builder
.
set
(
"uInt16Field"
,
45678u
);
builder
.
set
(
"uInt32Field"
,
3456789012u
);
builder
.
set
(
"uInt64Field"
,
12345678901234567890ull
);
builder
.
set
(
"float32Field"
,
1234.5
);
builder
.
set
(
"float64Field"
,
-
123e45
);
builder
.
set
(
"textField"
,
"foo"
);
builder
.
set
(
"dataField"
,
"bar"
);
{
auto
subBuilder
=
builder
.
init
(
"structField"
).
as
<
DynamicStruct
>
();
subBuilder
.
set
(
"voidField"
,
Void
::
VOID
);
subBuilder
.
set
(
"boolField"
,
true
);
subBuilder
.
set
(
"int8Field"
,
-
12
);
subBuilder
.
set
(
"int16Field"
,
3456
);
subBuilder
.
set
(
"int32Field"
,
-
78901234
);
subBuilder
.
set
(
"int64Field"
,
56789012345678ll
);
subBuilder
.
set
(
"uInt8Field"
,
90u
);
subBuilder
.
set
(
"uInt16Field"
,
1234u
);
subBuilder
.
set
(
"uInt32Field"
,
56789012u
);
subBuilder
.
set
(
"uInt64Field"
,
345678901234567890ull
);
subBuilder
.
set
(
"float32Field"
,
-
1.25e-10
);
subBuilder
.
set
(
"float64Field"
,
345
);
subBuilder
.
set
(
"textField"
,
"baz"
);
subBuilder
.
set
(
"dataField"
,
"qux"
);
{
auto
subSubBuilder
=
subBuilder
.
init
(
"structField"
).
as
<
DynamicStruct
>
();
subSubBuilder
.
set
(
"textField"
,
"nested"
);
subSubBuilder
.
init
(
"structField"
).
as
<
DynamicStruct
>
().
set
(
"textField"
,
"really nested"
);
}
subBuilder
.
set
(
"enumField"
,
"baz"
);
subBuilder
.
set
(
"voidList"
,
{
Void
::
VOID
,
Void
::
VOID
,
Void
::
VOID
});
subBuilder
.
set
(
"boolList"
,
{
false
,
true
,
false
,
true
,
true
});
subBuilder
.
set
(
"int8List"
,
{
12
,
-
34
,
-
0x80
,
0x7f
});
subBuilder
.
set
(
"int16List"
,
{
1234
,
-
5678
,
-
0x8000
,
0x7fff
});
// 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
});
subBuilder
.
set
(
"uInt32List"
,
{
12345678u
,
90123456u
,
0u
,
0xffffffffu
});
subBuilder
.
set
(
"uInt64List"
,
{
123456789012345ull
,
678901234567890ull
,
0ull
,
0xffffffffffffffffull
});
subBuilder
.
set
(
"float32List"
,
{
0
,
1234567
,
1e37
,
-
1e37
,
1e-37
,
-
1e-37
});
subBuilder
.
set
(
"float64List"
,
{
0
,
123456789012345
,
1e306
,
-
1e306
,
1e-306
,
-
1e-306
});
subBuilder
.
set
(
"textList"
,
{
"quux"
,
"corge"
,
"grault"
});
subBuilder
.
set
(
"dataList"
,
{
"garply"
,
"waldo"
,
"fred"
});
{
auto
listBuilder
=
subBuilder
.
init
(
"structList"
,
3
).
as
<
DynamicList
>
();
listBuilder
[
0
].
as
<
DynamicStruct
>
().
set
(
"textField"
,
"x structlist 1"
);
listBuilder
[
1
].
as
<
DynamicStruct
>
().
set
(
"textField"
,
"x structlist 2"
);
listBuilder
[
2
].
as
<
DynamicStruct
>
().
set
(
"textField"
,
"x structlist 3"
);
}
subBuilder
.
set
(
"enumList"
,
{
"qux"
,
"bar"
,
"grault"
});
}
builder
.
set
(
"enumField"
,
"corge"
);
builder
.
init
(
"voidList"
,
6
);
builder
.
set
(
"boolList"
,
{
true
,
false
,
false
,
true
});
builder
.
set
(
"int8List"
,
{
111
,
-
111
});
builder
.
set
(
"int16List"
,
{
11111
,
-
11111
});
builder
.
set
(
"int32List"
,
{
111111111
,
-
111111111
});
builder
.
set
(
"int64List"
,
{
1111111111111111111ll
,
-
1111111111111111111ll
});
builder
.
set
(
"uInt8List"
,
{
111u
,
222u
});
builder
.
set
(
"uInt16List"
,
{
33333u
,
44444u
});
builder
.
set
(
"uInt32List"
,
{
3333333333u
});
builder
.
set
(
"uInt64List"
,
{
11111111111111111111ull
});
builder
.
set
(
"float32List"
,
{
5555.5
,
std
::
numeric_limits
<
float
>::
infinity
(),
-
std
::
numeric_limits
<
float
>::
infinity
(),
std
::
numeric_limits
<
float
>::
quiet_NaN
()});
builder
.
set
(
"float64List"
,
{
7777.75
,
std
::
numeric_limits
<
double
>::
infinity
(),
-
std
::
numeric_limits
<
double
>::
infinity
(),
std
::
numeric_limits
<
double
>::
quiet_NaN
()});
builder
.
set
(
"textList"
,
{
"plugh"
,
"xyzzy"
,
"thud"
});
builder
.
set
(
"dataList"
,
{
"oops"
,
"exhausted"
,
"rfc3092"
});
{
auto
listBuilder
=
builder
.
init
(
"structList"
,
3
).
as
<
DynamicList
>
();
listBuilder
[
0
].
as
<
DynamicStruct
>
().
set
(
"textField"
,
"structlist 1"
);
listBuilder
[
1
].
as
<
DynamicStruct
>
().
set
(
"textField"
,
"structlist 2"
);
listBuilder
[
2
].
as
<
DynamicStruct
>
().
set
(
"textField"
,
"structlist 3"
);
}
builder
.
set
(
"enumList"
,
{
"foo"
,
"garply"
});
}
template
<
typename
T
,
typename
U
>
void
checkList
(
T
reader
,
std
::
initializer_list
<
U
>
expected
)
{
ASSERT_EQ
(
expected
.
size
(),
reader
.
size
());
...
...
@@ -252,6 +344,148 @@ void genericCheckTestMessage(Reader reader) {
checkList
(
reader
.
getEnumList
(),
{
TestEnum
::
FOO
,
TestEnum
::
GARPLY
});
}
// Hack because as<>() is a template-parameter-dependent lookup everywhere below...
#define as template as
template
<
typename
T
>
void
expectPrimitiveEq
(
T
a
,
T
b
)
{
EXPECT_EQ
(
a
,
b
);
}
void
expectPrimitiveEq
(
float
a
,
float
b
)
{
EXPECT_FLOAT_EQ
(
a
,
b
);
}
void
expectPrimitiveEq
(
double
a
,
double
b
)
{
EXPECT_DOUBLE_EQ
(
a
,
b
);
}
void
expectPrimitiveEq
(
Text
::
Reader
a
,
Text
::
Builder
b
)
{
EXPECT_EQ
(
a
,
b
);
}
void
expectPrimitiveEq
(
Data
::
Reader
a
,
Data
::
Builder
b
)
{
EXPECT_EQ
(
a
,
b
);
}
template
<
typename
Element
,
typename
T
>
void
checkList
(
T
reader
,
std
::
initializer_list
<
ReaderFor
<
Element
>>
expected
)
{
auto
list
=
reader
.
as
<
DynamicList
>
();
ASSERT_EQ
(
expected
.
size
(),
list
.
size
());
for
(
uint
i
=
0
;
i
<
expected
.
size
();
i
++
)
{
expectPrimitiveEq
(
expected
.
begin
()[
i
],
list
[
i
].
as
<
Element
>
());
}
auto
typed
=
reader
.
as
<
List
<
Element
>>
();
ASSERT_EQ
(
expected
.
size
(),
typed
.
size
());
for
(
uint
i
=
0
;
i
<
expected
.
size
();
i
++
)
{
expectPrimitiveEq
(
expected
.
begin
()[
i
],
typed
[
i
]);
}
}
template
<
typename
T
>
void
checkEnumList
(
T
reader
,
std
::
initializer_list
<
const
char
*>
expected
)
{
auto
list
=
reader
.
as
<
DynamicList
>
();
ASSERT_EQ
(
expected
.
size
(),
list
.
size
());
for
(
uint
i
=
0
;
i
<
expected
.
size
();
i
++
)
{
EXPECT_EQ
(
expected
.
begin
()[
i
],
list
[
i
].
as
<
DynamicEnum
>
().
getEnumerant
()
->
getProto
().
getName
());
}
}
template
<
typename
Reader
>
void
dynamicCheckTestMessage
(
Reader
reader
)
{
EXPECT_EQ
(
Void
::
VOID
,
reader
.
get
(
"voidField"
).
as
<
Void
>
());
EXPECT_EQ
(
true
,
reader
.
get
(
"boolField"
).
as
<
bool
>
());
EXPECT_EQ
(
-
123
,
reader
.
get
(
"int8Field"
).
as
<
int8_t
>
());
EXPECT_EQ
(
-
12345
,
reader
.
get
(
"int16Field"
).
as
<
int16_t
>
());
EXPECT_EQ
(
-
12345678
,
reader
.
get
(
"int32Field"
).
as
<
int32_t
>
());
EXPECT_EQ
(
-
123456789012345ll
,
reader
.
get
(
"int64Field"
).
as
<
int64_t
>
());
EXPECT_EQ
(
234u
,
reader
.
get
(
"uInt8Field"
).
as
<
uint8_t
>
());
EXPECT_EQ
(
45678u
,
reader
.
get
(
"uInt16Field"
).
as
<
uint16_t
>
());
EXPECT_EQ
(
3456789012u
,
reader
.
get
(
"uInt32Field"
).
as
<
uint32_t
>
());
EXPECT_EQ
(
12345678901234567890ull
,
reader
.
get
(
"uInt64Field"
).
as
<
uint64_t
>
());
EXPECT_FLOAT_EQ
(
1234.5
f
,
reader
.
get
(
"float32Field"
).
as
<
float
>
());
EXPECT_DOUBLE_EQ
(
-
123e45
,
reader
.
get
(
"float64Field"
).
as
<
double
>
());
EXPECT_EQ
(
"foo"
,
reader
.
get
(
"textField"
).
as
<
Text
>
());
EXPECT_EQ
(
"bar"
,
reader
.
get
(
"dataField"
).
as
<
Data
>
());
{
auto
subReader
=
reader
.
get
(
"structField"
).
as
<
DynamicStruct
>
();
EXPECT_EQ
(
Void
::
VOID
,
subReader
.
get
(
"voidField"
).
as
<
Void
>
());
EXPECT_EQ
(
true
,
subReader
.
get
(
"boolField"
).
as
<
bool
>
());
EXPECT_EQ
(
-
12
,
subReader
.
get
(
"int8Field"
).
as
<
int8_t
>
());
EXPECT_EQ
(
3456
,
subReader
.
get
(
"int16Field"
).
as
<
int16_t
>
());
EXPECT_EQ
(
-
78901234
,
subReader
.
get
(
"int32Field"
).
as
<
int32_t
>
());
EXPECT_EQ
(
56789012345678ll
,
subReader
.
get
(
"int64Field"
).
as
<
int64_t
>
());
EXPECT_EQ
(
90u
,
subReader
.
get
(
"uInt8Field"
).
as
<
uint8_t
>
());
EXPECT_EQ
(
1234u
,
subReader
.
get
(
"uInt16Field"
).
as
<
uint16_t
>
());
EXPECT_EQ
(
56789012u
,
subReader
.
get
(
"uInt32Field"
).
as
<
uint32_t
>
());
EXPECT_EQ
(
345678901234567890ull
,
subReader
.
get
(
"uInt64Field"
).
as
<
uint64_t
>
());
EXPECT_FLOAT_EQ
(
-
1.25e-10
f
,
subReader
.
get
(
"float32Field"
).
as
<
float
>
());
EXPECT_DOUBLE_EQ
(
345
,
subReader
.
get
(
"float64Field"
).
as
<
double
>
());
EXPECT_EQ
(
"baz"
,
subReader
.
get
(
"textField"
).
as
<
Text
>
());
EXPECT_EQ
(
"qux"
,
subReader
.
get
(
"dataField"
).
as
<
Data
>
());
{
auto
subSubReader
=
subReader
.
get
(
"structField"
).
as
<
DynamicStruct
>
();
EXPECT_EQ
(
"nested"
,
subSubReader
.
get
(
"textField"
).
as
<
Text
>
());
EXPECT_EQ
(
"really nested"
,
subSubReader
.
get
(
"structField"
).
as
<
DynamicStruct
>
()
.
get
(
"textField"
).
as
<
Text
>
());
}
EXPECT_EQ
(
"baz"
,
subReader
.
get
(
"enumField"
).
as
<
DynamicEnum
>
().
getEnumerant
()
->
getProto
().
getName
());
checkList
<
Void
>
(
subReader
.
get
(
"voidList"
),
{
Void
::
VOID
,
Void
::
VOID
,
Void
::
VOID
});
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
});
// 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
});
checkList
<
uint32_t
>
(
subReader
.
get
(
"uInt32List"
),
{
12345678u
,
90123456u
,
0u
,
0xffffffffu
});
checkList
<
uint64_t
>
(
subReader
.
get
(
"uInt64List"
),
{
123456789012345ull
,
678901234567890ull
,
0ull
,
0xffffffffffffffffull
});
checkList
<
float
>
(
subReader
.
get
(
"float32List"
),
{
0.0
f
,
1234567.0
f
,
1e37
f
,
-
1e37
f
,
1e-37
f
,
-
1e-37
f
});
checkList
<
double
>
(
subReader
.
get
(
"float64List"
),
{
0.0
,
123456789012345.0
,
1e306
,
-
1e306
,
1e-306
,
-
1e-306
});
checkList
<
Text
>
(
subReader
.
get
(
"textList"
),
{
"quux"
,
"corge"
,
"grault"
});
checkList
<
Data
>
(
subReader
.
get
(
"dataList"
),
{
"garply"
,
"waldo"
,
"fred"
});
{
auto
listReader
=
subReader
.
get
(
"structList"
).
as
<
DynamicList
>
();
ASSERT_EQ
(
3u
,
listReader
.
size
());
EXPECT_EQ
(
"x structlist 1"
,
listReader
[
0
].
as
<
DynamicStruct
>
().
get
(
"textField"
).
as
<
Text
>
());
EXPECT_EQ
(
"x structlist 2"
,
listReader
[
1
].
as
<
DynamicStruct
>
().
get
(
"textField"
).
as
<
Text
>
());
EXPECT_EQ
(
"x structlist 3"
,
listReader
[
2
].
as
<
DynamicStruct
>
().
get
(
"textField"
).
as
<
Text
>
());
}
checkEnumList
(
subReader
.
get
(
"enumList"
),
{
"qux"
,
"bar"
,
"grault"
});
}
EXPECT_EQ
(
"corge"
,
reader
.
get
(
"enumField"
).
as
<
DynamicEnum
>
().
getEnumerant
()
->
getProto
().
getName
());
EXPECT_EQ
(
6u
,
reader
.
get
(
"voidList"
).
as
<
DynamicList
>
().
size
());
checkList
<
bool
>
(
reader
.
get
(
"boolList"
),
{
true
,
false
,
false
,
true
});
checkList
<
int8_t
>
(
reader
.
get
(
"int8List"
),
{
111
,
-
111
});
checkList
<
int16_t
>
(
reader
.
get
(
"int16List"
),
{
11111
,
-
11111
});
checkList
<
int32_t
>
(
reader
.
get
(
"int32List"
),
{
111111111
,
-
111111111
});
checkList
<
int64_t
>
(
reader
.
get
(
"int64List"
),
{
1111111111111111111ll
,
-
1111111111111111111ll
});
checkList
<
uint8_t
>
(
reader
.
get
(
"uInt8List"
),
{
111u
,
222u
});
checkList
<
uint16_t
>
(
reader
.
get
(
"uInt16List"
),
{
33333u
,
44444u
});
checkList
<
uint32_t
>
(
reader
.
get
(
"uInt32List"
),
{
3333333333u
});
checkList
<
uint64_t
>
(
reader
.
get
(
"uInt64List"
),
{
11111111111111111111ull
});
{
auto
listReader
=
reader
.
get
(
"float32List"
).
as
<
DynamicList
>
();
ASSERT_EQ
(
4u
,
listReader
.
size
());
EXPECT_EQ
(
5555.5
f
,
listReader
[
0
].
as
<
float
>
());
EXPECT_EQ
(
std
::
numeric_limits
<
float
>::
infinity
(),
listReader
[
1
].
as
<
float
>
());
EXPECT_EQ
(
-
std
::
numeric_limits
<
float
>::
infinity
(),
listReader
[
2
].
as
<
float
>
());
EXPECT_TRUE
(
isNaN
(
listReader
[
3
].
as
<
float
>
()));
}
{
auto
listReader
=
reader
.
get
(
"float64List"
).
as
<
DynamicList
>
();
ASSERT_EQ
(
4u
,
listReader
.
size
());
EXPECT_EQ
(
7777.75
,
listReader
[
0
].
as
<
double
>
());
EXPECT_EQ
(
std
::
numeric_limits
<
double
>::
infinity
(),
listReader
[
1
].
as
<
double
>
());
EXPECT_EQ
(
-
std
::
numeric_limits
<
double
>::
infinity
(),
listReader
[
2
].
as
<
double
>
());
EXPECT_TRUE
(
isNaN
(
listReader
[
3
].
as
<
double
>
()));
}
checkList
<
Text
>
(
reader
.
get
(
"textList"
),
{
"plugh"
,
"xyzzy"
,
"thud"
});
checkList
<
Data
>
(
reader
.
get
(
"dataList"
),
{
"oops"
,
"exhausted"
,
"rfc3092"
});
{
auto
listReader
=
reader
.
get
(
"structList"
).
as
<
DynamicList
>
();
ASSERT_EQ
(
3u
,
listReader
.
size
());
EXPECT_EQ
(
"structlist 1"
,
listReader
[
0
].
as
<
DynamicStruct
>
().
get
(
"textField"
).
as
<
Text
>
());
EXPECT_EQ
(
"structlist 2"
,
listReader
[
1
].
as
<
DynamicStruct
>
().
get
(
"textField"
).
as
<
Text
>
());
EXPECT_EQ
(
"structlist 3"
,
listReader
[
2
].
as
<
DynamicStruct
>
().
get
(
"textField"
).
as
<
Text
>
());
}
checkEnumList
(
reader
.
get
(
"enumList"
),
{
"foo"
,
"garply"
});
}
#undef as
template
<
typename
Reader
>
void
genericCheckTestMessageAllZero
(
Reader
reader
)
{
EXPECT_EQ
(
Void
::
VOID
,
reader
.
getVoidField
());
...
...
@@ -324,6 +558,84 @@ void genericCheckTestMessageAllZero(Reader reader) {
EXPECT_EQ
(
0u
,
reader
.
getStructList
().
size
());
}
// Hack because as<>() is a template-parameter-dependent lookup everywhere below...
#define as template as
template
<
typename
Reader
>
void
dynamicCheckTestMessageAllZero
(
Reader
reader
)
{
EXPECT_EQ
(
Void
::
VOID
,
reader
.
get
(
"voidField"
).
as
<
Void
>
());
EXPECT_EQ
(
false
,
reader
.
get
(
"boolField"
).
as
<
bool
>
());
EXPECT_EQ
(
0
,
reader
.
get
(
"int8Field"
).
as
<
int8_t
>
());
EXPECT_EQ
(
0
,
reader
.
get
(
"int16Field"
).
as
<
int16_t
>
());
EXPECT_EQ
(
0
,
reader
.
get
(
"int32Field"
).
as
<
int32_t
>
());
EXPECT_EQ
(
0
,
reader
.
get
(
"int64Field"
).
as
<
int64_t
>
());
EXPECT_EQ
(
0u
,
reader
.
get
(
"uInt8Field"
).
as
<
uint8_t
>
());
EXPECT_EQ
(
0u
,
reader
.
get
(
"uInt16Field"
).
as
<
uint16_t
>
());
EXPECT_EQ
(
0u
,
reader
.
get
(
"uInt32Field"
).
as
<
uint32_t
>
());
EXPECT_EQ
(
0u
,
reader
.
get
(
"uInt64Field"
).
as
<
uint64_t
>
());
EXPECT_FLOAT_EQ
(
0
,
reader
.
get
(
"float32Field"
).
as
<
float
>
());
EXPECT_DOUBLE_EQ
(
0
,
reader
.
get
(
"float64Field"
).
as
<
double
>
());
EXPECT_EQ
(
""
,
reader
.
get
(
"textField"
).
as
<
Text
>
());
EXPECT_EQ
(
""
,
reader
.
get
(
"dataField"
).
as
<
Data
>
());
{
auto
subReader
=
reader
.
get
(
"structField"
).
as
<
DynamicStruct
>
();
EXPECT_EQ
(
Void
::
VOID
,
subReader
.
get
(
"voidField"
).
as
<
Void
>
());
EXPECT_EQ
(
false
,
subReader
.
get
(
"boolField"
).
as
<
bool
>
());
EXPECT_EQ
(
0
,
subReader
.
get
(
"int8Field"
).
as
<
int8_t
>
());
EXPECT_EQ
(
0
,
subReader
.
get
(
"int16Field"
).
as
<
int16_t
>
());
EXPECT_EQ
(
0
,
subReader
.
get
(
"int32Field"
).
as
<
int32_t
>
());
EXPECT_EQ
(
0
,
subReader
.
get
(
"int64Field"
).
as
<
int64_t
>
());
EXPECT_EQ
(
0u
,
subReader
.
get
(
"uInt8Field"
).
as
<
uint8_t
>
());
EXPECT_EQ
(
0u
,
subReader
.
get
(
"uInt16Field"
).
as
<
uint16_t
>
());
EXPECT_EQ
(
0u
,
subReader
.
get
(
"uInt32Field"
).
as
<
uint32_t
>
());
EXPECT_EQ
(
0u
,
subReader
.
get
(
"uInt64Field"
).
as
<
uint64_t
>
());
EXPECT_FLOAT_EQ
(
0
,
subReader
.
get
(
"float32Field"
).
as
<
float
>
());
EXPECT_DOUBLE_EQ
(
0
,
subReader
.
get
(
"float64Field"
).
as
<
double
>
());
EXPECT_EQ
(
""
,
subReader
.
get
(
"textField"
).
as
<
Text
>
());
EXPECT_EQ
(
""
,
subReader
.
get
(
"dataField"
).
as
<
Data
>
());
{
auto
subSubReader
=
subReader
.
get
(
"structField"
).
as
<
DynamicStruct
>
();
EXPECT_EQ
(
""
,
subSubReader
.
get
(
"textField"
).
as
<
Text
>
());
EXPECT_EQ
(
""
,
subSubReader
.
get
(
"structField"
).
as
<
DynamicStruct
>
()
.
get
(
"textField"
).
as
<
Text
>
());
}
EXPECT_EQ
(
0u
,
subReader
.
get
(
"voidList"
).
as
<
DynamicList
>
().
size
());
EXPECT_EQ
(
0u
,
subReader
.
get
(
"boolList"
).
as
<
DynamicList
>
().
size
());
EXPECT_EQ
(
0u
,
subReader
.
get
(
"int8List"
).
as
<
DynamicList
>
().
size
());
EXPECT_EQ
(
0u
,
subReader
.
get
(
"int16List"
).
as
<
DynamicList
>
().
size
());
EXPECT_EQ
(
0u
,
subReader
.
get
(
"int32List"
).
as
<
DynamicList
>
().
size
());
EXPECT_EQ
(
0u
,
subReader
.
get
(
"int64List"
).
as
<
DynamicList
>
().
size
());
EXPECT_EQ
(
0u
,
subReader
.
get
(
"uInt8List"
).
as
<
DynamicList
>
().
size
());
EXPECT_EQ
(
0u
,
subReader
.
get
(
"uInt16List"
).
as
<
DynamicList
>
().
size
());
EXPECT_EQ
(
0u
,
subReader
.
get
(
"uInt32List"
).
as
<
DynamicList
>
().
size
());
EXPECT_EQ
(
0u
,
subReader
.
get
(
"uInt64List"
).
as
<
DynamicList
>
().
size
());
EXPECT_EQ
(
0u
,
subReader
.
get
(
"float32List"
).
as
<
DynamicList
>
().
size
());
EXPECT_EQ
(
0u
,
subReader
.
get
(
"float64List"
).
as
<
DynamicList
>
().
size
());
EXPECT_EQ
(
0u
,
subReader
.
get
(
"textList"
).
as
<
DynamicList
>
().
size
());
EXPECT_EQ
(
0u
,
subReader
.
get
(
"dataList"
).
as
<
DynamicList
>
().
size
());
EXPECT_EQ
(
0u
,
subReader
.
get
(
"structList"
).
as
<
DynamicList
>
().
size
());
}
EXPECT_EQ
(
0u
,
reader
.
get
(
"voidList"
).
as
<
DynamicList
>
().
size
());
EXPECT_EQ
(
0u
,
reader
.
get
(
"boolList"
).
as
<
DynamicList
>
().
size
());
EXPECT_EQ
(
0u
,
reader
.
get
(
"int8List"
).
as
<
DynamicList
>
().
size
());
EXPECT_EQ
(
0u
,
reader
.
get
(
"int16List"
).
as
<
DynamicList
>
().
size
());
EXPECT_EQ
(
0u
,
reader
.
get
(
"int32List"
).
as
<
DynamicList
>
().
size
());
EXPECT_EQ
(
0u
,
reader
.
get
(
"int64List"
).
as
<
DynamicList
>
().
size
());
EXPECT_EQ
(
0u
,
reader
.
get
(
"uInt8List"
).
as
<
DynamicList
>
().
size
());
EXPECT_EQ
(
0u
,
reader
.
get
(
"uInt16List"
).
as
<
DynamicList
>
().
size
());
EXPECT_EQ
(
0u
,
reader
.
get
(
"uInt32List"
).
as
<
DynamicList
>
().
size
());
EXPECT_EQ
(
0u
,
reader
.
get
(
"uInt64List"
).
as
<
DynamicList
>
().
size
());
EXPECT_EQ
(
0u
,
reader
.
get
(
"float32List"
).
as
<
DynamicList
>
().
size
());
EXPECT_EQ
(
0u
,
reader
.
get
(
"float64List"
).
as
<
DynamicList
>
().
size
());
EXPECT_EQ
(
0u
,
reader
.
get
(
"textList"
).
as
<
DynamicList
>
().
size
());
EXPECT_EQ
(
0u
,
reader
.
get
(
"dataList"
).
as
<
DynamicList
>
().
size
());
EXPECT_EQ
(
0u
,
reader
.
get
(
"structList"
).
as
<
DynamicList
>
().
size
());
}
#undef as
template
<
typename
Builder
>
void
genericInitListDefaults
(
Builder
builder
)
{
auto
lists
=
builder
.
initLists
();
...
...
@@ -377,6 +689,58 @@ void genericInitListDefaults(Builder builder) {
}
}
void
dynamicInitListDefaults
(
DynamicStruct
::
Builder
builder
)
{
auto
lists
=
builder
.
init
(
"lists"
).
as
<
DynamicStruct
>
();
lists
.
init
(
"list0"
,
2
);
lists
.
init
(
"list1"
,
4
);
lists
.
init
(
"list8"
,
2
);
lists
.
init
(
"list16"
,
2
);
lists
.
init
(
"list32"
,
2
);
lists
.
init
(
"list64"
,
2
);
lists
.
init
(
"listP"
,
2
);
lists
.
get
(
"list0"
).
as
<
DynamicList
>
()[
0
].
as
<
DynamicStruct
>
().
set
(
"f"
,
Void
::
VOID
);
lists
.
get
(
"list0"
).
as
<
DynamicList
>
()[
1
].
as
<
DynamicStruct
>
().
set
(
"f"
,
Void
::
VOID
);
lists
.
get
(
"list1"
).
as
<
DynamicList
>
()[
0
].
as
<
DynamicStruct
>
().
set
(
"f"
,
true
);
lists
.
get
(
"list1"
).
as
<
DynamicList
>
()[
1
].
as
<
DynamicStruct
>
().
set
(
"f"
,
false
);
lists
.
get
(
"list1"
).
as
<
DynamicList
>
()[
2
].
as
<
DynamicStruct
>
().
set
(
"f"
,
true
);
lists
.
get
(
"list1"
).
as
<
DynamicList
>
()[
3
].
as
<
DynamicStruct
>
().
set
(
"f"
,
true
);
lists
.
get
(
"list8"
).
as
<
DynamicList
>
()[
0
].
as
<
DynamicStruct
>
().
set
(
"f"
,
123u
);
lists
.
get
(
"list8"
).
as
<
DynamicList
>
()[
1
].
as
<
DynamicStruct
>
().
set
(
"f"
,
45u
);
lists
.
get
(
"list16"
).
as
<
DynamicList
>
()[
0
].
as
<
DynamicStruct
>
().
set
(
"f"
,
12345u
);
lists
.
get
(
"list16"
).
as
<
DynamicList
>
()[
1
].
as
<
DynamicStruct
>
().
set
(
"f"
,
6789u
);
lists
.
get
(
"list32"
).
as
<
DynamicList
>
()[
0
].
as
<
DynamicStruct
>
().
set
(
"f"
,
123456789u
);
lists
.
get
(
"list32"
).
as
<
DynamicList
>
()[
1
].
as
<
DynamicStruct
>
().
set
(
"f"
,
234567890u
);
lists
.
get
(
"list64"
).
as
<
DynamicList
>
()[
0
].
as
<
DynamicStruct
>
().
set
(
"f"
,
1234567890123456u
);
lists
.
get
(
"list64"
).
as
<
DynamicList
>
()[
1
].
as
<
DynamicStruct
>
().
set
(
"f"
,
2345678901234567u
);
lists
.
get
(
"listP"
).
as
<
DynamicList
>
()[
0
].
as
<
DynamicStruct
>
().
set
(
"f"
,
"foo"
);
lists
.
get
(
"listP"
).
as
<
DynamicList
>
()[
1
].
as
<
DynamicStruct
>
().
set
(
"f"
,
"bar"
);
{
auto
l
=
lists
.
init
(
"int32ListList"
,
3
).
as
<
DynamicList
>
();
l
.
init
(
0
,
3
).
as
<
DynamicList
>
().
copyFrom
({
1
,
2
,
3
});
l
.
init
(
1
,
2
).
as
<
DynamicList
>
().
copyFrom
({
4
,
5
});
l
.
init
(
2
,
1
).
as
<
DynamicList
>
().
copyFrom
({
12341234
});
}
{
auto
l
=
lists
.
init
(
"textListList"
,
3
).
as
<
DynamicList
>
();
l
.
init
(
0
,
2
).
as
<
DynamicList
>
().
copyFrom
({
"foo"
,
"bar"
});
l
.
init
(
1
,
1
).
as
<
DynamicList
>
().
copyFrom
({
"baz"
});
l
.
init
(
2
,
2
).
as
<
DynamicList
>
().
copyFrom
({
"qux"
,
"corge"
});
}
{
auto
l
=
lists
.
init
(
"structListList"
,
2
).
as
<
DynamicList
>
();
auto
e
=
l
.
init
(
0
,
2
).
as
<
DynamicList
>
();
e
[
0
].
as
<
TestAllTypes
>
().
setInt32Field
(
123
);
e
[
1
].
as
<
TestAllTypes
>
().
setInt32Field
(
456
);
e
=
l
.
init
(
1
,
1
).
as
<
DynamicList
>
();
e
[
0
].
as
<
TestAllTypes
>
().
setInt32Field
(
789
);
}
}
template
<
typename
Reader
>
void
genericCheckListDefaults
(
Reader
reader
)
{
auto
lists
=
reader
.
getLists
();
...
...
@@ -435,6 +799,69 @@ void genericCheckListDefaults(Reader reader) {
}
}
// Hack because as<>() is a template-parameter-dependent lookup everywhere below...
#define as template as
template
<
typename
Reader
>
void
dynamicCheckListDefaults
(
Reader
reader
)
{
auto
lists
=
reader
.
get
(
"lists"
).
as
<
DynamicStruct
>
();
ASSERT_EQ
(
2u
,
lists
.
get
(
"list0"
).
as
<
DynamicList
>
().
size
());
ASSERT_EQ
(
4u
,
lists
.
get
(
"list1"
).
as
<
DynamicList
>
().
size
());
ASSERT_EQ
(
2u
,
lists
.
get
(
"list8"
).
as
<
DynamicList
>
().
size
());
ASSERT_EQ
(
2u
,
lists
.
get
(
"list16"
).
as
<
DynamicList
>
().
size
());
ASSERT_EQ
(
2u
,
lists
.
get
(
"list32"
).
as
<
DynamicList
>
().
size
());
ASSERT_EQ
(
2u
,
lists
.
get
(
"list64"
).
as
<
DynamicList
>
().
size
());
ASSERT_EQ
(
2u
,
lists
.
get
(
"listP"
).
as
<
DynamicList
>
().
size
());
EXPECT_EQ
(
Void
::
VOID
,
lists
.
get
(
"list0"
).
as
<
DynamicList
>
()[
0
].
as
<
DynamicStruct
>
().
get
(
"f"
).
as
<
Void
>
());
EXPECT_EQ
(
Void
::
VOID
,
lists
.
get
(
"list0"
).
as
<
DynamicList
>
()[
1
].
as
<
DynamicStruct
>
().
get
(
"f"
).
as
<
Void
>
());
EXPECT_TRUE
(
lists
.
get
(
"list1"
).
as
<
DynamicList
>
()[
0
].
as
<
DynamicStruct
>
().
get
(
"f"
).
as
<
bool
>
());
EXPECT_FALSE
(
lists
.
get
(
"list1"
).
as
<
DynamicList
>
()[
1
].
as
<
DynamicStruct
>
().
get
(
"f"
).
as
<
bool
>
());
EXPECT_TRUE
(
lists
.
get
(
"list1"
).
as
<
DynamicList
>
()[
2
].
as
<
DynamicStruct
>
().
get
(
"f"
).
as
<
bool
>
());
EXPECT_TRUE
(
lists
.
get
(
"list1"
).
as
<
DynamicList
>
()[
3
].
as
<
DynamicStruct
>
().
get
(
"f"
).
as
<
bool
>
());
EXPECT_EQ
(
123u
,
lists
.
get
(
"list8"
).
as
<
DynamicList
>
()[
0
].
as
<
DynamicStruct
>
().
get
(
"f"
).
as
<
uint8_t
>
());
EXPECT_EQ
(
45u
,
lists
.
get
(
"list8"
).
as
<
DynamicList
>
()[
1
].
as
<
DynamicStruct
>
().
get
(
"f"
).
as
<
uint8_t
>
());
EXPECT_EQ
(
12345u
,
lists
.
get
(
"list16"
).
as
<
DynamicList
>
()[
0
].
as
<
DynamicStruct
>
().
get
(
"f"
).
as
<
uint16_t
>
());
EXPECT_EQ
(
6789u
,
lists
.
get
(
"list16"
).
as
<
DynamicList
>
()[
1
].
as
<
DynamicStruct
>
().
get
(
"f"
).
as
<
uint16_t
>
());
EXPECT_EQ
(
123456789u
,
lists
.
get
(
"list32"
).
as
<
DynamicList
>
()[
0
].
as
<
DynamicStruct
>
().
get
(
"f"
).
as
<
uint32_t
>
());
EXPECT_EQ
(
234567890u
,
lists
.
get
(
"list32"
).
as
<
DynamicList
>
()[
1
].
as
<
DynamicStruct
>
().
get
(
"f"
).
as
<
uint32_t
>
());
EXPECT_EQ
(
1234567890123456u
,
lists
.
get
(
"list64"
).
as
<
DynamicList
>
()[
0
].
as
<
DynamicStruct
>
().
get
(
"f"
).
as
<
uint64_t
>
());
EXPECT_EQ
(
2345678901234567u
,
lists
.
get
(
"list64"
).
as
<
DynamicList
>
()[
1
].
as
<
DynamicStruct
>
().
get
(
"f"
).
as
<
uint64_t
>
());
EXPECT_EQ
(
"foo"
,
lists
.
get
(
"listP"
).
as
<
DynamicList
>
()[
0
].
as
<
DynamicStruct
>
().
get
(
"f"
).
as
<
Text
>
());
EXPECT_EQ
(
"bar"
,
lists
.
get
(
"listP"
).
as
<
DynamicList
>
()[
1
].
as
<
DynamicStruct
>
().
get
(
"f"
).
as
<
Text
>
());
{
auto
l
=
lists
.
get
(
"int32ListList"
).
as
<
DynamicList
>
();
ASSERT_EQ
(
3u
,
l
.
size
());
checkList
<
int32_t
>
(
l
[
0
],
{
1
,
2
,
3
});
checkList
<
int32_t
>
(
l
[
1
],
{
4
,
5
});
checkList
<
int32_t
>
(
l
[
2
],
{
12341234
});
}
{
auto
l
=
lists
.
get
(
"textListList"
).
as
<
DynamicList
>
();
ASSERT_EQ
(
3u
,
l
.
size
());
checkList
<
Text
>
(
l
[
0
],
{
"foo"
,
"bar"
});
checkList
<
Text
>
(
l
[
1
],
{
"baz"
});
checkList
<
Text
>
(
l
[
2
],
{
"qux"
,
"corge"
});
}
{
auto
l
=
lists
.
get
(
"structListList"
).
as
<
DynamicList
>
();
ASSERT_EQ
(
2u
,
l
.
size
());
auto
e
=
l
[
0
].
as
<
DynamicList
>
();
ASSERT_EQ
(
2u
,
e
.
size
());
EXPECT_EQ
(
123
,
e
[
0
].
as
<
TestAllTypes
>
().
getInt32Field
());
EXPECT_EQ
(
456
,
e
[
1
].
as
<
TestAllTypes
>
().
getInt32Field
());
e
=
l
[
1
].
as
<
DynamicList
>
();
ASSERT_EQ
(
1u
,
e
.
size
());
EXPECT_EQ
(
789
,
e
[
0
].
as
<
TestAllTypes
>
().
getInt32Field
());
}
}
#undef as
}
// namespace
void
initTestMessage
(
TestAllTypes
::
Builder
builder
)
{
genericInitTestMessage
(
builder
);
}
...
...
@@ -456,5 +883,30 @@ void checkTestMessageAllZero(TestAllTypes::Reader reader) {
genericCheckTestMessageAllZero
(
reader
);
}
void
initDynamicTestMessage
(
DynamicStruct
::
Builder
builder
)
{
dynamicInitTestMessage
(
builder
);
}
void
initDynamicTestLists
(
DynamicStruct
::
Builder
builder
)
{
dynamicInitListDefaults
(
builder
);
}
void
checkDynamicTestMessage
(
DynamicStruct
::
Builder
builder
)
{
dynamicCheckTestMessage
(
builder
);
}
void
checkDynamicTestLists
(
DynamicStruct
::
Builder
builder
)
{
dynamicCheckListDefaults
(
builder
);
}
void
checkDynamicTestMessage
(
DynamicStruct
::
Reader
reader
)
{
dynamicCheckTestMessage
(
reader
);
}
void
checkDynamicTestLists
(
DynamicStruct
::
Reader
reader
)
{
dynamicCheckListDefaults
(
reader
);
}
void
checkDynamicTestMessageAllZero
(
DynamicStruct
::
Builder
builder
)
{
dynamicCheckTestMessageAllZero
(
builder
);
}
void
checkDynamicTestMessageAllZero
(
DynamicStruct
::
Reader
reader
)
{
dynamicCheckTestMessageAllZero
(
reader
);
}
}
// namespace internal
}
// namespace capnproto
c++/src/capnproto/test-util.h
View file @
d7f3123c
...
...
@@ -27,6 +27,7 @@
#include "test.capnp.h"
#include <iostream>
#include "blob.h"
#include "dynamic.h"
namespace
capnproto
{
...
...
@@ -76,6 +77,15 @@ void checkTestMessage(TestListDefaults::Reader reader);
void
checkTestMessageAllZero
(
TestAllTypes
::
Builder
builder
);
void
checkTestMessageAllZero
(
TestAllTypes
::
Reader
reader
);
void
initDynamicTestMessage
(
DynamicStruct
::
Builder
builder
);
void
initDynamicTestLists
(
DynamicStruct
::
Builder
builder
);
void
checkDynamicTestMessage
(
DynamicStruct
::
Builder
builder
);
void
checkDynamicTestLists
(
DynamicStruct
::
Builder
builder
);
void
checkDynamicTestMessage
(
DynamicStruct
::
Reader
reader
);
void
checkDynamicTestLists
(
DynamicStruct
::
Reader
reader
);
void
checkDynamicTestMessageAllZero
(
DynamicStruct
::
Builder
builder
);
void
checkDynamicTestMessageAllZero
(
DynamicStruct
::
Reader
reader
);
}
// namespace internal
}
// namespace capnproto
...
...
compiler/src/c++-header.mustache
View file @
d7f3123c
...
...
@@ -168,6 +168,7 @@ public:
inline
{{
fieldType
}}
get
{{
fieldTitleCase
}}
();
{{/
fieldIsPrimitive
}}
{{^
fieldIsPrimitive
}}
inline bool has
{{
fieldTitleCase
}}
();
{{^
fieldIsGenericObject
}}
inline
{{
fieldType
}}
::Reader get
{{
fieldTitleCase
}}
();
{{/
fieldIsGenericObject
}}
...
...
@@ -218,6 +219,7 @@ public:
inline void set
{{
fieldTitleCase
}}
(
{{
fieldType
}}
value);
{{/
fieldIsPrimitive
}}
{{^
fieldIsPrimitive
}}
inline bool has
{{
fieldTitleCase
}}
();
{{^
fieldIsGenericObject
}}
inline
{{
fieldType
}}
::Builder get
{{
fieldTitleCase
}}
();
inline void set
{{
fieldTitleCase
}}
(
{{
fieldType
}}
::Reader other);
...
...
@@ -310,8 +312,16 @@ inline void {{typeFullName}}::Builder::set{{fieldTitleCase}}({{fieldType}} value
}
{{/
fieldIsPrimitive
}}
{{! ------------------------------------------------------------------------------------------- }}
{{^
fieldIsGenericObject
}}
{{^
fieldIsPrimitive
}}
inline bool
{{
typeFullName
}}
::Reader::has
{{
fieldTitleCase
}}
() {
return !_reader.isPointerFieldNull(
{{
fieldOffset
}}
* ::capnproto::POINTERS);
}
inline bool
{{
typeFullName
}}
::Builder::has
{{
fieldTitleCase
}}
() {
return !_builder.isPointerFieldNull(
{{
fieldOffset
}}
* ::capnproto::POINTERS);
}
{{^
fieldIsGenericObject
}}
inline
{{
fieldType
}}
::Reader
{{
typeFullName
}}
::Reader::get
{{
fieldTitleCase
}}
() {
{{#
fieldUnion
}}
CAPNPROTO_INLINE_DPRECOND(which() ==
{{
unionTitleCase
}}
::
{{
fieldUpperCase
}}
,
...
...
@@ -375,7 +385,6 @@ inline {{fieldType}}::Builder {{typeFullName}}::Builder::init{{fieldTitleCase}}(
}
{{/
fieldIsStruct
}}
{{/
fieldIsPrimitive
}}
{{/
fieldIsGenericObject
}}
{{! ------------------------------------------------------------------------------------------- }}
{{#
fieldIsGenericObject
}}
...
...
@@ -450,6 +459,7 @@ inline typename T::Builder {{typeFullName}}::Builder::init{{fieldTitleCase}}(Par
}
{{/
fieldIsGenericObject
}}
{{/
fieldIsPrimitive
}}
{{/
typeFields
}}
{{/
typeStructOrUnion
}}
{{/
fileTypes
}}
...
...
compiler/src/c++-source.mustache
View file @
d7f3123c
...
...
@@ -69,7 +69,8 @@ static const ::capnproto::internal::RawSchema::MemberInfo m_{{schemaId}}[] = {
{{/
schemaMembersByName
}}
};
const ::capnproto::internal::RawSchema s_
{{
schemaId
}}
= {
b_
{{
schemaId
}}
.words, d_
{{
schemaId
}}
, m_
{{
schemaId
}}
,
{{
schemaDependencyCount
}}
,
{{
schemaMemberCount
}}
b_
{{
schemaId
}}
.words, d_
{{
schemaId
}}
, m_
{{
schemaId
}}
,
{{
schemaDependencyCount
}}
,
{{
schemaMemberCount
}}
, nullptr
};
{{/
typeSchema
}}
{{/
fileTypes
}}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment