Commit 7f20d533 authored by Kenton Varda's avatar Kenton Varda

Implement inline structs.

parent 9e7acd4b
...@@ -165,19 +165,14 @@ std::ostream& operator<<(std::ostream& os, const UnionState& us) { ...@@ -165,19 +165,14 @@ std::ostream& operator<<(std::ostream& os, const UnionState& us) {
return os << "}, " << us.dataOffset << ")"; return os << "}, " << us.dataOffset << ")";
} }
template <typename T> T one() { return static_cast<T>(1); } template <typename StructType, typename Func>
template <> Text::Reader one() { return "1"; } UnionState initUnion(Func&& initializer) {
template <> Void one() { return Void::VOID; }
template <typename T, typename U>
UnionState initUnion(U (TestUnion::Builder::*unionGetter)(),
void (U::Builder::*setter)(T value)) {
// Use the given setter to initialize the given union field and then return a struct indicating // Use the given setter to initialize the given union field and then return a struct indicating
// the location of the data that was written as well as the values of the four union // the location of the data that was written as well as the values of the four union
// discriminants. // discriminants.
MallocMessageBuilder builder; MallocMessageBuilder builder;
((builder.getRoot<TestUnion>().*unionGetter)().*setter)(one<T>()); initializer(builder.getRoot<StructType>());
ArrayPtr<const word> segment = builder.getSegmentsForOutput()[0]; ArrayPtr<const word> segment = builder.getSegmentsForOutput()[0];
CHECK(segment.size() > 2, segment.size()); CHECK(segment.size() > 2, segment.size());
...@@ -204,58 +199,60 @@ found: ...@@ -204,58 +199,60 @@ found:
offset); offset);
} }
#define INIT_UNION(unionName, fieldName) \
initUnion(&TestUnion::Builder::get##unionName, &TestUnion::unionName::Builder::set##fieldName)
TEST(Encoding, UnionLayout) { TEST(Encoding, UnionLayout) {
EXPECT_EQ(UnionState({ 0,0,0,0}, -1), INIT_UNION(Union0, U0f0s0)); #define INIT_UNION(setter) \
EXPECT_EQ(UnionState({ 1,0,0,0}, 0), INIT_UNION(Union0, U0f0s1)); initUnion<TestUnion>([](TestUnion::Builder b) {b.setter;})
EXPECT_EQ(UnionState({ 2,0,0,0}, 0), INIT_UNION(Union0, U0f0s8));
EXPECT_EQ(UnionState({ 3,0,0,0}, 0), INIT_UNION(Union0, U0f0s16)); EXPECT_EQ(UnionState({ 0,0,0,0}, -1), INIT_UNION(getUnion0().setU0f0s0(Void::VOID)));
EXPECT_EQ(UnionState({ 4,0,0,0}, 0), INIT_UNION(Union0, U0f0s32)); EXPECT_EQ(UnionState({ 1,0,0,0}, 0), INIT_UNION(getUnion0().setU0f0s1(1)));
EXPECT_EQ(UnionState({ 5,0,0,0}, 0), INIT_UNION(Union0, U0f0s64)); EXPECT_EQ(UnionState({ 2,0,0,0}, 0), INIT_UNION(getUnion0().setU0f0s8(1)));
EXPECT_EQ(UnionState({ 6,0,0,0}, 448), INIT_UNION(Union0, U0f0sp)); EXPECT_EQ(UnionState({ 3,0,0,0}, 0), INIT_UNION(getUnion0().setU0f0s16(1)));
EXPECT_EQ(UnionState({ 4,0,0,0}, 0), INIT_UNION(getUnion0().setU0f0s32(1)));
EXPECT_EQ(UnionState({ 7,0,0,0}, -1), INIT_UNION(Union0, U0f1s0)); EXPECT_EQ(UnionState({ 5,0,0,0}, 0), INIT_UNION(getUnion0().setU0f0s64(1)));
EXPECT_EQ(UnionState({ 8,0,0,0}, 0), INIT_UNION(Union0, U0f1s1)); EXPECT_EQ(UnionState({ 6,0,0,0}, 448), INIT_UNION(getUnion0().setU0f0sp("1")));
EXPECT_EQ(UnionState({ 9,0,0,0}, 0), INIT_UNION(Union0, U0f1s8));
EXPECT_EQ(UnionState({10,0,0,0}, 0), INIT_UNION(Union0, U0f1s16)); EXPECT_EQ(UnionState({ 7,0,0,0}, -1), INIT_UNION(getUnion0().setU0f1s0(Void::VOID)));
EXPECT_EQ(UnionState({11,0,0,0}, 0), INIT_UNION(Union0, U0f1s32)); EXPECT_EQ(UnionState({ 8,0,0,0}, 0), INIT_UNION(getUnion0().setU0f1s1(1)));
EXPECT_EQ(UnionState({12,0,0,0}, 0), INIT_UNION(Union0, U0f1s64)); EXPECT_EQ(UnionState({ 9,0,0,0}, 0), INIT_UNION(getUnion0().setU0f1s8(1)));
EXPECT_EQ(UnionState({13,0,0,0}, 448), INIT_UNION(Union0, U0f1sp)); EXPECT_EQ(UnionState({10,0,0,0}, 0), INIT_UNION(getUnion0().setU0f1s16(1)));
EXPECT_EQ(UnionState({11,0,0,0}, 0), INIT_UNION(getUnion0().setU0f1s32(1)));
EXPECT_EQ(UnionState({0, 0,0,0}, -1), INIT_UNION(Union1, U1f0s0)); EXPECT_EQ(UnionState({12,0,0,0}, 0), INIT_UNION(getUnion0().setU0f1s64(1)));
EXPECT_EQ(UnionState({0, 1,0,0}, 65), INIT_UNION(Union1, U1f0s1)); EXPECT_EQ(UnionState({13,0,0,0}, 448), INIT_UNION(getUnion0().setU0f1sp("1")));
EXPECT_EQ(UnionState({0, 2,0,0}, 65), INIT_UNION(Union1, U1f1s1));
EXPECT_EQ(UnionState({0, 3,0,0}, 72), INIT_UNION(Union1, U1f0s8)); EXPECT_EQ(UnionState({0, 0,0,0}, -1), INIT_UNION(getUnion1().setU1f0s0(Void::VOID)));
EXPECT_EQ(UnionState({0, 4,0,0}, 72), INIT_UNION(Union1, U1f1s8)); EXPECT_EQ(UnionState({0, 1,0,0}, 65), INIT_UNION(getUnion1().setU1f0s1(1)));
EXPECT_EQ(UnionState({0, 5,0,0}, 80), INIT_UNION(Union1, U1f0s16)); EXPECT_EQ(UnionState({0, 2,0,0}, 65), INIT_UNION(getUnion1().setU1f1s1(1)));
EXPECT_EQ(UnionState({0, 6,0,0}, 80), INIT_UNION(Union1, U1f1s16)); EXPECT_EQ(UnionState({0, 3,0,0}, 72), INIT_UNION(getUnion1().setU1f0s8(1)));
EXPECT_EQ(UnionState({0, 7,0,0}, 96), INIT_UNION(Union1, U1f0s32)); EXPECT_EQ(UnionState({0, 4,0,0}, 72), INIT_UNION(getUnion1().setU1f1s8(1)));
EXPECT_EQ(UnionState({0, 8,0,0}, 96), INIT_UNION(Union1, U1f1s32)); EXPECT_EQ(UnionState({0, 5,0,0}, 80), INIT_UNION(getUnion1().setU1f0s16(1)));
EXPECT_EQ(UnionState({0, 9,0,0}, 128), INIT_UNION(Union1, U1f0s64)); EXPECT_EQ(UnionState({0, 6,0,0}, 80), INIT_UNION(getUnion1().setU1f1s16(1)));
EXPECT_EQ(UnionState({0,10,0,0}, 128), INIT_UNION(Union1, U1f1s64)); EXPECT_EQ(UnionState({0, 7,0,0}, 96), INIT_UNION(getUnion1().setU1f0s32(1)));
EXPECT_EQ(UnionState({0,11,0,0}, 512), INIT_UNION(Union1, U1f0sp)); EXPECT_EQ(UnionState({0, 8,0,0}, 96), INIT_UNION(getUnion1().setU1f1s32(1)));
EXPECT_EQ(UnionState({0,12,0,0}, 512), INIT_UNION(Union1, U1f1sp)); EXPECT_EQ(UnionState({0, 9,0,0}, 128), INIT_UNION(getUnion1().setU1f0s64(1)));
EXPECT_EQ(UnionState({0,10,0,0}, 128), INIT_UNION(getUnion1().setU1f1s64(1)));
EXPECT_EQ(UnionState({0,13,0,0}, -1), INIT_UNION(Union1, U1f2s0)); EXPECT_EQ(UnionState({0,11,0,0}, 512), INIT_UNION(getUnion1().setU1f0sp("1")));
EXPECT_EQ(UnionState({0,14,0,0}, 128), INIT_UNION(Union1, U1f2s1)); EXPECT_EQ(UnionState({0,12,0,0}, 512), INIT_UNION(getUnion1().setU1f1sp("1")));
EXPECT_EQ(UnionState({0,15,0,0}, 128), INIT_UNION(Union1, U1f2s8));
EXPECT_EQ(UnionState({0,16,0,0}, 128), INIT_UNION(Union1, U1f2s16)); EXPECT_EQ(UnionState({0,13,0,0}, -1), INIT_UNION(getUnion1().setU1f2s0(Void::VOID)));
EXPECT_EQ(UnionState({0,17,0,0}, 128), INIT_UNION(Union1, U1f2s32)); EXPECT_EQ(UnionState({0,14,0,0}, 128), INIT_UNION(getUnion1().setU1f2s1(1)));
EXPECT_EQ(UnionState({0,18,0,0}, 128), INIT_UNION(Union1, U1f2s64)); EXPECT_EQ(UnionState({0,15,0,0}, 128), INIT_UNION(getUnion1().setU1f2s8(1)));
EXPECT_EQ(UnionState({0,19,0,0}, 512), INIT_UNION(Union1, U1f2sp)); EXPECT_EQ(UnionState({0,16,0,0}, 128), INIT_UNION(getUnion1().setU1f2s16(1)));
EXPECT_EQ(UnionState({0,17,0,0}, 128), INIT_UNION(getUnion1().setU1f2s32(1)));
EXPECT_EQ(UnionState({0,0,0,0}, 192), INIT_UNION(Union2, U2f0s1)); EXPECT_EQ(UnionState({0,18,0,0}, 128), INIT_UNION(getUnion1().setU1f2s64(1)));
EXPECT_EQ(UnionState({0,0,0,0}, 193), INIT_UNION(Union3, U3f0s1)); EXPECT_EQ(UnionState({0,19,0,0}, 512), INIT_UNION(getUnion1().setU1f2sp("1")));
EXPECT_EQ(UnionState({0,0,1,0}, 200), INIT_UNION(Union2, U2f0s8));
EXPECT_EQ(UnionState({0,0,0,1}, 208), INIT_UNION(Union3, U3f0s8)); EXPECT_EQ(UnionState({0,0,0,0}, 192), INIT_UNION(getUnion2().setU2f0s1(1)));
EXPECT_EQ(UnionState({0,0,2,0}, 224), INIT_UNION(Union2, U2f0s16)); EXPECT_EQ(UnionState({0,0,0,0}, 193), INIT_UNION(getUnion3().setU3f0s1(1)));
EXPECT_EQ(UnionState({0,0,0,2}, 240), INIT_UNION(Union3, U3f0s16)); EXPECT_EQ(UnionState({0,0,1,0}, 200), INIT_UNION(getUnion2().setU2f0s8(1)));
EXPECT_EQ(UnionState({0,0,3,0}, 256), INIT_UNION(Union2, U2f0s32)); EXPECT_EQ(UnionState({0,0,0,1}, 208), INIT_UNION(getUnion3().setU3f0s8(1)));
EXPECT_EQ(UnionState({0,0,0,3}, 288), INIT_UNION(Union3, U3f0s32)); EXPECT_EQ(UnionState({0,0,2,0}, 224), INIT_UNION(getUnion2().setU2f0s16(1)));
EXPECT_EQ(UnionState({0,0,4,0}, 320), INIT_UNION(Union2, U2f0s64)); EXPECT_EQ(UnionState({0,0,0,2}, 240), INIT_UNION(getUnion3().setU3f0s16(1)));
EXPECT_EQ(UnionState({0,0,0,4}, 384), INIT_UNION(Union3, U3f0s64)); EXPECT_EQ(UnionState({0,0,3,0}, 256), INIT_UNION(getUnion2().setU2f0s32(1)));
EXPECT_EQ(UnionState({0,0,0,3}, 288), INIT_UNION(getUnion3().setU3f0s32(1)));
EXPECT_EQ(UnionState({0,0,4,0}, 320), INIT_UNION(getUnion2().setU2f0s64(1)));
EXPECT_EQ(UnionState({0,0,0,4}, 384), INIT_UNION(getUnion3().setU3f0s64(1)));
#undef INIT_UNION
} }
TEST(Encoding, UnionDefault) { TEST(Encoding, UnionDefault) {
...@@ -287,6 +284,318 @@ TEST(Encoding, UnionDefault) { ...@@ -287,6 +284,318 @@ TEST(Encoding, UnionDefault) {
} }
} }
// =======================================================================================
TEST(Encoding, InlineStructUnionLayout) {
uint ptrOffset = TestInlineUnions::STRUCT_SIZE.data * BITS_PER_WORD / BITS - 64;
auto ptr = [=](uint i) { return ptrOffset + i * 64; };
#define INIT_UNION(setter) \
initUnion<TestInlineUnions>([](TestInlineUnions::Builder b) {b.setter;})
EXPECT_EQ(UnionState({ 0,0,0,0}, -1), INIT_UNION(getUnion0().initF0()));
EXPECT_EQ(UnionState({ 1,0,0,0}, 0), INIT_UNION(getUnion0().initF1().setF(1)));
EXPECT_EQ(UnionState({ 2,0,0,0}, 0), INIT_UNION(getUnion0().initF8().setF0(true)));
EXPECT_EQ(UnionState({ 3,0,0,0}, 0), INIT_UNION(getUnion0().initF16().setF0(1)));
EXPECT_EQ(UnionState({ 4,0,0,0}, 0), INIT_UNION(getUnion0().initF32().setF0(1)));
EXPECT_EQ(UnionState({ 5,0,0,0}, 0), INIT_UNION(getUnion0().initF64().setF0(1)));
EXPECT_EQ(UnionState({ 6,0,0,0}, 0), INIT_UNION(getUnion0().initF128().setF0(1)));
EXPECT_EQ(UnionState({ 7,0,0,0}, 0), INIT_UNION(getUnion0().initF192().setF0(1)));
EXPECT_EQ(UnionState({ 8,0,0,0}, -1), INIT_UNION(getUnion0().initF0p().initF()));
EXPECT_EQ(UnionState({ 9,0,0,0}, 0), INIT_UNION(getUnion0().initF1p().initF().setF(1)));
EXPECT_EQ(UnionState({10,0,0,0}, 0), INIT_UNION(getUnion0().initF8p().initF().setF0(true)));
EXPECT_EQ(UnionState({11,0,0,0}, 0), INIT_UNION(getUnion0().initF16p().initF().setF0(1)));
EXPECT_EQ(UnionState({12,0,0,0}, 0), INIT_UNION(getUnion0().initF32p().initF().setF0(1)));
EXPECT_EQ(UnionState({13,0,0,0}, 0), INIT_UNION(getUnion0().initF64p().initF().setF0(1)));
EXPECT_EQ(UnionState({14,0,0,0}, 0), INIT_UNION(getUnion0().initF128p().initF().setF0(1)));
EXPECT_EQ(UnionState({15,0,0,0}, 0), INIT_UNION(getUnion0().initF192p().initF().setF0(1)));
EXPECT_EQ(UnionState({ 8,0,0,0}, ptr(0)), INIT_UNION(getUnion0().initF0p().setP0("1")));
EXPECT_EQ(UnionState({ 9,0,0,0}, ptr(0)), INIT_UNION(getUnion0().initF1p().setP0("1")));
EXPECT_EQ(UnionState({10,0,0,0}, ptr(0)), INIT_UNION(getUnion0().initF8p().setP0("1")));
EXPECT_EQ(UnionState({11,0,0,0}, ptr(0)), INIT_UNION(getUnion0().initF16p().setP0("1")));
EXPECT_EQ(UnionState({12,0,0,0}, ptr(0)), INIT_UNION(getUnion0().initF32p().setP0("1")));
EXPECT_EQ(UnionState({13,0,0,0}, ptr(0)), INIT_UNION(getUnion0().initF64p().setP0("1")));
EXPECT_EQ(UnionState({14,0,0,0}, ptr(0)), INIT_UNION(getUnion0().initF128p().setP0("1")));
EXPECT_EQ(UnionState({15,0,0,0}, ptr(0)), INIT_UNION(getUnion0().initF192p().setP0("1")));
EXPECT_EQ(UnionState({0, 0,0,0}, -1), INIT_UNION(getUnion1().initF0()));
EXPECT_EQ(UnionState({0, 1,0,0}, 193), INIT_UNION(getUnion1().initF1().setF(1)));
EXPECT_EQ(UnionState({0, 2,0,0}, 200), INIT_UNION(getUnion1().initF8().setF0(true)));
EXPECT_EQ(UnionState({0, 3,0,0}, 208), INIT_UNION(getUnion1().initF16().setF0(1)));
EXPECT_EQ(UnionState({0, 4,0,0}, 224), INIT_UNION(getUnion1().initF32().setF0(1)));
EXPECT_EQ(UnionState({0, 5,0,0}, 256), INIT_UNION(getUnion1().initF64().setF0(1)));
EXPECT_EQ(UnionState({0, 6,0,0}, 256), INIT_UNION(getUnion1().initF128().setF0(1)));
EXPECT_EQ(UnionState({0, 7,0,0}, 256), INIT_UNION(getUnion1().initF192().setF0(1)));
EXPECT_EQ(UnionState({0,0, 0,0}, 448), INIT_UNION(getUnion2().initF1p().initF().setF(1)));
EXPECT_EQ(UnionState({0,0,0, 0}, 449), INIT_UNION(getUnion3().initF1p().initF().setF(1)));
EXPECT_EQ(UnionState({0,0, 1,0}, 456), INIT_UNION(getUnion2().initF8p().initF().setF0(true)));
EXPECT_EQ(UnionState({0,0,0, 1}, 464), INIT_UNION(getUnion3().initF8p().initF().setF0(true)));
EXPECT_EQ(UnionState({0,0, 2,0}, 480), INIT_UNION(getUnion2().initF16p().initF().setF0(1)));
EXPECT_EQ(UnionState({0,0,0, 2}, 496), INIT_UNION(getUnion3().initF16p().initF().setF0(1)));
EXPECT_EQ(UnionState({0,0, 3,0}, 512), INIT_UNION(getUnion2().initF32p().initF().setF0(1)));
EXPECT_EQ(UnionState({0,0,0, 3}, 544), INIT_UNION(getUnion3().initF32p().initF().setF0(1)));
EXPECT_EQ(UnionState({0,0, 4,0}, 576), INIT_UNION(getUnion2().initF64p().initF().setF0(1)));
EXPECT_EQ(UnionState({0,0,0, 4}, 640), INIT_UNION(getUnion3().initF64p().initF().setF0(1)));
EXPECT_EQ(UnionState({0,0, 5,0}, 704), INIT_UNION(getUnion2().initF128p().initF().setF0(1)));
EXPECT_EQ(UnionState({0,0,0, 5}, 832), INIT_UNION(getUnion3().initF128p().initF().setF0(1)));
EXPECT_EQ(UnionState({0,0, 6,0}, 960), INIT_UNION(getUnion2().initF192p().initF().setF0(1)));
EXPECT_EQ(UnionState({0,0,0, 6},1152), INIT_UNION(getUnion3().initF192p().initF().setF0(1)));
EXPECT_EQ(UnionState({0,0, 0,0}, ptr( 3)), INIT_UNION(getUnion2().initF1p().setP0("1")));
EXPECT_EQ(UnionState({0,0,0, 0}, ptr( 4)), INIT_UNION(getUnion3().initF1p().setP0("1")));
EXPECT_EQ(UnionState({0,0, 1,0}, ptr( 3)), INIT_UNION(getUnion2().initF8p().setP0("1")));
EXPECT_EQ(UnionState({0,0,0, 1}, ptr( 4)), INIT_UNION(getUnion3().initF8p().setP0("1")));
EXPECT_EQ(UnionState({0,0, 2,0}, ptr( 5)), INIT_UNION(getUnion2().initF16p().setP0("1")));
EXPECT_EQ(UnionState({0,0,0, 2}, ptr( 7)), INIT_UNION(getUnion3().initF16p().setP0("1")));
EXPECT_EQ(UnionState({0,0, 3,0}, ptr( 5)), INIT_UNION(getUnion2().initF32p().setP0("1")));
EXPECT_EQ(UnionState({0,0,0, 3}, ptr( 7)), INIT_UNION(getUnion3().initF32p().setP0("1")));
EXPECT_EQ(UnionState({0,0, 4,0}, ptr( 5)), INIT_UNION(getUnion2().initF64p().setP0("1")));
EXPECT_EQ(UnionState({0,0,0, 4}, ptr( 7)), INIT_UNION(getUnion3().initF64p().setP0("1")));
EXPECT_EQ(UnionState({0,0, 5,0}, ptr( 9)), INIT_UNION(getUnion2().initF128p().setP0("1")));
EXPECT_EQ(UnionState({0,0,0, 5}, ptr(12)), INIT_UNION(getUnion3().initF128p().setP0("1")));
EXPECT_EQ(UnionState({0,0, 6,0}, ptr( 9)), INIT_UNION(getUnion2().initF192p().setP0("1")));
EXPECT_EQ(UnionState({0,0,0, 6}, ptr(12)), INIT_UNION(getUnion3().initF192p().setP0("1")));
#undef INIT_UNION
}
TEST(Encoding, InitInlineStruct) {
MallocMessageBuilder builder;
auto root = builder.getRoot<TestInlineLayout>();
// Set as many bits as we can.
root.initF1().setF(true);
root.initF1Offset().setF(true);
root.setBit(true);
root.initF8().setF0(true);
root.getF8().setF1(true);
root.getF8().setF2(true);
root.initF16().setF0(0xffu);
root.getF16().setF1(0xffu);
root.initF32().setF0(0xffu);
root.getF32().setF1(0xffffu);
root.initF64().setF0(0xffu);
root.getF64().setF1(0xffffffffu);
root.initF128().setF0(0xffffffffffffffffull);
root.getF128().setF1(0xffffffffffffffffull);
root.initF192().setF0(0xffffffffffffffffull);
root.getF192().setF1(0xffffffffffffffffull);
root.getF192().setF2(0xffffffffffffffffull);
root.initF0p().setP0("foo");
root.initF1p().setP0("foo");
root.getF1p().initF().setF(true);
root.initF8p().setP0("foo");
root.initF16p().setP0("foo");
root.getF16p().setP1("foo");
root.initF32p().setP0("foo");
root.getF32p().setP1("foo");
root.initF64p().setP0("foo");
root.getF64p().setP1("foo");
root.initF128p().setP0("foo");
root.getF128p().setP1("foo");
root.getF128p().setP2("foo");
root.initF192p().setP0("foo");
root.getF192p().setP1("foo");
root.getF192p().setP2("foo");
// Now try re-initializing each thing and making sure the surrounding things aren't modified.
EXPECT_FALSE(root.initF1().getF());
EXPECT_TRUE(root.getF1Offset().getF());
root.getF1().setF(true);
EXPECT_FALSE(root.initF1Offset().getF());
EXPECT_TRUE(root.getF1().getF());
EXPECT_TRUE(root.getBit());
EXPECT_TRUE(root.getF8().getF0());
root.getF1Offset().setF(true);
EXPECT_FALSE(root.initF8().getF0());
EXPECT_FALSE(root.getF8().getF1());
EXPECT_FALSE(root.getF8().getF2());
EXPECT_TRUE(root.getF1().getF());
EXPECT_TRUE(root.getBit());
EXPECT_EQ(0xffu, root.getF16().getF0());
root.initF8().setF0(true);
root.getF8().setF1(true);
root.getF8().setF2(true);
EXPECT_EQ(0u, root.initF16().getF0());
EXPECT_EQ(0u, root.getF16().getF1());
EXPECT_TRUE(root.getF8().getF0());
EXPECT_TRUE(root.getF8().getF1());
EXPECT_TRUE(root.getF8().getF2());
EXPECT_EQ(0xffu, root.getF32().getF0());
root.getF16().setF0(0xffu);
root.getF16().setF1(0xffu);
EXPECT_EQ(0u, root.initF32().getF0());
EXPECT_EQ(0u, root.getF32().getF1());
EXPECT_EQ(0xffu, root.getF16().getF0());
EXPECT_EQ(0xffu, root.getF16().getF1());
EXPECT_EQ(0xffu, root.getF64().getF0());
root.getF32().setF0(0xffu);
root.getF32().setF1(0xffffu);
EXPECT_EQ(0u, root.initF64().getF0());
EXPECT_EQ(0u, root.getF64().getF1());
EXPECT_EQ(0xffu, root.getF32().getF0());
EXPECT_EQ(0xffffu, root.getF32().getF1());
EXPECT_EQ(0xffffffffffffffffull, root.getF128().getF0());
root.getF64().setF0(0xffu);
root.getF64().setF1(0xffffffffu);
EXPECT_EQ(0u, root.initF128().getF0());
EXPECT_EQ(0u, root.getF128().getF1());
EXPECT_EQ(0xffu, root.getF64().getF0());
EXPECT_EQ(0xffffffffu, root.getF64().getF1());
EXPECT_EQ(0xffffffffffffffffull, root.getF192().getF0());
root.getF128().setF0(0xffffffffffffffffull);
root.getF128().setF1(0xffffffffffffffffull);
EXPECT_EQ(0u, root.initF192().getF0());
EXPECT_EQ(0u, root.getF192().getF1());
EXPECT_EQ(0u, root.getF192().getF2());
EXPECT_EQ(0xffffffffffffffffull, root.getF128().getF0());
EXPECT_EQ(0xffffffffffffffffull, root.getF128().getF1());
EXPECT_TRUE(root.getF1p().getF().getF());
root.getF192().setF0(0xffffffffffffffffull);
root.getF192().setF1(0xffffffffffffffffull);
root.getF192().setF2(0xffffffffffffffffull);
EXPECT_EQ("", root.initF0p().getP0());
EXPECT_EQ("foo", root.getF1p().getP0());
root.getF0p().setP0("foo");
EXPECT_EQ("", root.initF1p().getP0());
EXPECT_EQ("foo", root.getF0p().getP0());
EXPECT_EQ("foo", root.getF8p().getP0());
root.getF1p().setP0("foo");
EXPECT_EQ("", root.initF8p().getP0());
EXPECT_EQ("foo", root.getF1p().getP0());
EXPECT_EQ("foo", root.getF16p().getP0());
root.initF8p().setP0("foo");
EXPECT_EQ("", root.initF16p().getP0());
EXPECT_EQ("", root.getF16p().getP1());
EXPECT_EQ("foo", root.getF8p().getP0());
EXPECT_EQ("foo", root.getF32p().getP0());
root.initF16p().setP0("foo");
root.getF16p().setP1("foo");
EXPECT_EQ("", root.initF32p().getP0());
EXPECT_EQ("", root.getF32p().getP1());
EXPECT_EQ("foo", root.getF16p().getP1());
EXPECT_EQ("foo", root.getF64p().getP0());
root.initF32p().setP0("foo");
root.getF32p().setP1("foo");
EXPECT_EQ("", root.initF64p().getP0());
EXPECT_EQ("", root.getF64p().getP1());
EXPECT_EQ("foo", root.getF32p().getP1());
EXPECT_EQ("foo", root.getF128p().getP0());
root.initF64p().setP0("foo");
root.getF64p().setP1("foo");
EXPECT_EQ("", root.initF128p().getP0());
EXPECT_EQ("", root.getF128p().getP1());
EXPECT_EQ("", root.getF128p().getP2());
EXPECT_EQ("foo", root.getF64p().getP1());
EXPECT_EQ("foo", root.getF192p().getP0());
root.initF128p().setP0("foo");
root.getF128p().setP1("foo");
root.getF128p().setP2("foo");
EXPECT_EQ("", root.initF192p().getP0());
EXPECT_EQ("", root.getF192p().getP1());
EXPECT_EQ("", root.getF192p().getP2());
EXPECT_EQ("foo", root.getF128p().getP2());
root.initF192p().setP0("foo");
root.getF192p().setP1("foo");
root.getF192p().setP2("foo");
}
TEST(Encoding, InlineDefaults) {
MallocMessageBuilder builder;
TestInlineDefaults::Reader reader = builder.getRoot<TestInlineDefaults>().asReader();
{
auto normal = reader.getNormal();
EXPECT_TRUE(normal.getF1().getF());
EXPECT_TRUE(normal.getF8().getF0());
EXPECT_FALSE(normal.getF8().getF1());
EXPECT_TRUE(normal.getF8().getF2());
EXPECT_EQ(123u, normal.getF16().getF0());
EXPECT_EQ(45u, normal.getF16().getF1());
EXPECT_EQ(67u, normal.getF32().getF0());
EXPECT_EQ(8901u, normal.getF32().getF1());
EXPECT_EQ(234u, normal.getF64().getF0());
EXPECT_EQ(567890123u, normal.getF64().getF1());
EXPECT_EQ(1234567890123ull, normal.getF128().getF0());
EXPECT_EQ(4567890123456ull, normal.getF128().getF1());
EXPECT_EQ(7890123456789ull, normal.getF192().getF0());
EXPECT_EQ(2345678901234ull, normal.getF192().getF1());
EXPECT_EQ(5678901234567ull, normal.getF192().getF2());
EXPECT_FALSE(normal.getF1p().getF().getF());
EXPECT_TRUE(normal.getF8p().getF().getF0());
EXPECT_TRUE(normal.getF8p().getF().getF1());
EXPECT_FALSE(normal.getF8p().getF().getF2());
EXPECT_EQ(98u, normal.getF16p().getF().getF0());
EXPECT_EQ(76u, normal.getF16p().getF().getF1());
EXPECT_EQ(54u, normal.getF32p().getF().getF0());
EXPECT_EQ(32109u, normal.getF32p().getF().getF1());
EXPECT_EQ(87u, normal.getF64p().getF().getF0());
EXPECT_EQ(654321098u, normal.getF64p().getF().getF1());
EXPECT_EQ(7654321098765ull, normal.getF128p().getF().getF0());
EXPECT_EQ(4321098765432ull, normal.getF128p().getF().getF1());
EXPECT_EQ(1098765432109ull, normal.getF192p().getF().getF0());
EXPECT_EQ(8765432109876ull, normal.getF192p().getF().getF1());
EXPECT_EQ(5432109876543ull, normal.getF192p().getF().getF2());
EXPECT_EQ("foo", normal.getF0p().getP0());
EXPECT_EQ("bar", normal.getF1p().getP0());
EXPECT_EQ("baz", normal.getF8p().getP0());
EXPECT_EQ("qux", normal.getF16p().getP0());
EXPECT_EQ("quux", normal.getF16p().getP1());
EXPECT_EQ("corge", normal.getF32p().getP0());
EXPECT_EQ("grault", normal.getF32p().getP1());
EXPECT_EQ("garply", normal.getF64p().getP0());
EXPECT_EQ("waldo", normal.getF64p().getP1());
EXPECT_EQ("fred", normal.getF128p().getP0());
EXPECT_EQ("plugh", normal.getF128p().getP1());
EXPECT_EQ("xyzzy", normal.getF128p().getP2());
EXPECT_EQ("thud", normal.getF192p().getP0());
EXPECT_EQ("foobar", normal.getF192p().getP1());
EXPECT_EQ("barbaz", normal.getF192p().getP2());
}
{
auto unions = reader.getUnions();
ASSERT_EQ(TestInlineUnions::Union0::F32, unions.getUnion0().which());
EXPECT_EQ(67u, unions.getUnion0().getF32().getF0());
EXPECT_EQ(8901u, unions.getUnion0().getF32().getF1());
ASSERT_EQ(TestInlineUnions::Union1::F128, unions.getUnion1().which());
EXPECT_EQ(1234567890123ull, unions.getUnion1().getF128().getF0());
EXPECT_EQ(4567890123456ull, unions.getUnion1().getF128().getF1());
ASSERT_EQ(TestInlineUnions::Union2::F1P, unions.getUnion2().which());
EXPECT_EQ("foo", unions.getUnion2().getF1p().getP0());
ASSERT_EQ(TestInlineUnions::Union3::F16P, unions.getUnion3().which());
EXPECT_EQ(98u, unions.getUnion3().getF16p().getF().getF0());
EXPECT_EQ(76u, unions.getUnion3().getF16p().getF().getF1());
EXPECT_EQ("qux", unions.getUnion3().getF16p().getP0());
EXPECT_EQ("quux", unions.getUnion3().getF16p().getP1());
}
}
// ======================================================================================= // =======================================================================================
// Tests of generated code, not really of the encoding. // Tests of generated code, not really of the encoding.
// TODO(cleanup): Move to a different test? // TODO(cleanup): Move to a different test?
......
...@@ -415,7 +415,7 @@ struct WireHelpers { ...@@ -415,7 +415,7 @@ struct WireHelpers {
ref->structRef.set(size); ref->structRef.set(size);
// Build the StructBuilder. // Build the StructBuilder.
return StructBuilder(segment, ptr, reinterpret_cast<WireReference*>(ptr + size.data)); return StructBuilder(segment, ptr, reinterpret_cast<WireReference*>(ptr + size.data), 0 * BITS);
} }
static CAPNPROTO_ALWAYS_INLINE(StructBuilder getWritableStructReference( static CAPNPROTO_ALWAYS_INLINE(StructBuilder getWritableStructReference(
...@@ -442,7 +442,7 @@ struct WireHelpers { ...@@ -442,7 +442,7 @@ struct WireHelpers {
"Trying to update struct with incorrect reference count."); "Trying to update struct with incorrect reference count.");
} }
return StructBuilder(segment, ptr, reinterpret_cast<WireReference*>(ptr + size.data)); return StructBuilder(segment, ptr, reinterpret_cast<WireReference*>(ptr + size.data), 0 * BITS);
} }
static CAPNPROTO_ALWAYS_INLINE(ListBuilder initListReference( static CAPNPROTO_ALWAYS_INLINE(ListBuilder initListReference(
...@@ -603,7 +603,7 @@ struct WireHelpers { ...@@ -603,7 +603,7 @@ struct WireHelpers {
if (ref == nullptr || ref->isNull()) { if (ref == nullptr || ref->isNull()) {
useDefault: useDefault:
if (defaultValue == nullptr) { if (defaultValue == nullptr) {
return StructReader(nullptr, nullptr, nullptr, 0 * WORDS, 0 * REFERENCES, 0 * BITS, return StructReader(nullptr, nullptr, nullptr, 0 * BITS, 0 * REFERENCES, 0 * BITS,
std::numeric_limits<int>::max()); std::numeric_limits<int>::max());
} }
segment = nullptr; segment = nullptr;
...@@ -637,7 +637,7 @@ struct WireHelpers { ...@@ -637,7 +637,7 @@ struct WireHelpers {
return StructReader( return StructReader(
segment, ptr, reinterpret_cast<const WireReference*>(ptr + ref->structRef.dataSize.get()), segment, ptr, reinterpret_cast<const WireReference*>(ptr + ref->structRef.dataSize.get()),
ref->structRef.dataSize.get(), ref->structRef.dataSize.get() * BITS_PER_WORD,
ref->structRef.refCount.get(), ref->structRef.refCount.get(),
0 * BITS, nestingLimit - 1); 0 * BITS, nestingLimit - 1);
} }
...@@ -752,7 +752,8 @@ struct WireHelpers { ...@@ -752,7 +752,8 @@ struct WireHelpers {
} }
return ListReader(segment, ptr, size, wordsPerElement * BITS_PER_WORD, return ListReader(segment, ptr, size, wordsPerElement * BITS_PER_WORD,
tag->structRef.dataSize.get(), tag->structRef.refCount.get(), nestingLimit - 1); tag->structRef.dataSize.get() * BITS_PER_WORD,
tag->structRef.refCount.get(), nestingLimit - 1);
} else { } else {
// The elements of the list are NOT structs. // The elements of the list are NOT structs.
...@@ -774,28 +775,17 @@ struct WireHelpers { ...@@ -774,28 +775,17 @@ struct WireHelpers {
// old version of the protocol. We need to verify that the struct's first field matches // old version of the protocol. We need to verify that the struct's first field matches
// what the sender sent us. // what the sender sent us.
WordCount dataSize; BitCount dataSize = 0 * BITS;
WireReferenceCount referenceCount; WireReferenceCount referenceCount = 0 * REFERENCES;
switch (ref->listRef.elementSize()) { switch (ref->listRef.elementSize()) {
case FieldSize::VOID: case FieldSize::VOID: break;
dataSize = 0 * WORDS; case FieldSize::BIT: dataSize = 1 * BITS; break;
referenceCount = 0 * REFERENCES; case FieldSize::BYTE: dataSize = 8 * BITS; break;
break; case FieldSize::TWO_BYTES: dataSize = 16 * BITS; break;
case FieldSize::FOUR_BYTES: dataSize = 32 * BITS; break;
case FieldSize::BIT: case FieldSize::EIGHT_BYTES: dataSize = 64 * BITS; break;
case FieldSize::BYTE: case FieldSize::REFERENCE: referenceCount = 1 * REFERENCES; break;
case FieldSize::TWO_BYTES:
case FieldSize::FOUR_BYTES:
case FieldSize::EIGHT_BYTES:
dataSize = 1 * WORDS;
referenceCount = 0 * REFERENCES;
break;
case FieldSize::REFERENCE:
dataSize = 0 * WORDS;
referenceCount = 1 * REFERENCES;
break;
case FieldSize::INLINE_COMPOSITE: case FieldSize::INLINE_COMPOSITE:
FAIL_CHECK(); FAIL_CHECK();
...@@ -978,7 +968,7 @@ StructReader StructBuilder::asReader() const { ...@@ -978,7 +968,7 @@ StructReader StructBuilder::asReader() const {
static_assert(sizeof(WireReference::structRef.refCount) == 2, static_assert(sizeof(WireReference::structRef.refCount) == 2,
"Has the maximum reference count changed?"); "Has the maximum reference count changed?");
return StructReader(segment, data, references, return StructReader(segment, data, references,
0xffff * WORDS, 0xffff * REFERENCES, 0 * BITS, std::numeric_limits<int>::max()); 0xffffffff * BITS, 0xffff * REFERENCES, 0 * BITS, std::numeric_limits<int>::max());
} }
StructReader StructReader::readRootTrusted(const word* location) { StructReader StructReader::readRootTrusted(const word* location) {
...@@ -998,7 +988,7 @@ StructReader StructReader::readRoot( ...@@ -998,7 +988,7 @@ StructReader StructReader::readRoot(
} }
StructReader StructReader::readEmpty() { StructReader StructReader::readEmpty() {
return StructReader(nullptr, nullptr, nullptr, 0 * WORDS, 0 * REFERENCES, 0 * BITS, return StructReader(nullptr, nullptr, nullptr, 0 * BITS, 0 * REFERENCES, 0 * BITS,
std::numeric_limits<int>::max()); std::numeric_limits<int>::max());
} }
...@@ -1031,7 +1021,7 @@ StructBuilder ListBuilder::getStructElement( ...@@ -1031,7 +1021,7 @@ StructBuilder ListBuilder::getStructElement(
ElementCount index, decltype(WORDS/ELEMENTS) elementSize, WordCount structDataSize) const { ElementCount index, decltype(WORDS/ELEMENTS) elementSize, WordCount structDataSize) const {
word* structPtr = ptr + elementSize * index; word* structPtr = ptr + elementSize * index;
return StructBuilder(segment, structPtr, return StructBuilder(segment, structPtr,
reinterpret_cast<WireReference*>(structPtr + structDataSize)); reinterpret_cast<WireReference*>(structPtr + structDataSize), 0 * BITS);
} }
ListBuilder ListBuilder::initListElement( ListBuilder ListBuilder::initListElement(
...@@ -1087,9 +1077,9 @@ ListReader ListBuilder::asReader(FieldSize elementSize) const { ...@@ -1087,9 +1077,9 @@ ListReader ListBuilder::asReader(FieldSize elementSize) const {
std::numeric_limits<int>::max()); std::numeric_limits<int>::max());
} }
ListReader ListBuilder::asReader(WordCount dataSize, WireReferenceCount referenceCount) const { ListReader ListBuilder::asReader(BitCount dataSize, WireReferenceCount referenceCount) const {
return ListReader(segment, ptr, elementCount, return ListReader(segment, ptr, elementCount,
(dataSize + referenceCount * WORDS_PER_REFERENCE) * BITS_PER_WORD / ELEMENTS, (dataSize + referenceCount * WORDS_PER_REFERENCE * BITS_PER_WORD) / ELEMENTS,
dataSize, referenceCount, std::numeric_limits<int>::max()); dataSize, referenceCount, std::numeric_limits<int>::max());
} }
...@@ -1103,7 +1093,7 @@ StructReader ListReader::getStructElement(ElementCount index) const { ...@@ -1103,7 +1093,7 @@ StructReader ListReader::getStructElement(ElementCount index) const {
const byte* structPtr = reinterpret_cast<const byte*>(ptr) + indexBit / BITS_PER_BYTE; const byte* structPtr = reinterpret_cast<const byte*>(ptr) + indexBit / BITS_PER_BYTE;
return StructReader( return StructReader(
segment, structPtr, segment, structPtr,
reinterpret_cast<const WireReference*>(structPtr + structDataSize * BYTES_PER_WORD), reinterpret_cast<const WireReference*>(structPtr + structDataSize / BITS_PER_BYTE),
structDataSize, structReferenceCount, indexBit % BITS_PER_BYTE, nestingLimit - 1); structDataSize, structReferenceCount, indexBit % BITS_PER_BYTE, nestingLimit - 1);
} }
......
...@@ -28,6 +28,11 @@ ...@@ -28,6 +28,11 @@
// as does other parts of the Cap'n proto library which provide a higher-level interface for // as does other parts of the Cap'n proto library which provide a higher-level interface for
// dynamic introspection. // dynamic introspection.
#ifdef __CDT_PARSER__
// Eclipse keeps thinking this is pre-defined for no apparent reason.
#undef CAPNPROTO_LAYOUT_H_
#endif
#ifndef CAPNPROTO_LAYOUT_H_ #ifndef CAPNPROTO_LAYOUT_H_
#define CAPNPROTO_LAYOUT_H_ #define CAPNPROTO_LAYOUT_H_
...@@ -91,8 +96,8 @@ enum class FieldSize: uint8_t { ...@@ -91,8 +96,8 @@ enum class FieldSize: uint8_t {
// 2) For struct fields of composite types where the field's total size is known at compile time, // 2) For struct fields of composite types where the field's total size is known at compile time,
// we can embed the field directly into the parent struct to avoid indirection through a // we can embed the field directly into the parent struct to avoid indirection through a
// reference. However, this means that the field size can never change -- e.g. if it is a // reference. However, this means that the field size can never change -- e.g. if it is a
// struct, new fields cannot be added to it. It's unclear if this is really useful so at this // struct, new fields cannot be added to it. The field's struct type is therefore required to
// time it is not supported. // be declared "inline" with a fixed width.
}; };
typedef decltype(BITS / ELEMENTS) BitsPerElement; typedef decltype(BITS / ELEMENTS) BitsPerElement;
...@@ -276,6 +281,17 @@ public: ...@@ -276,6 +281,17 @@ public:
// initialized, it is initialized as a deep copy of the given default value (a trusted message), // initialized, it is initialized as a deep copy of the given default value (a trusted message),
// or to the empty state if defaultValue is nullptr. // or to the empty state if defaultValue is nullptr.
CAPNPROTO_ALWAYS_INLINE(StructBuilder initInlineStructField(
BitCount dataOffset, BitCount inlineDataSize,
WireReferenceCount refIndex, WireReferenceCount inlineRefCount) const);
// Initialize an inlined struct field, given the position and size of the data and pointer
// sections.
CAPNPROTO_ALWAYS_INLINE(StructBuilder getInlineStructField(
BitCount dataOffset, BitCount inlineDataSize,
WireReferenceCount refIndex, WireReferenceCount inlineRefCount) const);
// Gets an inlined struct field, given the position and size of the data and pointer sections.
ListBuilder initListField(WireReferenceCount refIndex, FieldSize elementSize, ListBuilder initListField(WireReferenceCount refIndex, FieldSize elementSize,
ElementCount elementCount) const; ElementCount elementCount) const;
// Allocates a new list of the given size for the field at the given index in the reference // Allocates a new list of the given size for the field at the given index in the reference
...@@ -313,11 +329,16 @@ public: ...@@ -313,11 +329,16 @@ public:
private: private:
SegmentBuilder* segment; // Memory segment in which the struct resides. SegmentBuilder* segment; // Memory segment in which the struct resides.
word* data; // Pointer to the encoded data. void* data; // Pointer to the encoded data.
WireReference* references; // Pointer to the encoded references. WireReference* references; // Pointer to the encoded references.
inline StructBuilder(SegmentBuilder* segment, word* data, WireReference* references) BitCount8 bit0Offset;
: segment(segment), data(data), references(references) {} // A special hack: When accessing a boolean with field number zero, pretend its offset is this
// instead of the usual zero. This is needed to support 1-bit inline structs.
inline StructBuilder(SegmentBuilder* segment, void* data, WireReference* references,
BitCount8 bit0Offset)
: segment(segment), data(data), references(references), bit0Offset(bit0Offset) {}
friend class ListBuilder; friend class ListBuilder;
friend struct WireHelpers; friend struct WireHelpers;
...@@ -351,6 +372,11 @@ public: ...@@ -351,6 +372,11 @@ public:
// struct reference, which in turn points at the struct value. The default value is allowed to // struct reference, which in turn points at the struct value. The default value is allowed to
// be null, in which case an empty struct is used. // be null, in which case an empty struct is used.
CAPNPROTO_ALWAYS_INLINE(StructReader getInlineStructField(
BitCount dataOffset, BitCount inlineDataSize,
WireReferenceCount refIndex, WireReferenceCount inlineRefCount) const);
// Gets an inlined struct field, given the position and size of the data and pointer sections.
ListReader getListField(WireReferenceCount refIndex, FieldSize expectedElementSize, ListReader getListField(WireReferenceCount refIndex, FieldSize expectedElementSize,
const word* defaultValue) const; const word* defaultValue) const;
// Get the list field at the given index in the reference segment, or the default value if not // Get the list field at the given index in the reference segment, or the default value if not
...@@ -370,20 +396,21 @@ private: ...@@ -370,20 +396,21 @@ private:
const void* data; const void* data;
const WireReference* references; const WireReference* references;
WordCount8 dataSize; // Size of data segment. BitCount32 dataSize; // Size of data segment.
WireReferenceCount8 referenceCount; // Size of the reference segment. WireReferenceCount16 referenceCount; // Size of the reference segment.
BitCount8 bit0Offset; BitCount8 bit0Offset;
// A special hack: When accessing a boolean with field number zero, pretend its offset is this // A special hack: When accessing a boolean with field number zero, pretend its offset is this
// instead of the usual zero. This is needed to allow a boolean list to be upgraded to a list // instead of the usual zero. This is needed to allow a boolean list to be upgraded to a list
// of structs. // of structs, and to support 1-bit inline structs.
int nestingLimit; int nestingLimit;
// Limits the depth of message structures to guard against stack-overflow-based DoS attacks. // Limits the depth of message structures to guard against stack-overflow-based DoS attacks.
// Once this reaches zero, further pointers will be pruned. // Once this reaches zero, further pointers will be pruned.
// TODO: Limit to 8 bits for better alignment?
inline StructReader(SegmentReader* segment, const void* data, const WireReference* references, inline StructReader(SegmentReader* segment, const void* data, const WireReference* references,
WordCount dataSize, WireReferenceCount referenceCount, BitCount dataSize, WireReferenceCount referenceCount,
BitCount bit0Offset, int nestingLimit) BitCount bit0Offset, int nestingLimit)
: segment(segment), data(data), references(references), : segment(segment), data(data), references(references),
dataSize(dataSize), referenceCount(referenceCount), bit0Offset(bit0Offset), dataSize(dataSize), referenceCount(referenceCount), bit0Offset(bit0Offset),
...@@ -448,7 +475,7 @@ public: ...@@ -448,7 +475,7 @@ public:
ListReader asReader(FieldSize elementSize) const; ListReader asReader(FieldSize elementSize) const;
// Get a ListReader pointing at the same memory. Use this version only for non-struct lists. // Get a ListReader pointing at the same memory. Use this version only for non-struct lists.
ListReader asReader(WordCount dataSize, WireReferenceCount referenceCount) const; ListReader asReader(BitCount dataSize, WireReferenceCount referenceCount) const;
// Get a ListReader pointing at the same memory. Use this version only for struct lists. // Get a ListReader pointing at the same memory. Use this version only for struct lists.
private: private:
...@@ -503,7 +530,7 @@ private: ...@@ -503,7 +530,7 @@ private:
// if the sender upgraded a data list to a struct list. It will always be aligned properly for // if the sender upgraded a data list to a struct list. It will always be aligned properly for
// the type. Unsigned so that division by a constant power of 2 is efficient. // the type. Unsigned so that division by a constant power of 2 is efficient.
WordCount structDataSize; BitCount structDataSize;
WireReferenceCount structReferenceCount; WireReferenceCount structReferenceCount;
// If the elements are structs, the properties of the struct. The reference count is // If the elements are structs, the properties of the struct. The reference count is
// only used to check for field presence; the data size is also used to compute the reference // only used to check for field presence; the data size is also used to compute the reference
...@@ -519,7 +546,7 @@ private: ...@@ -519,7 +546,7 @@ private:
structDataSize(0), structReferenceCount(0), structDataSize(0), structReferenceCount(0),
nestingLimit(nestingLimit) {} nestingLimit(nestingLimit) {}
inline ListReader(SegmentReader* segment, const void* ptr, ElementCount elementCount, inline ListReader(SegmentReader* segment, const void* ptr, ElementCount elementCount,
decltype(BITS / ELEMENTS) stepBits, WordCount structDataSize, decltype(BITS / ELEMENTS) stepBits, BitCount structDataSize,
WireReferenceCount structReferenceCount, int nestingLimit) WireReferenceCount structReferenceCount, int nestingLimit)
: segment(segment), ptr(ptr), elementCount(elementCount), stepBits(stepBits), : segment(segment), ptr(ptr), elementCount(elementCount), stepBits(stepBits),
structDataSize(structDataSize), structReferenceCount(structReferenceCount), structDataSize(structDataSize), structReferenceCount(structReferenceCount),
...@@ -541,6 +568,10 @@ inline T StructBuilder::getDataField(ElementCount offset) const { ...@@ -541,6 +568,10 @@ inline T StructBuilder::getDataField(ElementCount offset) const {
template <> template <>
inline bool StructBuilder::getDataField<bool>(ElementCount offset) const { inline bool StructBuilder::getDataField<bool>(ElementCount offset) const {
BitCount boffset = offset * (1 * BITS / ELEMENTS); BitCount boffset = offset * (1 * BITS / ELEMENTS);
// This branch should always be optimized away when inlining.
if (boffset == 0 * BITS) boffset = bit0Offset;
byte* b = reinterpret_cast<byte*>(data) + boffset / BITS_PER_BYTE; byte* b = reinterpret_cast<byte*>(data) + boffset / BITS_PER_BYTE;
return (*reinterpret_cast<uint8_t*>(b) & (1 << (boffset % BITS_PER_BYTE / BITS))) != 0; return (*reinterpret_cast<uint8_t*>(b) & (1 << (boffset % BITS_PER_BYTE / BITS))) != 0;
} }
...@@ -564,6 +595,10 @@ inline void StructBuilder::setDataField( ...@@ -564,6 +595,10 @@ inline void StructBuilder::setDataField(
template <> template <>
inline void StructBuilder::setDataField<bool>(ElementCount offset, bool value) const { inline void StructBuilder::setDataField<bool>(ElementCount offset, bool value) const {
BitCount boffset = offset * (1 * BITS / ELEMENTS); BitCount boffset = offset * (1 * BITS / ELEMENTS);
// This branch should always be optimized away when inlining.
if (boffset == 0 * BITS) boffset = bit0Offset;
byte* b = reinterpret_cast<byte*>(data) + boffset / BITS_PER_BYTE; byte* b = reinterpret_cast<byte*>(data) + boffset / BITS_PER_BYTE;
uint bitnum = boffset % BITS_PER_BYTE / BITS; uint bitnum = boffset % BITS_PER_BYTE / BITS;
*reinterpret_cast<uint8_t*>(b) = (*reinterpret_cast<uint8_t*>(b) & ~(1 << bitnum)) *reinterpret_cast<uint8_t*>(b) = (*reinterpret_cast<uint8_t*>(b) & ~(1 << bitnum))
...@@ -579,11 +614,37 @@ inline void StructBuilder::setDataField( ...@@ -579,11 +614,37 @@ inline void StructBuilder::setDataField(
setDataField<typename MaskType<T>::Type>(offset, mask<T>(value, m)); setDataField<typename MaskType<T>::Type>(offset, mask<T>(value, m));
} }
inline StructBuilder StructBuilder::initInlineStructField(
BitCount dataOffset, BitCount inlineDataSize,
WireReferenceCount refIndex, WireReferenceCount inlineRefCount) const {
// This branch should be optimized away.
if (inlineDataSize == 1 * BITS) {
setDataField<bool>(dataOffset / (1 * BITS / ELEMENTS), false);
} else {
memset(reinterpret_cast<byte*>(data) + dataOffset / BITS_PER_BYTE / BYTES,
0, inlineDataSize / BITS_PER_BYTE / BYTES);
}
memset(reinterpret_cast<word*>(references) + refIndex * WORDS_PER_REFERENCE,
0, inlineRefCount * WORDS_PER_REFERENCE * BYTES_PER_WORD / BYTES);
return getInlineStructField(dataOffset, inlineDataSize, refIndex, inlineRefCount);
}
inline StructBuilder StructBuilder::getInlineStructField(
BitCount dataOffset, BitCount inlineDataSize,
WireReferenceCount refIndex, WireReferenceCount inlineRefCount) const {
return StructBuilder(
segment, reinterpret_cast<byte*>(data) + dataOffset / BITS_PER_BYTE,
// WireReference is incomplete here so we have to cast around... Bah.
reinterpret_cast<WireReference*>(
reinterpret_cast<word*>(references) + refIndex * WORDS_PER_REFERENCE),
dataOffset == 0 * BITS ? BitCount(bit0Offset) : dataOffset % BITS_PER_BYTE);
}
// ------------------------------------------------------------------- // -------------------------------------------------------------------
template <typename T> template <typename T>
T StructReader::getDataField(ElementCount offset) const { T StructReader::getDataField(ElementCount offset) const {
if (offset * bytesPerElement<T>() < dataSize * BYTES_PER_WORD) { if (offset * capnproto::bitsPerElement<T>() < dataSize) {
return reinterpret_cast<const WireValue<T>*>(data)[offset / ELEMENTS].get(); return reinterpret_cast<const WireValue<T>*>(data)[offset / ELEMENTS].get();
} else { } else {
return static_cast<T>(0); return static_cast<T>(0);
...@@ -597,7 +658,7 @@ inline bool StructReader::getDataField<bool>(ElementCount offset) const { ...@@ -597,7 +658,7 @@ inline bool StructReader::getDataField<bool>(ElementCount offset) const {
// This branch should always be optimized away when inlining. // This branch should always be optimized away when inlining.
if (boffset == 0 * BITS) boffset = bit0Offset; if (boffset == 0 * BITS) boffset = bit0Offset;
if (boffset < dataSize * BITS_PER_WORD) { if (boffset < dataSize) {
const byte* b = reinterpret_cast<const byte*>(data) + boffset / BITS_PER_BYTE; const byte* b = reinterpret_cast<const byte*>(data) + boffset / BITS_PER_BYTE;
return (*reinterpret_cast<const uint8_t*>(b) & (1 << (boffset % BITS_PER_BYTE / BITS))) != 0; return (*reinterpret_cast<const uint8_t*>(b) & (1 << (boffset % BITS_PER_BYTE / BITS))) != 0;
} else { } else {
...@@ -615,6 +676,19 @@ T StructReader::getDataField(ElementCount offset, typename MaskType<T>::Type mas ...@@ -615,6 +676,19 @@ T StructReader::getDataField(ElementCount offset, typename MaskType<T>::Type mas
return unmask<T>(getDataField<typename MaskType<T>::Type>(offset), mask); return unmask<T>(getDataField<typename MaskType<T>::Type>(offset), mask);
} }
inline StructReader StructReader::getInlineStructField(
BitCount dataOffset, BitCount inlineDataSize,
WireReferenceCount refIndex, WireReferenceCount inlineRefCount) const {
return StructReader(
segment, reinterpret_cast<const byte*>(data) + dataOffset / BITS_PER_BYTE,
// WireReference is incomplete here so we have to cast around... Bah.
reinterpret_cast<const WireReference*>(
reinterpret_cast<const word*>(references) + refIndex * WORDS_PER_REFERENCE),
dataSize, inlineRefCount,
dataOffset == 0 * BITS ? BitCount(bit0Offset) : dataOffset % BITS_PER_BYTE,
nestingLimit);
}
// ------------------------------------------------------------------- // -------------------------------------------------------------------
inline ElementCount ListBuilder::size() { return elementCount; } inline ElementCount ListBuilder::size() { return elementCount; }
......
...@@ -57,6 +57,9 @@ using ::capnproto::test::TestUnion; ...@@ -57,6 +57,9 @@ using ::capnproto::test::TestUnion;
using ::capnproto::test::TestUnionDefaults; using ::capnproto::test::TestUnionDefaults;
using ::capnproto::test::TestNestedTypes; using ::capnproto::test::TestNestedTypes;
using ::capnproto::test::TestUsing; using ::capnproto::test::TestUsing;
using ::capnproto::test::TestInlineLayout;
using ::capnproto::test::TestInlineUnions;
using ::capnproto::test::TestInlineDefaults;
void initTestMessage(test::TestAllTypes::Builder builder); void initTestMessage(test::TestAllTypes::Builder builder);
void initTestMessage(test::TestDefaults::Builder builder); void initTestMessage(test::TestDefaults::Builder builder);
......
...@@ -213,7 +213,6 @@ struct TestUnion { ...@@ -213,7 +213,6 @@ struct TestUnion {
bit5 @42: Bool; bit5 @42: Bool;
bit6 @43: Bool; bit6 @43: Bool;
bit7 @44: Bool; bit7 @44: Bool;
byte0 @49: UInt8;
# Interleave two unions to be really annoying. # Interleave two unions to be really annoying.
# Also declare in reverse order to make sure union discriminant values are sorted by field number # Also declare in reverse order to make sure union discriminant values are sorted by field number
...@@ -233,6 +232,8 @@ struct TestUnion { ...@@ -233,6 +232,8 @@ struct TestUnion {
u3f0s8 @48: Int8; u3f0s8 @48: Int8;
u3f0s1 @46: Bool; u3f0s1 @46: Bool;
} }
byte0 @49: UInt8;
} }
struct TestUnionDefaults { struct TestUnionDefaults {
...@@ -274,3 +275,140 @@ struct TestUsing { ...@@ -274,3 +275,140 @@ struct TestUsing {
outerNestedEnum @1 :OuterNestedEnum = bar; outerNestedEnum @1 :OuterNestedEnum = bar;
innerNestedEnum @0 :NestedEnum = quux; innerNestedEnum @0 :NestedEnum = quux;
} }
struct TestInline0 fixed(0 bits) {}
struct TestInline1 fixed(1 bits) { f @0: Bool; }
struct TestInline8 fixed(8 bits) { f0 @0: Bool; f1 @1: Bool; f2 @2: Bool; }
struct TestInline16 fixed(16 bits) { f0 @0: UInt8; f1 @1: UInt8; }
struct TestInline32 fixed(32 bits) { f0 @0: UInt8; f1 @1: UInt16; }
struct TestInline64 fixed(64 bits) { f0 @0: UInt8; f1 @1: UInt32; }
struct TestInline128 fixed(2 words) { f0 @0: UInt64; f1 @1: UInt64; }
struct TestInline192 fixed(3 words) { f0 @0: UInt64; f1 @1: UInt64; f2 @2: UInt64; }
struct TestInline0p fixed(0 bits, 1 pointers) { f @0 :Inline(TestInline0); p0 @1 :Text; }
struct TestInline1p fixed(1 bits, 1 pointers) { f @0 :Inline(TestInline1); p0 @1 :Text; }
struct TestInline8p fixed(8 bits, 1 pointers) { f @0 :Inline(TestInline8); p0 @1 :Text; }
struct TestInline16p fixed(16 bits, 2 pointers) { f @0 :Inline(TestInline16); p0 @1 :Text; p1 @2 :Text; }
struct TestInline32p fixed(32 bits, 2 pointers) { f @0 :Inline(TestInline32); p0 @1 :Text; p1 @2 :Text; }
struct TestInline64p fixed(64 bits, 2 pointers) { f @0 :Inline(TestInline64); p0 @1 :Text; p1 @2 :Text; }
struct TestInline128p fixed(2 words, 3 pointers) { f @0 :Inline(TestInline128); p0 @1 :Text; p1 @2 :Text; p2 @3 :Text; }
struct TestInline192p fixed(3 words, 3 pointers) { f @0 :Inline(TestInline192); p0 @1 :Text; p1 @2 :Text; p2 @3 :Text; }
struct TestInlineLayout {
f0 @0 :Inline(TestInline0);
f1 @1 :Inline(TestInline1);
f8 @2 :Inline(TestInline8);
f16 @3 :Inline(TestInline16);
f32 @4 :Inline(TestInline32);
f64 @5 :Inline(TestInline64);
f128 @6 :Inline(TestInline128);
f192 @7 :Inline(TestInline192);
f0p @8 :Inline(TestInline0p);
f1p @9 :Inline(TestInline1p);
f8p @10 :Inline(TestInline8p);
f16p @11 :Inline(TestInline16p);
f32p @12 :Inline(TestInline32p);
f64p @13 :Inline(TestInline64p);
f128p @14 :Inline(TestInline128p);
f192p @15 :Inline(TestInline192p);
f1Offset @16 :Inline(TestInline1);
bit @17 :Bool;
}
struct TestInlineUnions {
union0 @0 union {
f0 @4 :Inline(TestInline0);
f1 @5 :Inline(TestInline1);
f8 @6 :Inline(TestInline8);
f16 @7 :Inline(TestInline16);
f32 @8 :Inline(TestInline32);
f64 @9 :Inline(TestInline64);
f128 @10 :Inline(TestInline128);
f192 @11 :Inline(TestInline192);
f0p @12 :Inline(TestInline0p);
f1p @13 :Inline(TestInline1p);
f8p @14 :Inline(TestInline8p);
f16p @15 :Inline(TestInline16p);
f32p @16 :Inline(TestInline32p);
f64p @17 :Inline(TestInline64p);
f128p @18 :Inline(TestInline128p);
f192p @19 :Inline(TestInline192p);
}
# Pack one bit in order to make pathological situation for union1.
bit0 @20: Bool;
union1 @1 union {
f0 @21 :Inline(TestInline0);
f1 @22 :Inline(TestInline1);
f8 @23 :Inline(TestInline8);
f16 @24 :Inline(TestInline16);
f32 @25 :Inline(TestInline32);
f64 @26 :Inline(TestInline64);
f128 @27 :Inline(TestInline128);
f192 @28 :Inline(TestInline192);
}
# Fill in the rest of that bitfield from earlier.
bit2 @29: Bool;
bit3 @30: Bool;
bit4 @31: Bool;
bit5 @32: Bool;
bit6 @33: Bool;
bit7 @34: Bool;
# Interleave two unions to be really annoying.
union2 @2 union {
f1p @35 :Inline(TestInline1p);
f8p @37 :Inline(TestInline8p);
f16p @40 :Inline(TestInline16p);
f32p @42 :Inline(TestInline32p);
f64p @44 :Inline(TestInline64p);
f128p @46 :Inline(TestInline128p);
f192p @48 :Inline(TestInline192p);
}
union3 @3 union {
f1p @36 :Inline(TestInline1p);
f8p @38 :Inline(TestInline8p);
f16p @41 :Inline(TestInline16p);
f32p @43 :Inline(TestInline32p);
f64p @45 :Inline(TestInline64p);
f128p @47 :Inline(TestInline128p);
f192p @49 :Inline(TestInline192p);
}
byte0 @39: UInt8;
}
struct TestInlineDefaults {
normal @0 :TestInlineLayout = (
f0 = (),
f1 = (f = true),
f8 = (f0 = true, f1 = false, f2 = true),
f16 = (f0 = 123, f1 = 45),
f32 = (f0 = 67, f1 = 8901),
f64 = (f0 = 234, f1 = 567890123),
f128 = (f0 = 1234567890123, f1 = 4567890123456),
f192 = (f0 = 7890123456789, f1 = 2345678901234, f2 = 5678901234567),
f0p = (p0 = "foo"),
f1p = (f = (f = false), p0 = "bar"),
f8p = (f = (f0 = true, f1 = true, f2 = false), p0 = "baz"),
f16p = (f = (f0 = 98, f1 = 76), p0 = "qux", p1 = "quux"),
f32p = (f = (f0 = 54, f1 = 32109), p0 = "corge", p1 = "grault"),
f64p = (f = (f0 = 87, f1 = 654321098), p0 = "garply", p1 = "waldo"),
f128p = (f = (f0 = 7654321098765, f1 = 4321098765432),
p0 = "fred", p1 = "plugh", p2 = "xyzzy"),
f192p = (f = (f0 = 1098765432109, f1 = 8765432109876, f2 = 5432109876543),
p0 = "thud", p1 = "foobar", p2 = "barbaz"));
unions @1 :TestInlineUnions = (
union0 = f32(f0 = 67, f1 = 8901),
union1 = f128(f0 = 1234567890123, f1 = 4567890123456),
union2 = f1p(p0 = "foo"),
union3 = f16p(f = (f0 = 98, f1 = 76), p0 = "qux", p1 = "quux"));
}
...@@ -651,6 +651,11 @@ inline constexpr decltype(BYTES / ELEMENTS) bytesPerElement() { ...@@ -651,6 +651,11 @@ inline constexpr decltype(BYTES / ELEMENTS) bytesPerElement() {
return sizeof(T) * BYTES / ELEMENTS; return sizeof(T) * BYTES / ELEMENTS;
} }
template <typename T>
inline constexpr decltype(BITS / ELEMENTS) bitsPerElement() {
return sizeof(T) * 8 * BITS / ELEMENTS;
}
#ifndef __CDT_PARSER__ #ifndef __CDT_PARSER__
template <typename T, typename U> template <typename T, typename U>
......
...@@ -4,6 +4,13 @@ cabal-version: >=1.2 ...@@ -4,6 +4,13 @@ cabal-version: >=1.2
build-type: Simple build-type: Simple
author: kenton author: kenton
-- How to get stack traces:
-- 1. Compile normally and do not clean.
-- 2. Add "-prof -fprof-auto -osuf .prof.o" to ghc-options and compile again.
-- (TODO: Figure out how to add these through "cabal configure" instead of by editing
-- this file. --enable-executable-profiling alone doesn't appear to get the job done.)
-- 3. Run with +RTS -xc -RTS on the command line.
executable capnpc executable capnpc
hs-source-dirs: src hs-source-dirs: src
main-is: Main.hs main-is: Main.hs
......
...@@ -27,12 +27,12 @@ import Grammar ...@@ -27,12 +27,12 @@ import Grammar
import Semantics import Semantics
import Token(Located(Located), locatedPos, locatedValue) import Token(Located(Located), locatedPos, locatedValue)
import Parser(parseFile) import Parser(parseFile)
import Control.Monad(unless) import Control.Monad(when, unless)
import qualified Data.Map as Map import qualified Data.Map as Map
import Data.Map((!)) import Data.Map((!))
import qualified Data.Set as Set import qualified Data.Set as Set
import qualified Data.List as List import qualified Data.List as List
import Data.Maybe(mapMaybe, fromMaybe, listToMaybe, catMaybes) import Data.Maybe(mapMaybe, fromMaybe, listToMaybe, catMaybes, isJust)
import Text.Parsec.Pos(SourcePos, newPos) import Text.Parsec.Pos(SourcePos, newPos)
import Text.Parsec.Error(ParseError, newErrorMessage, Message(Message, Expect)) import Text.Parsec.Error(ParseError, newErrorMessage, Message(Message, Expect))
import Text.Printf(printf) import Text.Printf(printf)
...@@ -67,9 +67,16 @@ instance Monad Status where ...@@ -67,9 +67,16 @@ instance Monad Status where
return x = Active x [] return x = Active x []
fail = makeError (newPos "?" 0 0) fail = makeError (newPos "?" 0 0)
-- Recovers from Failed status by using a fallback result, but keeps the errors.
--
-- This function is carefully written such that the runtime can see that it returns Active without
-- actually evaluating the parameters. The parameters are only evaluated when the returned value
-- or errors are examined.
recover :: a -> Status a -> Status a recover :: a -> Status a -> Status a
recover _ (Active x e) = Active x e recover fallback status = Active value errs where
recover x (Failed e) = Active x e (value, errs) = case status of
Active v e -> (v, e)
Failed e -> (fallback, e)
succeed :: a -> Status a succeed :: a -> Status a
succeed x = Active x [] succeed x = Active x []
...@@ -154,7 +161,7 @@ lookupDesc scope name = lookupDesc (descParent scope) name ...@@ -154,7 +161,7 @@ lookupDesc scope name = lookupDesc (descParent scope) name
builtinTypeMap :: Map.Map String Desc builtinTypeMap :: Map.Map String Desc
builtinTypeMap = Map.fromList builtinTypeMap = Map.fromList
([(builtinTypeName t, DescBuiltinType t) | t <- builtinTypes] ++ ([(builtinTypeName t, DescBuiltinType t) | t <- builtinTypes] ++
[("List", DescBuiltinList), ("id", DescBuiltinId)]) [("List", DescBuiltinList), ("Inline", DescBuiltinInline), ("id", DescBuiltinId)])
------------------------------------------------------------------------------------------ ------------------------------------------------------------------------------------------
...@@ -234,6 +241,8 @@ compileValue pos (StructType desc) (RecordFieldValue fields) = do ...@@ -234,6 +241,8 @@ compileValue pos (StructType desc) (RecordFieldValue fields) = do
return (StructValueDesc assignments) return (StructValueDesc assignments)
compileValue pos (InlineStructType desc) v = compileValue pos (StructType desc) v
compileValue _ (ListType t) (ListFieldValue l) = compileValue _ (ListType t) (ListFieldValue l) =
fmap ListDesc (doAll [ compileValue vpos t v | Located vpos v <- l ]) fmap ListDesc (doAll [ compileValue vpos t v | Located vpos v <- l ])
...@@ -254,6 +263,7 @@ compileValue pos (BuiltinType BuiltinData) _ = makeExpectError pos "string" ...@@ -254,6 +263,7 @@ compileValue pos (BuiltinType BuiltinData) _ = makeExpectError pos "string"
compileValue pos (EnumType _) _ = makeExpectError pos "enumerant name" compileValue pos (EnumType _) _ = makeExpectError pos "enumerant name"
compileValue pos (StructType _) _ = makeExpectError pos "parenthesized list of field assignments" compileValue pos (StructType _) _ = makeExpectError pos "parenthesized list of field assignments"
compileValue pos (InlineStructType _) _ = makeExpectError pos "parenthesized list of field assignments"
compileValue pos (InterfaceType _) _ = makeError pos "Interfaces can't have default values." compileValue pos (InterfaceType _) _ = makeError pos "Interfaces can't have default values."
compileValue pos (ListType _) _ = makeExpectError pos "list" compileValue pos (ListType _) _ = makeExpectError pos "list"
...@@ -264,6 +274,8 @@ descAsType _ (DescBuiltinType desc) = succeed (BuiltinType desc) ...@@ -264,6 +274,8 @@ descAsType _ (DescBuiltinType desc) = succeed (BuiltinType desc)
descAsType name (DescUsing desc) = descAsType name (usingTarget desc) descAsType name (DescUsing desc) = descAsType name (usingTarget desc)
descAsType name DescBuiltinList = makeError (declNamePos name) message where descAsType name DescBuiltinList = makeError (declNamePos name) message where
message = printf "'List' requires exactly one type parameter." (declNameString name) message = printf "'List' requires exactly one type parameter." (declNameString name)
descAsType name DescBuiltinInline = makeError (declNamePos name) message where
message = printf "'Inline' requires exactly one type parameter." (declNameString name)
descAsType name _ = makeError (declNamePos name) message where descAsType name _ = makeError (declNamePos name) message where
message = printf "'%s' is not a type." (declNameString name) message = printf "'%s' is not a type." (declNameString name)
...@@ -278,6 +290,18 @@ compileType scope (TypeExpression n (param:moreParams)) = do ...@@ -278,6 +290,18 @@ compileType scope (TypeExpression n (param:moreParams)) = do
if null moreParams if null moreParams
then fmap ListType (compileType scope param) then fmap ListType (compileType scope param)
else makeError (declNamePos n) "'List' requires exactly one type parameter." else makeError (declNamePos n) "'List' requires exactly one type parameter."
DescBuiltinInline ->
if null moreParams
then do
inner <- compileType scope param
case inner of
StructType s -> if structIsFixedWidth s
then return (InlineStructType s)
else makeError (declNamePos n) $
printf "'%s' cannot be inlined because it is not fixed-width."
(structName s)
_ -> makeError (declNamePos n) "'Inline' parameter must be a struct type."
else makeError (declNamePos n) "'Inline' requires exactly one type parameter."
_ -> makeError (declNamePos n) "Only the type 'List' can have type parameters." _ -> makeError (declNamePos n) "Only the type 'List' can have type parameters."
compileAnnotation :: Desc -> AnnotationTarget -> Annotation compileAnnotation :: Desc -> AnnotationTarget -> Annotation
...@@ -378,10 +402,6 @@ requireNoDuplicateNames decls = Active () (loop (List.sort locatedNames)) where ...@@ -378,10 +402,6 @@ requireNoDuplicateNames decls = Active () (loop (List.sort locatedNames)) where
dupError val = newErrorMessage (Message message) where dupError val = newErrorMessage (Message message) where
message = printf "Duplicate declaration \"%s\"." val message = printf "Duplicate declaration \"%s\"." val
fieldInUnion name f = case fieldUnion f of
Nothing -> False
Just (x, _) -> unionName x == name
requireNoMoreThanOneFieldNumberLessThan name pos num fields = Active () errors where requireNoMoreThanOneFieldNumberLessThan name pos num fields = Active () errors where
retroFields = [fieldName f | f <- fields, fieldNumber f < num] retroFields = [fieldName f | f <- fields, fieldNumber f < num]
message = printf "No more than one field in a union may have a number less than the \ message = printf "No more than one field in a union may have a number less than the \
...@@ -399,102 +419,217 @@ extractFieldNumbers decls = concat ...@@ -399,102 +419,217 @@ extractFieldNumbers decls = concat
------------------------------------------------------------------------------------------ ------------------------------------------------------------------------------------------
initialPackingState = PackingState 0 0 0 0 0 0 data PackingState = PackingState
{ packingHoles :: Map.Map DataSize Integer
, packingDataSize :: Integer
, packingReferenceCount :: Integer
}
packValue :: FieldSize -> PackingState -> (Integer, PackingState) initialPackingState = PackingState Map.empty 0 0
packValue Size64 s@(PackingState { packingDataSize = ds }) =
(ds, s { packingDataSize = ds + 1 }) packValue :: FieldSize -> PackingState -> (FieldOffset, PackingState)
packValue SizeVoid s = (VoidOffset, s)
packValue SizeReference s@(PackingState { packingReferenceCount = rc }) = packValue SizeReference s@(PackingState { packingReferenceCount = rc }) =
(rc, s { packingReferenceCount = rc + 1 }) (PointerOffset rc, s { packingReferenceCount = rc + 1 })
packValue (SizeInlineComposite _ _) _ = error "Inline fields not yet supported." packValue (SizeInlineComposite (DataSectionWords inlineDs) inlineRc)
packValue Size32 s@(PackingState { packingHole32 = 0 }) = s@(PackingState { packingDataSize = ds, packingReferenceCount = rc }) =
case packValue Size64 s of (InlineCompositeOffset ds rc (DataSectionWords inlineDs) inlineRc,
(o64, s2) -> (o64 * 2, s2 { packingHole32 = o64 * 2 + 1 }) s { packingDataSize = ds + inlineDs
packValue Size32 s@(PackingState { packingHole32 = h32 }) = , packingReferenceCount = rc + inlineRc })
(h32, s { packingHole32 = 0 }) packValue (SizeInlineComposite inlineDs inlineRc)
packValue Size16 s@(PackingState { packingHole16 = 0 }) = s@(PackingState { packingReferenceCount = rc }) = let
case packValue Size32 s of size = (dataSectionAlignment inlineDs)
(o32, s2) -> (o32 * 2, s2 { packingHole16 = o32 * 2 + 1 }) (offset, s2) = packData size s
packValue Size16 s@(PackingState { packingHole16 = h16 }) = in (InlineCompositeOffset offset rc inlineDs inlineRc,
(h16, s { packingHole16 = 0 }) s2 { packingReferenceCount = rc + inlineRc })
packValue Size8 s@(PackingState { packingHole8 = 0 }) = packValue (SizeData size) s = let (o, s2) = packData size s in (DataOffset size o, s2)
case packValue Size16 s of
(o16, s2) -> (o16 * 2, s2 { packingHole8 = o16 * 2 + 1 }) packData :: DataSize -> PackingState -> (Integer, PackingState)
packValue Size8 s@(PackingState { packingHole8 = h8 }) = packData Size64 s@(PackingState { packingDataSize = ds }) =
(h8, s { packingHole8 = 0 }) (ds, s { packingDataSize = ds + 1 })
packValue Size1 s@(PackingState { packingHole1 = 0 }) =
case packValue Size8 s of packData size s = let
(o8, s2) -> (o8 * 8, s2 { packingHole1 = o8 * 8 + 1 }) -- updateLookupWithKey doesn't quite work here because it returns the new value if updated, or
packValue Size1 s@(PackingState { packingHole1 = h1 }) = -- the old value if not. We really always want the old value and have no way to distinguish.
(h1, s { packingHole1 = if mod (h1 + 1) 8 == 0 then 0 else h1 + 1 }) -- There appears to be no function that does this, AFAICT.
packValue Size0 s = (0, s) hole = Map.lookup size $ packingHoles s
newHoles = Map.update splitHole size $ packingHoles s
initialUnionPackingState = UnionPackingState Nothing Nothing splitHole off = case size of
Size1 -> if mod off 8 == 7 then Nothing else Just (off + 1)
_ -> Nothing
in case hole of
-- If there was a hole of the correct size, use it.
Just off -> (off, s { packingHoles = newHoles })
-- Otherwise, try to pack a value of the next size up, and then split it.
Nothing -> let
nextSize = succ size
(nextOff, s2) = packData nextSize s
off = demoteOffset nextSize nextOff
newHoles2 = Map.insert size (off + 1) $ packingHoles s2
in (off, s2 { packingHoles = newHoles2 })
-- Convert an offset of one data size to an offset of the next smaller size.
demoteOffset :: DataSize -> Integer -> Integer
demoteOffset Size1 _ = error "can't split bit"
demoteOffset Size8 i = i * 8
demoteOffset _ i = i * 2
data UnionSlot sizeType = UnionSlot { unionSlotSize :: sizeType, unionSlotOffset :: Integer }
data UnionPackingState = UnionPackingState
{ unionDataSlot :: UnionSlot DataSectionSize
, unionPointerSlot :: UnionSlot Integer }
initialUnionPackingState = UnionPackingState (UnionSlot (DataSectionWords 0) 0) (UnionSlot 0 0)
packUnionizedValue :: FieldSize -- Size of field to pack. packUnionizedValue :: FieldSize -- Size of field to pack.
-> UnionPackingState -- Current layout of the union -> UnionPackingState -- Current layout of the union
-> PackingState -- Current layout of the struct. -> PackingState -- Current layout of the struct.
-> (Integer, UnionPackingState, PackingState) -> (FieldOffset, UnionPackingState, PackingState)
packUnionizedValue (SizeInlineComposite _ _) _ _ = error "Can't put inline composite into union."
packUnionizedValue Size0 u s = (0, u, s) packUnionizedValue SizeVoid u s = (VoidOffset, u, s)
-- Pack reference when we already have a reference slot allocated. -- Pack data when there is no existing slot.
packUnionizedValue SizeReference u@(UnionPackingState _ (Just offset)) s = (offset, u, s) packUnionizedValue (SizeData size) (UnionPackingState (UnionSlot (DataSectionWords 0) _) p) s =
let (offset, s2) = packData size s
in (DataOffset size offset,
UnionPackingState (UnionSlot (dataSizeToSectionSize size) offset) p, s2)
-- Pack data when there is a word-sized slot. All data fits in a word.
packUnionizedValue (SizeData size)
ups@(UnionPackingState (UnionSlot (DataSectionWords _) offset) _) s =
(DataOffset size (offset * div 64 (dataSizeInBits size)), ups, s)
-- Pack data when there is a non-word-sized slot.
packUnionizedValue (SizeData size) (UnionPackingState (UnionSlot slotSize slotOffset) p) s =
case tryExpandSubWordDataSlot (dataSectionAlignment slotSize, slotOffset) s size of
Just (offset, (newSlotSize, newSlotOffset), s2) ->
(DataOffset size offset,
UnionPackingState (UnionSlot (dataSizeToSectionSize newSlotSize) newSlotOffset) p, s2)
-- If the slot wasn't big enough, pack as if there were no slot.
Nothing -> packUnionizedValue (SizeData size)
(UnionPackingState (UnionSlot (DataSectionWords 0) 0) p) s
-- Pack reference when we don't have a reference slot. -- Pack reference when we don't have a reference slot.
packUnionizedValue SizeReference (UnionPackingState d Nothing) s = (offset, u2, s2) where packUnionizedValue SizeReference u@(UnionPackingState _ (UnionSlot 0 _)) s = let
(offset, s2) = packValue SizeReference s (PointerOffset offset, s2) = packValue SizeReference s
u2 = UnionPackingState d (Just offset) u2 = u { unionPointerSlot = UnionSlot 1 offset }
in (PointerOffset offset, u2, s2)
-- Pack data.
packUnionizedValue size (UnionPackingState d r) s = -- Pack reference when we already have a reference slot allocated.
case packUnionizedData (fromMaybe (0, Size0) d) s size of packUnionizedValue SizeReference u@(UnionPackingState _ (UnionSlot _ offset)) s =
Just (offset, slotOffset, slotSize, s2) -> (PointerOffset offset, u, s)
(offset, UnionPackingState (Just (slotOffset, slotSize)) r, s2)
Nothing -> let -- Pack inline composite.
(offset, s2) = packValue size s packUnionizedValue (SizeInlineComposite dataSize pointerCount)
in (offset, UnionPackingState (Just (offset, size)) r, s2) u@(UnionPackingState { unionDataSlot = UnionSlot dataSlotSize dataSlotOffset
, unionPointerSlot = UnionSlot pointerSlotSize pointerSlotOffset })
s = let
-- Pack the data section.
(dataOffset, u2, s2) = case dataSize of
DataSectionWords 0 -> (0, u, s)
DataSectionWords requestedWordSize -> let
maybeExpanded = case dataSlotSize of
-- Try to expand existing n-word slot to fit.
DataSectionWords existingWordSize ->
tryExpandUnionizedDataWords u s
dataSlotOffset existingWordSize requestedWordSize
-- Try to expand the existing sub-word slot into a word, then from there to a slot
-- of the size we need.
_ -> do
(expandedSlotOffset, _, expandedPackingState) <-
tryExpandSubWordDataSlot (dataSectionAlignment dataSlotSize, dataSlotOffset)
s Size64
let newU = u { unionDataSlot =
UnionSlot (DataSectionWords 1) expandedSlotOffset }
tryExpandUnionizedDataWords newU expandedPackingState
expandedSlotOffset 1 requestedWordSize
-- If expanding fails, fall back to appending the new words to the end of the struct.
atEnd = (packingDataSize s,
u { unionDataSlot = UnionSlot (DataSectionWords requestedWordSize)
(packingDataSize s) },
s { packingDataSize = packingDataSize s + requestedWordSize })
in fromMaybe atEnd maybeExpanded
_ -> let
(DataOffset _ result, newU, newS) =
packUnionizedValue (SizeData (dataSectionAlignment dataSize)) u s
in (result, newU, newS)
-- Pack the pointer section.
(pointerOffset, u3, s3)
| pointerCount <= pointerSlotSize = (pointerSlotOffset, u2, s2)
| pointerSlotOffset + pointerSlotSize == packingReferenceCount s2 =
(pointerSlotOffset,
u2 { unionPointerSlot = UnionSlot pointerCount pointerSlotOffset },
s2 { packingReferenceCount = pointerSlotOffset + pointerCount })
| otherwise =
(packingReferenceCount s2,
u2 { unionPointerSlot = UnionSlot pointerCount (packingReferenceCount s2) },
s2 { packingReferenceCount = packingReferenceCount s2 + pointerCount })
combinedOffset = InlineCompositeOffset
{ inlineCompositeDataOffset = dataOffset
, inlineCompositePointerOffset = pointerOffset
, inlineCompositeDataSize = dataSize
, inlineCompositePointerSize = pointerCount
}
packUnionizedData :: (Integer, FieldSize) -- existing slot to expand in (combinedOffset, u3, s3)
tryExpandUnionizedDataWords unionState packingState existingOffset existingSize requestedSize
-- Is the existing multi-word slot big enough?
| requestedSize <= existingSize =
-- Yes, use it.
Just (existingOffset, unionState, packingState)
-- Is the slot at the end of the struct?
| existingOffset + existingSize == packingDataSize packingState =
-- Yes, expand it.
Just (existingOffset,
unionState { unionDataSlot = UnionSlot (DataSectionWords requestedSize)
existingOffset },
packingState { packingDataSize = packingDataSize packingState
+ requestedSize - existingSize })
| otherwise = Nothing
-- Try to expand an existing data slot to be big enough for a data field.
tryExpandSubWordDataSlot :: (DataSize, Integer) -- existing slot to expand
-> PackingState -- existing packing state -> PackingState -- existing packing state
-> FieldSize -- desired field size -> DataSize -- desired field size
-> Maybe (Integer, -- Offset of the new field (in multiples of field size). -> Maybe (Integer, -- Offset of the new field.
Integer, -- New offset of the slot (in multiples of slot size). (DataSize, Integer), -- New offset of the slot.
FieldSize, -- New size of the slot.
PackingState) -- New struct packing state. PackingState) -- New struct packing state.
-- Don't try to allocate space for voids.
packUnionizedData (slotOffset, slotSize) state Size0 = Just (0, slotOffset, slotSize, state)
-- If slot is bigger than desired size, no expansion is needed. -- If slot is bigger than desired size, no expansion is needed.
packUnionizedData (slotOffset, slotSize) state desiredSize tryExpandSubWordDataSlot (slotSize, slotOffset) state desiredSize
| sizeInBits slotSize >= sizeInBits desiredSize = | dataSizeInBits slotSize >= dataSizeInBits desiredSize =
Just (div (sizeInBits slotSize) (sizeInBits desiredSize) * slotOffset, Just (div (dataSizeInBits slotSize) (dataSizeInBits desiredSize) * slotOffset,
slotOffset, slotSize, state) (slotSize, slotOffset), state)
-- If slot is a bit, and it is the first bit in its byte, and the bit hole immediately follows -- Try expanding the slot by combining it with subsequent padding.
-- expand it to a byte. tryExpandSubWordDataSlot (slotSize, slotOffset) state desiredSize = let
packUnionizedData (slotOffset, Size1) p@(PackingState { packingHole1 = hole }) desiredSize nextSize = succ slotSize
| mod slotOffset 8 == 0 && hole == slotOffset + 1 = ratio = div (dataSizeInBits nextSize) (dataSizeInBits slotSize)
packUnionizedData (div slotOffset 8, Size8) (p { packingHole1 = 0 }) desiredSize isAligned = mod slotOffset ratio == 0
nextOffset = div slotOffset ratio
-- If slot is size N, and the next N bits are padding, expand.
packUnionizedData (slotOffset, Size8) p@(PackingState { packingHole8 = hole }) desiredSize deleteHole _ _ = Nothing
| hole == slotOffset + 1 = (maybeHole, newHoles) = Map.updateLookupWithKey deleteHole slotSize $ packingHoles state
packUnionizedData (div slotOffset 2, Size16) (p { packingHole8 = 0 }) desiredSize newState = state { packingHoles = newHoles }
packUnionizedData (slotOffset, Size16) p@(PackingState { packingHole16 = hole }) desiredSize
| hole == slotOffset + 1 = in if not isAligned
packUnionizedData (div slotOffset 2, Size32) (p { packingHole16 = 0 }) desiredSize then Nothing -- Existing slot is not aligned properly.
packUnionizedData (slotOffset, Size32) p@(PackingState { packingHole32 = hole }) desiredSize else case maybeHole of
| hole == slotOffset + 1 = Just holeOffset | holeOffset == slotOffset + 1 ->
packUnionizedData (div slotOffset 2, Size64) (p { packingHole32 = 0 }) desiredSize tryExpandSubWordDataSlot (nextSize, nextOffset) newState desiredSize
_ -> Nothing
-- Otherwise, we fail.
packUnionizedData _ _ _ = Nothing
-- Determine the offset for the given field, and update the packing states to include the field. -- Determine the offset for the given field, and update the packing states to include the field.
packField :: FieldDesc -> PackingState -> Map.Map Integer UnionPackingState packField :: FieldDesc -> PackingState -> Map.Map Integer UnionPackingState
-> (Integer, PackingState, Map.Map Integer UnionPackingState) -> (FieldOffset, PackingState, Map.Map Integer UnionPackingState)
packField fieldDesc state unionState = packField fieldDesc state unionState =
case fieldUnion fieldDesc of case fieldUnion fieldDesc of
Nothing -> let Nothing -> let
...@@ -511,13 +646,19 @@ packField fieldDesc state unionState = ...@@ -511,13 +646,19 @@ packField fieldDesc state unionState =
-- Determine the offset for the given union, and update the packing states to include the union. -- Determine the offset for the given union, and update the packing states to include the union.
-- Specifically, this packs the union tag, *not* the fields of the union. -- Specifically, this packs the union tag, *not* the fields of the union.
packUnion :: UnionDesc -> PackingState -> Map.Map Integer UnionPackingState packUnion :: UnionDesc -> PackingState -> Map.Map Integer UnionPackingState
-> (Integer, PackingState, Map.Map Integer UnionPackingState) -> (FieldOffset, PackingState, Map.Map Integer UnionPackingState)
packUnion _ state unionState = (offset, newState, unionState) where packUnion _ state unionState = (DataOffset Size16 offset, newState, unionState) where
(offset, newState) = packValue Size16 state (offset, newState) = packData Size16 state
packFields :: [FieldDesc] -> [UnionDesc] stripHolesFromFirstWord Size1 _ = Size1 -- Nothing left to strip.
-> (PackingState, Map.Map Integer UnionPackingState, Map.Map Integer (Integer, PackingState)) stripHolesFromFirstWord size holes = let
packFields fields unions = (finalState, finalUnionState, Map.fromList packedItems) where nextSize = pred size
in case Map.lookup nextSize holes of
Just 1 -> stripHolesFromFirstWord nextSize holes
_ -> size
packFields :: [FieldDesc] -> [UnionDesc] -> (DataSectionSize, Integer, Map.Map Integer FieldOffset)
packFields fields unions = let
items = concat ( items = concat (
[(fieldNumber d, packField d) | d <- fields]: [(fieldNumber d, packField d) | d <- fields]:
[(unionNumber d, packUnion d):[(fieldNumber d2, packField d2) | d2 <- unionFields d] [(unionNumber d, packUnion d):[(fieldNumber d2, packField d2) | d2 <- unionFields d]
...@@ -526,13 +667,45 @@ packFields fields unions = (finalState, finalUnionState, Map.fromList packedItem ...@@ -526,13 +667,45 @@ packFields fields unions = (finalState, finalUnionState, Map.fromList packedItem
itemsByNumber = List.sortBy compareNumbers items itemsByNumber = List.sortBy compareNumbers items
compareNumbers (a, _) (b, _) = compare a b compareNumbers (a, _) (b, _) = compare a b
(finalState, finalUnionState, packedItems) = (finalState, _, packedItems) =
foldl packItem (initialPackingState, Map.empty, []) itemsByNumber foldl packItem (initialPackingState, Map.empty, []) itemsByNumber
packItem (state, unionState, packed) (n, item) = packItem (state, unionState, packed) (n, item) =
(newState, newUnionState, (n, (offset, newState)):packed) where (newState, newUnionState, (n, offset):packed) where
(offset, newState, newUnionState) = item state unionState (offset, newState, newUnionState) = item state unionState
dataSectionSize =
if packingDataSize finalState == 1
then dataSizeToSectionSize $ stripHolesFromFirstWord Size64 $ packingHoles finalState
else DataSectionWords $ packingDataSize finalState
in (dataSectionSize, packingReferenceCount finalState, Map.fromList packedItems)
enforceFixed Nothing sizes = return sizes
enforceFixed (Just (Located pos (requestedDataSize, requestedPointerCount)))
(actualDataSize, actualPointerCount) = do
validatedRequestedDataSize <- case requestedDataSize of
1 -> return DataSection1
8 -> return DataSection8
16 -> return DataSection16
32 -> return DataSection32
s | mod s 64 == 0 -> return $ DataSectionWords $ div s 64
_ -> makeError pos $ printf "Struct data section size must be a whole number of words \
\or 0, 1, 8, 16, or 32 bits."
recover () $ when (dataSectionBits actualDataSize > dataSectionBits validatedRequestedDataSize) $
makeError pos $ printf "Struct data section size is %s which exceeds specified maximum of \
\%s. WARNING: Increasing the maximum will break backwards-compatibility."
(dataSectionSizeString actualDataSize)
(dataSectionSizeString validatedRequestedDataSize)
recover () $ when (actualPointerCount > requestedPointerCount) $
makeError pos $ printf "Struct pointer section size is %d pointers which exceeds specified \
\maximum of %d pointers. WARNING: Increasing the maximum will break \
\backwards-compatibility."
actualPointerCount requestedPointerCount
return (validatedRequestedDataSize, requestedPointerCount)
------------------------------------------------------------------------------------------ ------------------------------------------------------------------------------------------
data CompiledStatementStatus = CompiledStatementStatus String (Status Desc) data CompiledStatementStatus = CompiledStatementStatus String (Status Desc)
...@@ -604,7 +777,7 @@ compileDecl scope@(DescEnum parent) ...@@ -604,7 +777,7 @@ compileDecl scope@(DescEnum parent)
compileDecl _ (EnumerantDecl (Located pos name) _ _) = compileDecl _ (EnumerantDecl (Located pos name) _ _) =
CompiledStatementStatus name (makeError pos "Enumerants can only appear inside enums.") CompiledStatementStatus name (makeError pos "Enumerants can only appear inside enums.")
compileDecl scope (StructDecl (Located _ name) annotations decls) = compileDecl scope (StructDecl (Located _ name) isFixed annotations decls) =
CompiledStatementStatus name (feedback (\desc -> do CompiledStatementStatus name (feedback (\desc -> do
(members, memberMap) <- compileChildDecls desc decls (members, memberMap) <- compileChildDecls desc decls
requireNoDuplicateNames decls requireNoDuplicateNames decls
...@@ -612,15 +785,19 @@ compileDecl scope (StructDecl (Located _ name) annotations decls) = ...@@ -612,15 +785,19 @@ compileDecl scope (StructDecl (Located _ name) annotations decls) =
requireSequentialNumbering "Fields" fieldNums requireSequentialNumbering "Fields" fieldNums
requireOrdinalsInRange fieldNums requireOrdinalsInRange fieldNums
(theId, compiledAnnotations) <- compileAnnotations scope StructAnnotation annotations (theId, compiledAnnotations) <- compileAnnotations scope StructAnnotation annotations
return (let let (dataSize, pointerCount, fieldPackingMap) = packFields fields unions
fields = [d | DescField d <- members] fields = [d | DescField d <- members]
unions = [d | DescUnion d <- members] unions = [d | DescUnion d <- members]
(packing, _, fieldPackingMap) = packFields fields unions (finalDataSize, finalPointerCount) <-
recover (dataSize, pointerCount) $ enforceFixed isFixed (dataSize, pointerCount)
return (let
in DescStruct StructDesc in DescStruct StructDesc
{ structName = name { structName = name
, structId = theId , structId = theId
, structParent = scope , structParent = scope
, structPacking = packing , structDataSize = finalDataSize
, structPointerCount = finalPointerCount
, structIsFixedWidth = isJust isFixed
, structFields = fields , structFields = fields
, structUnions = unions , structUnions = unions
, structAnnotations = compiledAnnotations , structAnnotations = compiledAnnotations
...@@ -639,14 +816,13 @@ compileDecl scope@(DescStruct parent) ...@@ -639,14 +816,13 @@ compileDecl scope@(DescStruct parent)
requireNoMoreThanOneFieldNumberLessThan name numPos number fields requireNoMoreThanOneFieldNumberLessThan name numPos number fields
(theId, compiledAnnotations) <- compileAnnotations scope UnionAnnotation annotations (theId, compiledAnnotations) <- compileAnnotations scope UnionAnnotation annotations
return (let return (let
(tagOffset, tagPacking) = structFieldPackingMap parent ! number DataOffset Size16 tagOffset = structFieldPackingMap parent ! number
in DescUnion UnionDesc in DescUnion UnionDesc
{ unionName = name { unionName = name
, unionId = theId , unionId = theId
, unionParent = parent , unionParent = parent
, unionNumber = number , unionNumber = number
, unionTagOffset = tagOffset , unionTagOffset = tagOffset
, unionTagPacking = tagPacking
, unionFields = fields , unionFields = fields
, unionAnnotations = compiledAnnotations , unionAnnotations = compiledAnnotations
, unionMemberMap = memberMap , unionMemberMap = memberMap
...@@ -668,18 +844,22 @@ compileDecl scope ...@@ -668,18 +844,22 @@ compileDecl scope
_ -> Nothing _ -> Nothing
typeDesc <- compileType scope typeExp typeDesc <- compileType scope typeExp
defaultDesc <- case defaultValue of defaultDesc <- case defaultValue of
Just (Located defaultPos value) -> fmap Just (compileValue defaultPos typeDesc value) Just (Located defaultPos value) -> do
result <- fmap Just (compileValue defaultPos typeDesc value)
recover () (case typeDesc of
InlineStructType _ ->
makeError defaultPos "Inline fields cannot have default values."
_ -> return ())
return result
Nothing -> return Nothing Nothing -> return Nothing
(theId, compiledAnnotations) <- compileAnnotations scope FieldAnnotation annotations (theId, compiledAnnotations) <- compileAnnotations scope FieldAnnotation annotations
return (let return (let
(offset, packing) = structFieldPackingMap parent ! number
in DescField FieldDesc in DescField FieldDesc
{ fieldName = name { fieldName = name
, fieldId = theId , fieldId = theId
, fieldParent = parent , fieldParent = parent
, fieldNumber = number , fieldNumber = number
, fieldOffset = offset , fieldOffset = structFieldPackingMap parent ! number
, fieldPacking = packing
, fieldUnion = unionDesc , fieldUnion = unionDesc
, fieldType = typeDesc , fieldType = typeDesc
, fieldDefaultValue = defaultDesc , fieldDefaultValue = defaultDesc
......
...@@ -88,6 +88,7 @@ hashString str = ...@@ -88,6 +88,7 @@ hashString str =
isPrimitive t@(BuiltinType _) = not $ isBlob t isPrimitive t@(BuiltinType _) = not $ isBlob t
isPrimitive (EnumType _) = True isPrimitive (EnumType _) = True
isPrimitive (StructType _) = False isPrimitive (StructType _) = False
isPrimitive (InlineStructType _) = False
isPrimitive (InterfaceType _) = False isPrimitive (InterfaceType _) = False
isPrimitive (ListType _) = False isPrimitive (ListType _) = False
...@@ -96,8 +97,12 @@ isBlob (BuiltinType BuiltinData) = True ...@@ -96,8 +97,12 @@ isBlob (BuiltinType BuiltinData) = True
isBlob _ = False isBlob _ = False
isStruct (StructType _) = True isStruct (StructType _) = True
isStruct (InlineStructType _) = True
isStruct _ = False isStruct _ = False
isInlineStruct (InlineStructType _) = True
isInlineStruct _ = False
isList (ListType _) = True isList (ListType _) = True
isList _ = False isList _ = False
...@@ -130,18 +135,30 @@ cxxTypeString (BuiltinType BuiltinText) = " ::capnproto::Text" ...@@ -130,18 +135,30 @@ cxxTypeString (BuiltinType BuiltinText) = " ::capnproto::Text"
cxxTypeString (BuiltinType BuiltinData) = " ::capnproto::Data" cxxTypeString (BuiltinType BuiltinData) = " ::capnproto::Data"
cxxTypeString (EnumType desc) = globalName $ DescEnum desc cxxTypeString (EnumType desc) = globalName $ DescEnum desc
cxxTypeString (StructType desc) = globalName $ DescStruct desc cxxTypeString (StructType desc) = globalName $ DescStruct desc
cxxTypeString (InlineStructType desc) = globalName $ DescStruct desc
cxxTypeString (InterfaceType desc) = globalName $ DescInterface desc cxxTypeString (InterfaceType desc) = globalName $ DescInterface desc
cxxTypeString (ListType t) = concat [" ::capnproto::List<", cxxTypeString t, ">"] cxxTypeString (ListType t) = concat [" ::capnproto::List<", cxxTypeString t, ">"]
cxxFieldSizeString Size0 = "VOID"; cxxFieldSizeString SizeVoid = "VOID";
cxxFieldSizeString Size1 = "BIT"; cxxFieldSizeString (SizeData Size1) = "BIT";
cxxFieldSizeString Size8 = "BYTE"; cxxFieldSizeString (SizeData Size8) = "BYTE";
cxxFieldSizeString Size16 = "TWO_BYTES"; cxxFieldSizeString (SizeData Size16) = "TWO_BYTES";
cxxFieldSizeString Size32 = "FOUR_BYTES"; cxxFieldSizeString (SizeData Size32) = "FOUR_BYTES";
cxxFieldSizeString Size64 = "EIGHT_BYTES"; cxxFieldSizeString (SizeData Size64) = "EIGHT_BYTES";
cxxFieldSizeString SizeReference = "REFERENCE"; cxxFieldSizeString SizeReference = "REFERENCE";
cxxFieldSizeString (SizeInlineComposite _ _) = "INLINE_COMPOSITE"; cxxFieldSizeString (SizeInlineComposite _ _) = "INLINE_COMPOSITE";
fieldOffsetInteger VoidOffset = "0"
fieldOffsetInteger (DataOffset _ o) = show o
fieldOffsetInteger (PointerOffset o) = show o
fieldOffsetInteger (InlineCompositeOffset d p ds ps) = let
bitSize = dataSectionBits ds
bitOffset = case ds of
DataSectionWords _ -> d * 64
_ -> d * bitSize
in printf "%d * ::capnproto::BITS, %d * ::capnproto::BITS, \
\%d * ::capnproto::REFERENCES, %d * ::capnproto::REFERENCES" bitOffset bitSize p ps
isDefaultZero VoidDesc = True isDefaultZero VoidDesc = True
isDefaultZero (BoolDesc b) = not b isDefaultZero (BoolDesc b) = not b
isDefaultZero (Int8Desc i) = i == 0 isDefaultZero (Int8Desc i) = i == 0
...@@ -221,6 +238,7 @@ fieldContext parent desc = mkStrContext context where ...@@ -221,6 +238,7 @@ fieldContext parent desc = mkStrContext context where
context "fieldIsPrimitive" = MuBool $ isPrimitive $ fieldType desc context "fieldIsPrimitive" = MuBool $ isPrimitive $ fieldType desc
context "fieldIsBlob" = MuBool $ isBlob $ fieldType desc context "fieldIsBlob" = MuBool $ isBlob $ fieldType desc
context "fieldIsStruct" = MuBool $ isStruct $ fieldType desc context "fieldIsStruct" = MuBool $ isStruct $ fieldType desc
context "fieldIsInlineStruct" = MuBool $ isInlineStruct $ fieldType desc
context "fieldIsList" = MuBool $ isList $ fieldType desc context "fieldIsList" = MuBool $ isList $ fieldType desc
context "fieldIsNonStructList" = MuBool $ isNonStructList $ fieldType desc context "fieldIsNonStructList" = MuBool $ isNonStructList $ fieldType desc
context "fieldIsPrimitiveList" = MuBool $ isPrimitiveList $ fieldType desc context "fieldIsPrimitiveList" = MuBool $ isPrimitiveList $ fieldType desc
...@@ -231,7 +249,7 @@ fieldContext parent desc = mkStrContext context where ...@@ -231,7 +249,7 @@ fieldContext parent desc = mkStrContext context where
Nothing -> muNull Nothing -> muNull
context "fieldType" = MuVariable $ cxxTypeString $ fieldType desc context "fieldType" = MuVariable $ cxxTypeString $ fieldType desc
context "fieldBlobType" = MuVariable $ blobTypeString $ fieldType desc context "fieldBlobType" = MuVariable $ blobTypeString $ fieldType desc
context "fieldOffset" = MuVariable $ fieldOffset desc context "fieldOffset" = MuVariable $ fieldOffsetInteger $ fieldOffset desc
context "fieldDefaultMask" = case fieldDefaultValue desc of context "fieldDefaultMask" = case fieldDefaultValue desc of
Nothing -> MuVariable "" Nothing -> MuVariable ""
Just v -> MuVariable (if isDefaultZero v then "" else ", " ++ defaultMask v) Just v -> MuVariable (if isDefaultZero v then "" else ", " ++ defaultMask v)
...@@ -283,8 +301,8 @@ structContext parent desc = mkStrContext context where ...@@ -283,8 +301,8 @@ structContext parent desc = mkStrContext context where
context "structFullName" = MuVariable $ fullName (DescStruct desc) context "structFullName" = MuVariable $ fullName (DescStruct desc)
context "structFields" = MuList $ map (fieldContext context) $ structFields desc context "structFields" = MuList $ map (fieldContext context) $ structFields desc
context "structUnions" = MuList $ map (unionContext context) $ structUnions desc context "structUnions" = MuList $ map (unionContext context) $ structUnions desc
context "structDataSize" = MuVariable $ packingDataSize $ structPacking desc context "structDataSize" = MuVariable $ dataSectionWordSize $ structDataSize desc
context "structReferenceCount" = MuVariable $ packingReferenceCount $ structPacking desc context "structReferenceCount" = MuVariable $ structPointerCount desc
context "structNestedEnums" = context "structNestedEnums" =
MuList $ map (enumContext context) [m | DescEnum m <- structMembers desc] MuList $ map (enumContext context) [m | DescEnum m <- structMembers desc]
context "structNestedStructs" = context "structNestedStructs" =
......
...@@ -95,7 +95,8 @@ data Declaration = UsingDecl (Located String) DeclName ...@@ -95,7 +95,8 @@ data Declaration = UsingDecl (Located String) DeclName
| ConstantDecl (Located String) TypeExpression [Annotation] (Located FieldValue) | ConstantDecl (Located String) TypeExpression [Annotation] (Located FieldValue)
| EnumDecl (Located String) [Annotation] [Declaration] | EnumDecl (Located String) [Annotation] [Declaration]
| EnumerantDecl (Located String) (Located Integer) [Annotation] | EnumerantDecl (Located String) (Located Integer) [Annotation]
| StructDecl (Located String) [Annotation] [Declaration] | StructDecl (Located String) (Maybe (Located (Integer, Integer)))
[Annotation] [Declaration]
| FieldDecl (Located String) (Located Integer) | FieldDecl (Located String) (Located Integer)
TypeExpression [Annotation] (Maybe (Located FieldValue)) TypeExpression [Annotation] (Maybe (Located FieldValue))
| UnionDecl (Located String) (Located Integer) [Annotation] [Declaration] | UnionDecl (Located String) (Located Integer) [Annotation] [Declaration]
...@@ -110,7 +111,7 @@ declarationName (UsingDecl n _) = Just n ...@@ -110,7 +111,7 @@ declarationName (UsingDecl n _) = Just n
declarationName (ConstantDecl n _ _ _) = Just n declarationName (ConstantDecl n _ _ _) = Just n
declarationName (EnumDecl n _ _) = Just n declarationName (EnumDecl n _ _) = Just n
declarationName (EnumerantDecl n _ _) = Just n declarationName (EnumerantDecl n _ _) = Just n
declarationName (StructDecl n _ _) = Just n declarationName (StructDecl n _ _ _) = Just n
declarationName (FieldDecl n _ _ _ _) = Just n declarationName (FieldDecl n _ _ _ _) = Just n
declarationName (UnionDecl n _ _ _) = Just n declarationName (UnionDecl n _ _ _) = Just n
declarationName (InterfaceDecl n _ _) = Just n declarationName (InterfaceDecl n _ _) = Just n
...@@ -122,7 +123,7 @@ declImports (UsingDecl _ name) = maybeToList (declNameImport name) ...@@ -122,7 +123,7 @@ declImports (UsingDecl _ name) = maybeToList (declNameImport name)
declImports (ConstantDecl _ t ann _) = typeImports t ++ concatMap annotationImports ann declImports (ConstantDecl _ t ann _) = typeImports t ++ concatMap annotationImports ann
declImports (EnumDecl _ ann decls) = concatMap annotationImports ann ++ concatMap declImports decls declImports (EnumDecl _ ann decls) = concatMap annotationImports ann ++ concatMap declImports decls
declImports (EnumerantDecl _ _ ann) = concatMap annotationImports ann declImports (EnumerantDecl _ _ ann) = concatMap annotationImports ann
declImports (StructDecl _ ann decls) = concatMap annotationImports ann ++ declImports (StructDecl _ _ ann decls) = concatMap annotationImports ann ++
concatMap declImports decls concatMap declImports decls
declImports (FieldDecl _ _ t ann _) = typeImports t ++ concatMap annotationImports ann declImports (FieldDecl _ _ t ann _) = typeImports t ++ concatMap annotationImports ann
declImports (UnionDecl _ _ ann decls) = concatMap annotationImports ann ++ declImports (UnionDecl _ _ ann decls) = concatMap annotationImports ann ++
......
...@@ -49,6 +49,7 @@ keywords = ...@@ -49,6 +49,7 @@ keywords =
, (UnionKeyword, "union") , (UnionKeyword, "union")
, (InterfaceKeyword, "interface") , (InterfaceKeyword, "interface")
, (AnnotationKeyword, "annotation") , (AnnotationKeyword, "annotation")
, (FixedKeyword, "fixed")
] ]
languageDef :: T.LanguageDef st languageDef :: T.LanguageDef st
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
module Parser (parseFile) where module Parser (parseFile) where
import Data.Generics import Data.Generics
import Data.Maybe(fromMaybe)
import Text.Parsec hiding (tokens) import Text.Parsec hiding (tokens)
import Token import Token
import Grammar import Grammar
...@@ -65,6 +66,7 @@ tokenErrorString StructKeyword = "keyword \"struct\"" ...@@ -65,6 +66,7 @@ tokenErrorString StructKeyword = "keyword \"struct\""
tokenErrorString UnionKeyword = "keyword \"union\"" tokenErrorString UnionKeyword = "keyword \"union\""
tokenErrorString InterfaceKeyword = "keyword \"interface\"" tokenErrorString InterfaceKeyword = "keyword \"interface\""
tokenErrorString AnnotationKeyword = "keyword \"annotation\"" tokenErrorString AnnotationKeyword = "keyword \"annotation\""
tokenErrorString FixedKeyword = "keyword \"fixed\""
type TokenParser = Parsec [Located Token] [ParseError] type TokenParser = Parsec [Located Token] [ParseError]
...@@ -120,6 +122,7 @@ structKeyword = tokenParser (matchSimpleToken StructKeyword) <?> "\"struct\"" ...@@ -120,6 +122,7 @@ structKeyword = tokenParser (matchSimpleToken StructKeyword) <?> "\"struct\""
unionKeyword = tokenParser (matchSimpleToken UnionKeyword) <?> "\"union\"" unionKeyword = tokenParser (matchSimpleToken UnionKeyword) <?> "\"union\""
interfaceKeyword = tokenParser (matchSimpleToken InterfaceKeyword) <?> "\"interface\"" interfaceKeyword = tokenParser (matchSimpleToken InterfaceKeyword) <?> "\"interface\""
annotationKeyword = tokenParser (matchSimpleToken AnnotationKeyword) <?> "\"annotation\"" annotationKeyword = tokenParser (matchSimpleToken AnnotationKeyword) <?> "\"annotation\""
fixedKeyword = tokenParser (matchSimpleToken FixedKeyword) <?> "\"fixed\""
exactIdentifier s = tokenParser (matchSimpleToken $ Identifier s) <?> "\"" ++ s ++ "\"" exactIdentifier s = tokenParser (matchSimpleToken $ Identifier s) <?> "\"" ++ s ++ "\""
...@@ -223,9 +226,39 @@ enumerantDecl = do ...@@ -223,9 +226,39 @@ enumerantDecl = do
structDecl statements = do structDecl statements = do
structKeyword structKeyword
name <- located typeIdentifier name <- located typeIdentifier
fixed <- optionMaybe fixedSpec
annotations <- many annotation annotations <- many annotation
children <- parseBlock structLine statements children <- parseBlock structLine statements
return (StructDecl name annotations children) return (StructDecl name fixed annotations children)
fixedSpec = do
fixedKeyword
Located pos sizes <- located $ parenthesizedList fixedSize
(dataSize, pointerSize) <- foldM combineFixedSizes (Nothing, Nothing) sizes
return $ Located pos (fromMaybe 0 dataSize, fromMaybe 0 pointerSize)
data FixedSize = FixedData Integer | FixedPointers Integer
combineFixedSizes :: (Maybe Integer, Maybe Integer) -> FixedSize
-> TokenParser (Maybe Integer, Maybe Integer)
combineFixedSizes (Nothing, p) (FixedData d) = return (Just d, p)
combineFixedSizes (Just _, _) (FixedData _) =
fail "Multiple data section size specifications."
combineFixedSizes (d, Nothing) (FixedPointers p) = return (d, Just p)
combineFixedSizes (_, Just _) (FixedPointers _) =
fail "Multiple pointer section size specifications."
fixedSize = do
size <- literalInt
(exactIdentifier "bit" >> return (FixedData size))
<|> (exactIdentifier "bits" >> return (FixedData size))
<|> (exactIdentifier "byte" >> return (FixedData (8 * size)))
<|> (exactIdentifier "bytes" >> return (FixedData (8 * size)))
<|> (exactIdentifier "word" >> return (FixedData (64 * size)))
<|> (exactIdentifier "words" >> return (FixedData (64 * size)))
<|> (exactIdentifier "pointer" >> return (FixedPointers size))
<|> (exactIdentifier "pointers" >> return (FixedPointers size))
<?> "\"bits\", \"bytes\", \"words\", or \"pointers\""
structLine :: Maybe [Located Statement] -> TokenParser Declaration structLine :: Maybe [Located Statement] -> TokenParser Declaration
structLine Nothing = usingDecl <|> constantDecl <|> fieldDecl <|> annotationDecl structLine Nothing = usingDecl <|> constantDecl <|> fieldDecl <|> annotationDecl
......
...@@ -55,6 +55,7 @@ data Desc = DescFile FileDesc ...@@ -55,6 +55,7 @@ data Desc = DescFile FileDesc
| DescAnnotation AnnotationDesc | DescAnnotation AnnotationDesc
| DescBuiltinType BuiltinType | DescBuiltinType BuiltinType
| DescBuiltinList | DescBuiltinList
| DescBuiltinInline
| DescBuiltinId | DescBuiltinId
descName (DescFile _) = "(top-level)" descName (DescFile _) = "(top-level)"
...@@ -71,6 +72,7 @@ descName (DescParam d) = paramName d ...@@ -71,6 +72,7 @@ descName (DescParam d) = paramName d
descName (DescAnnotation d) = annotationName d descName (DescAnnotation d) = annotationName d
descName (DescBuiltinType d) = builtinTypeName d descName (DescBuiltinType d) = builtinTypeName d
descName DescBuiltinList = "List" descName DescBuiltinList = "List"
descName DescBuiltinInline = "Inline"
descName DescBuiltinId = "id" descName DescBuiltinId = "id"
descId (DescFile d) = fileId d descId (DescFile d) = fileId d
...@@ -87,6 +89,7 @@ descId (DescParam d) = paramId d ...@@ -87,6 +89,7 @@ descId (DescParam d) = paramId d
descId (DescAnnotation d) = annotationId d descId (DescAnnotation d) = annotationId d
descId (DescBuiltinType _) = Nothing descId (DescBuiltinType _) = Nothing
descId DescBuiltinList = Nothing descId DescBuiltinList = Nothing
descId DescBuiltinInline = Nothing
descId DescBuiltinId = Just "0U0T3e_SnatEfk6UcH2tcjTt1E0" descId DescBuiltinId = Just "0U0T3e_SnatEfk6UcH2tcjTt1E0"
-- Gets the ID if explicitly defined, or generates it by appending ".name" to the parent's ID. -- Gets the ID if explicitly defined, or generates it by appending ".name" to the parent's ID.
...@@ -111,6 +114,7 @@ descParent (DescParam d) = DescMethod (paramParent d) ...@@ -111,6 +114,7 @@ descParent (DescParam d) = DescMethod (paramParent d)
descParent (DescAnnotation d) = annotationParent d descParent (DescAnnotation d) = annotationParent d
descParent (DescBuiltinType _) = error "Builtin type has no parent." descParent (DescBuiltinType _) = error "Builtin type has no parent."
descParent DescBuiltinList = error "Builtin type has no parent." descParent DescBuiltinList = error "Builtin type has no parent."
descParent DescBuiltinInline = error "Builtin type has no parent."
descParent DescBuiltinId = error "Builtin annotation has no parent." descParent DescBuiltinId = error "Builtin annotation has no parent."
descFile (DescFile d) = d descFile (DescFile d) = d
...@@ -130,9 +134,10 @@ descAnnotations (DescParam d) = paramAnnotations d ...@@ -130,9 +134,10 @@ descAnnotations (DescParam d) = paramAnnotations d
descAnnotations (DescAnnotation d) = annotationAnnotations d descAnnotations (DescAnnotation d) = annotationAnnotations d
descAnnotations (DescBuiltinType _) = Map.empty descAnnotations (DescBuiltinType _) = Map.empty
descAnnotations DescBuiltinList = Map.empty descAnnotations DescBuiltinList = Map.empty
descAnnotations DescBuiltinInline = Map.empty
descAnnotations DescBuiltinId = Map.empty descAnnotations DescBuiltinId = Map.empty
descRuntimeImports (DescFile d) = error "Not to be called on files." descRuntimeImports (DescFile _) = error "Not to be called on files."
descRuntimeImports (DescUsing d) = usingRuntimeImports d descRuntimeImports (DescUsing d) = usingRuntimeImports d
descRuntimeImports (DescConstant d) = constantRuntimeImports d descRuntimeImports (DescConstant d) = constantRuntimeImports d
descRuntimeImports (DescEnum d) = enumRuntimeImports d descRuntimeImports (DescEnum d) = enumRuntimeImports d
...@@ -146,6 +151,7 @@ descRuntimeImports (DescParam d) = paramRuntimeImports d ...@@ -146,6 +151,7 @@ descRuntimeImports (DescParam d) = paramRuntimeImports d
descRuntimeImports (DescAnnotation d) = annotationRuntimeImports d descRuntimeImports (DescAnnotation d) = annotationRuntimeImports d
descRuntimeImports (DescBuiltinType _) = [] descRuntimeImports (DescBuiltinType _) = []
descRuntimeImports DescBuiltinList = [] descRuntimeImports DescBuiltinList = []
descRuntimeImports DescBuiltinInline = []
descRuntimeImports DescBuiltinId = [] descRuntimeImports DescBuiltinId = []
type MemberMap = Map.Map String (Maybe Desc) type MemberMap = Map.Map String (Maybe Desc)
...@@ -212,101 +218,118 @@ valueString (ListDesc l) = "[" ++ delimit ", " (map valueString l) ++ "]" where ...@@ -212,101 +218,118 @@ valueString (ListDesc l) = "[" ++ delimit ", " (map valueString l) ++ "]" where
data TypeDesc = BuiltinType BuiltinType data TypeDesc = BuiltinType BuiltinType
| EnumType EnumDesc | EnumType EnumDesc
| StructType StructDesc | StructType StructDesc
| InlineStructType StructDesc
| InterfaceType InterfaceDesc | InterfaceType InterfaceDesc
| ListType TypeDesc | ListType TypeDesc
typeRuntimeImports (BuiltinType _) = [] typeRuntimeImports (BuiltinType _) = []
typeRuntimeImports (EnumType d) = [descFile (DescEnum d)] typeRuntimeImports (EnumType d) = [descFile (DescEnum d)]
typeRuntimeImports (StructType d) = [descFile (DescStruct d)] typeRuntimeImports (StructType d) = [descFile (DescStruct d)]
typeRuntimeImports (InlineStructType d) = [descFile (DescStruct d)]
typeRuntimeImports (InterfaceType d) = [descFile (DescInterface d)] typeRuntimeImports (InterfaceType d) = [descFile (DescInterface d)]
typeRuntimeImports (ListType d) = typeRuntimeImports d typeRuntimeImports (ListType d) = typeRuntimeImports d
data PackingState = PackingState data DataSectionSize = DataSection1 | DataSection8 | DataSection16 | DataSection32
{ packingHole1 :: Integer | DataSectionWords Integer
, packingHole8 :: Integer
, packingHole16 :: Integer dataSectionWordSize ds = case ds of
, packingHole32 :: Integer DataSectionWords w -> w
, packingDataSize :: Integer _ -> 1
, packingReferenceCount :: Integer
dataSectionAlignment DataSection1 = Size1
dataSectionAlignment DataSection8 = Size8
dataSectionAlignment DataSection16 = Size16
dataSectionAlignment DataSection32 = Size32
dataSectionAlignment (DataSectionWords _) = Size64
dataSectionBits DataSection1 = 1
dataSectionBits DataSection8 = 8
dataSectionBits DataSection16 = 16
dataSectionBits DataSection32 = 32
dataSectionBits (DataSectionWords w) = w * 64
dataSizeToSectionSize Size1 = DataSection1
dataSizeToSectionSize Size8 = DataSection8
dataSizeToSectionSize Size16 = DataSection16
dataSizeToSectionSize Size32 = DataSection32
dataSizeToSectionSize Size64 = DataSectionWords 1
dataSectionSizeString DataSection1 = "1 bits"
dataSectionSizeString DataSection8 = "8 bits"
dataSectionSizeString DataSection16 = "16 bits"
dataSectionSizeString DataSection32 = "32 bits"
dataSectionSizeString (DataSectionWords n) = show n ++ " words"
data DataSize = Size1 | Size8 | Size16 | Size32 | Size64 deriving(Eq, Ord, Enum)
dataSizeInBits :: DataSize -> Integer
dataSizeInBits Size1 = 1
dataSizeInBits Size8 = 8
dataSizeInBits Size16 = 16
dataSizeInBits Size32 = 32
dataSizeInBits Size64 = 64
data FieldSize = SizeVoid
| SizeData DataSize
| SizeReference
| SizeInlineComposite DataSectionSize Integer
data FieldOffset = VoidOffset
| DataOffset DataSize Integer
| PointerOffset Integer
| InlineCompositeOffset
{ inlineCompositeDataOffset :: Integer
, inlineCompositePointerOffset :: Integer
, inlineCompositeDataSize :: DataSectionSize
, inlineCompositePointerSize :: Integer
} }
packingSize PackingState { packingDataSize = ds, packingReferenceCount = rc } = ds + rc offsetToSize :: FieldOffset -> FieldSize
offsetToSize VoidOffset = SizeVoid
-- Represents the current packing state of a union. The parameters are: offsetToSize (DataOffset s _) = SizeData s
-- - The offset of a 64-bit word in the data segment allocated to the union. offsetToSize (PointerOffset _) = SizeReference
-- - The offset of a reference allocated to the union. offsetToSize (InlineCompositeOffset _ _ d p) = SizeInlineComposite d p
-- - The offset of a smaller piece of the data segment allocated to the union. Such a smaller
-- piece exists if one field in the union has lower number than the union itself -- in this case, fieldSize (BuiltinType BuiltinVoid) = SizeVoid
-- this is the piece that had been allocated to that field, and is now retroactively part of the fieldSize (BuiltinType BuiltinBool) = SizeData Size1
-- union. fieldSize (BuiltinType BuiltinInt8) = SizeData Size8
data UnionPackingState = UnionPackingState fieldSize (BuiltinType BuiltinInt16) = SizeData Size16
{ unionPackDataOffset :: Maybe (Integer, FieldSize) fieldSize (BuiltinType BuiltinInt32) = SizeData Size32
, unionPackReferenceOffset :: Maybe Integer fieldSize (BuiltinType BuiltinInt64) = SizeData Size64
} fieldSize (BuiltinType BuiltinUInt8) = SizeData Size8
fieldSize (BuiltinType BuiltinUInt16) = SizeData Size16
data FieldSize = Size0 | Size1 | Size8 | Size16 | Size32 | Size64 | SizeReference fieldSize (BuiltinType BuiltinUInt32) = SizeData Size32
| SizeInlineComposite Integer Integer fieldSize (BuiltinType BuiltinUInt64) = SizeData Size64
fieldSize (BuiltinType BuiltinFloat32) = SizeData Size32
isDataFieldSize SizeReference = False fieldSize (BuiltinType BuiltinFloat64) = SizeData Size64
isDataFieldSize (SizeInlineComposite _ _) = False
isDataFieldSize _ = True
fieldSize (BuiltinType BuiltinVoid) = Size0
fieldSize (BuiltinType BuiltinBool) = Size1
fieldSize (BuiltinType BuiltinInt8) = Size8
fieldSize (BuiltinType BuiltinInt16) = Size16
fieldSize (BuiltinType BuiltinInt32) = Size32
fieldSize (BuiltinType BuiltinInt64) = Size64
fieldSize (BuiltinType BuiltinUInt8) = Size8
fieldSize (BuiltinType BuiltinUInt16) = Size16
fieldSize (BuiltinType BuiltinUInt32) = Size32
fieldSize (BuiltinType BuiltinUInt64) = Size64
fieldSize (BuiltinType BuiltinFloat32) = Size32
fieldSize (BuiltinType BuiltinFloat64) = Size64
fieldSize (BuiltinType BuiltinText) = SizeReference fieldSize (BuiltinType BuiltinText) = SizeReference
fieldSize (BuiltinType BuiltinData) = SizeReference fieldSize (BuiltinType BuiltinData) = SizeReference
fieldSize (EnumType _) = Size16 -- TODO: ?? fieldSize (EnumType _) = SizeData Size16
fieldSize (StructType _) = SizeReference fieldSize (StructType _) = SizeReference
fieldSize (InlineStructType StructDesc { structDataSize = ds, structPointerCount = ps }) =
SizeInlineComposite ds ps
fieldSize (InterfaceType _) = SizeReference fieldSize (InterfaceType _) = SizeReference
fieldSize (ListType _) = SizeReference fieldSize (ListType _) = SizeReference
fieldValueSize VoidDesc = Size0 elementSize (StructType StructDesc { structDataSize = DataSection1, structPointerCount = 0 }) =
fieldValueSize (BoolDesc _) = Size1 SizeData Size1
fieldValueSize (Int8Desc _) = Size8 elementSize (StructType StructDesc { structDataSize = DataSection8, structPointerCount = 0 }) =
fieldValueSize (Int16Desc _) = Size16 SizeData Size8
fieldValueSize (Int32Desc _) = Size32 elementSize (StructType StructDesc { structDataSize = DataSection16, structPointerCount = 0 }) =
fieldValueSize (Int64Desc _) = Size64 SizeData Size16
fieldValueSize (UInt8Desc _) = Size8 elementSize (StructType StructDesc { structDataSize = DataSection32, structPointerCount = 0 }) =
fieldValueSize (UInt16Desc _) = Size16 SizeData Size32
fieldValueSize (UInt32Desc _) = Size32 elementSize (StructType StructDesc { structDataSize = ds, structPointerCount = pc }) =
fieldValueSize (UInt64Desc _) = Size64 SizeInlineComposite ds pc
fieldValueSize (Float32Desc _) = Size32 elementSize (InlineStructType s) = elementSize (StructType s)
fieldValueSize (Float64Desc _) = Size64
fieldValueSize (TextDesc _) = SizeReference
fieldValueSize (DataDesc _) = SizeReference
fieldValueSize (EnumerantValueDesc _) = Size16
fieldValueSize (StructValueDesc _) = SizeReference
fieldValueSize (ListDesc _) = SizeReference
elementSize (StructType StructDesc { structPacking =
PackingState { packingDataSize = ds, packingReferenceCount = rc } }) =
SizeInlineComposite ds rc
elementSize t = fieldSize t elementSize t = fieldSize t
sizeInBits Size0 = 0
sizeInBits Size1 = 1
sizeInBits Size8 = 8
sizeInBits Size16 = 16
sizeInBits Size32 = 32
sizeInBits Size64 = 64
sizeInBits SizeReference = 64
sizeInBits (SizeInlineComposite d r) = (d + r) * 64
-- Render the type descriptor's name as a string, appropriate for use in the given scope. -- Render the type descriptor's name as a string, appropriate for use in the given scope.
typeName :: Desc -> TypeDesc -> String typeName :: Desc -> TypeDesc -> String
typeName _ (BuiltinType t) = builtinTypeName t -- TODO: Check for shadowing. typeName _ (BuiltinType t) = builtinTypeName t -- TODO: Check for shadowing.
typeName scope (EnumType desc) = descQualifiedName scope (DescEnum desc) typeName scope (EnumType desc) = descQualifiedName scope (DescEnum desc)
typeName scope (StructType desc) = descQualifiedName scope (DescStruct desc) typeName scope (StructType desc) = descQualifiedName scope (DescStruct desc)
typeName scope (InlineStructType desc) = descQualifiedName scope (DescStruct desc)
typeName scope (InterfaceType desc) = descQualifiedName scope (DescInterface desc) typeName scope (InterfaceType desc) = descQualifiedName scope (DescInterface desc)
typeName scope (ListType t) = "List(" ++ typeName scope t ++ ")" typeName scope (ListType t) = "List(" ++ typeName scope t ++ ")"
...@@ -386,7 +409,9 @@ data StructDesc = StructDesc ...@@ -386,7 +409,9 @@ data StructDesc = StructDesc
{ structName :: String { structName :: String
, structId :: Maybe String , structId :: Maybe String
, structParent :: Desc , structParent :: Desc
, structPacking :: PackingState , structDataSize :: DataSectionSize
, structPointerCount :: Integer
, structIsFixedWidth :: Bool
, structFields :: [FieldDesc] , structFields :: [FieldDesc]
, structUnions :: [UnionDesc] , structUnions :: [UnionDesc]
, structAnnotations :: AnnotationMap , structAnnotations :: AnnotationMap
...@@ -396,7 +421,7 @@ data StructDesc = StructDesc ...@@ -396,7 +421,7 @@ data StructDesc = StructDesc
-- Don't use this directly, use the members of FieldDesc and UnionDesc. -- Don't use this directly, use the members of FieldDesc and UnionDesc.
-- This field is exposed here only because I was too lazy to create a way to pass it on -- This field is exposed here only because I was too lazy to create a way to pass it on
-- the side when compiling members of a struct. -- the side when compiling members of a struct.
, structFieldPackingMap :: Map.Map Integer (Integer, PackingState) , structFieldPackingMap :: Map.Map Integer FieldOffset
} }
structRuntimeImports desc = concatMap descRuntimeImports $ structMembers desc structRuntimeImports desc = concatMap descRuntimeImports $ structMembers desc
...@@ -407,7 +432,6 @@ data UnionDesc = UnionDesc ...@@ -407,7 +432,6 @@ data UnionDesc = UnionDesc
, unionParent :: StructDesc , unionParent :: StructDesc
, unionNumber :: Integer , unionNumber :: Integer
, unionTagOffset :: Integer , unionTagOffset :: Integer
, unionTagPacking :: PackingState
, unionFields :: [FieldDesc] , unionFields :: [FieldDesc]
, unionAnnotations :: AnnotationMap , unionAnnotations :: AnnotationMap
, unionMemberMap :: MemberMap , unionMemberMap :: MemberMap
...@@ -424,8 +448,7 @@ data FieldDesc = FieldDesc ...@@ -424,8 +448,7 @@ data FieldDesc = FieldDesc
, fieldId :: Maybe String , fieldId :: Maybe String
, fieldParent :: StructDesc , fieldParent :: StructDesc
, fieldNumber :: Integer , fieldNumber :: Integer
, fieldOffset :: Integer , fieldOffset :: FieldOffset
, fieldPacking :: PackingState -- PackingState for the struct *if* this were the final field.
, fieldUnion :: Maybe (UnionDesc, Integer) -- Integer is value of union discriminant. , fieldUnion :: Maybe (UnionDesc, Integer) -- Integer is value of union discriminant.
, fieldType :: TypeDesc , fieldType :: TypeDesc
, fieldDefaultValue :: Maybe ValueDesc , fieldDefaultValue :: Maybe ValueDesc
...@@ -508,8 +531,13 @@ descToCode indent self@(DescEnum desc) = printf "%senum %s%s {\n%s%s}\n" indent ...@@ -508,8 +531,13 @@ descToCode indent self@(DescEnum desc) = printf "%senum %s%s {\n%s%s}\n" indent
descToCode indent self@(DescEnumerant desc) = printf "%s%s @%d%s;\n" indent descToCode indent self@(DescEnumerant desc) = printf "%s%s @%d%s;\n" indent
(enumerantName desc) (enumerantNumber desc) (enumerantName desc) (enumerantNumber desc)
(annotationsCode self) (annotationsCode self)
descToCode indent self@(DescStruct desc) = printf "%sstruct %s%s {\n%s%s}\n" indent descToCode indent self@(DescStruct desc) = printf "%sstruct %s%s%s {\n%s%s}\n" indent
(structName desc) (structName desc)
(if structIsFixedWidth desc
then printf " fixed(%s, %d pointers) "
(dataSectionSizeString $ structDataSize desc)
(structPointerCount desc)
else "")
(annotationsCode self) (annotationsCode self)
(blockCode indent (structMembers desc)) (blockCode indent (structMembers desc))
indent indent
...@@ -519,12 +547,16 @@ descToCode indent self@(DescField desc) = printf "%s%s@%d%s: %s%s%s; # %s\n" in ...@@ -519,12 +547,16 @@ descToCode indent self@(DescField desc) = printf "%s%s@%d%s: %s%s%s; # %s\n" in
(typeName (descParent self) (fieldType desc)) (typeName (descParent self) (fieldType desc))
(case fieldDefaultValue desc of { Nothing -> ""; Just v -> " = " ++ valueString v; }) (case fieldDefaultValue desc of { Nothing -> ""; Just v -> " = " ++ valueString v; })
(annotationsCode self) (annotationsCode self)
(case fieldSize $ fieldType desc of (case fieldOffset desc of
SizeReference -> printf "ref[%d]" $ fieldOffset desc PointerOffset o -> printf "ptr[%d]" o
SizeInlineComposite _ _ -> "??" InlineCompositeOffset dataOffset pointerOffset dataSize pointerSize ->
s -> let let dataBitOffset = dataOffset * dataSizeInBits (dataSectionAlignment dataSize)
bits = sizeInBits s in printf "bits[%d, %d), ptrs[%d, %d)"
offset = fieldOffset desc dataBitOffset (dataBitOffset + dataSectionBits dataSize)
pointerOffset (pointerOffset + pointerSize)
VoidOffset -> "(none)"
DataOffset dataSize offset -> let
bits = dataSizeInBits dataSize
in printf "bits[%d, %d)" (offset * bits) ((offset + 1) * bits)) in printf "bits[%d, %d)" (offset * bits) ((offset + 1) * bits))
descToCode indent self@(DescUnion desc) = printf "%sunion %s@%d%s { # [%d, %d)\n%s%s}\n" indent descToCode indent self@(DescUnion desc) = printf "%sunion %s@%d%s { # [%d, %d)\n%s%s}\n" indent
(unionName desc) (unionNumber desc) (unionName desc) (unionNumber desc)
...@@ -556,6 +588,7 @@ descToCode indent self@(DescAnnotation desc) = printf "%sannotation %s: %s on(%s ...@@ -556,6 +588,7 @@ descToCode indent self@(DescAnnotation desc) = printf "%sannotation %s: %s on(%s
(annotationsCode self) (annotationsCode self)
descToCode _ (DescBuiltinType _) = error "Can't print code for builtin type." descToCode _ (DescBuiltinType _) = error "Can't print code for builtin type."
descToCode _ DescBuiltinList = error "Can't print code for builtin type." descToCode _ DescBuiltinList = error "Can't print code for builtin type."
descToCode _ DescBuiltinInline = error "Can't print code for builtin type."
descToCode _ DescBuiltinId = error "Can't print code for builtin annotation." descToCode _ DescBuiltinId = error "Can't print code for builtin annotation."
maybeBlockCode :: String -> [Desc] -> String maybeBlockCode :: String -> [Desc] -> String
......
...@@ -74,6 +74,7 @@ data Token = Identifier String ...@@ -74,6 +74,7 @@ data Token = Identifier String
| UnionKeyword | UnionKeyword
| InterfaceKeyword | InterfaceKeyword
| AnnotationKeyword | AnnotationKeyword
| FixedKeyword
deriving (Data, Typeable, Show, Eq) deriving (Data, Typeable, Show, Eq)
data Statement = Line TokenSequence data Statement = Line TokenSequence
......
...@@ -26,11 +26,11 @@ module WireFormat(encodeMessage) where ...@@ -26,11 +26,11 @@ module WireFormat(encodeMessage) where
import Data.List(sortBy, genericLength, genericReplicate) import Data.List(sortBy, genericLength, genericReplicate)
import Data.Word import Data.Word
import Data.Bits(shiftL, shiftR, Bits, setBit, xor) import Data.Bits(shiftL, shiftR, Bits, setBit, xor)
import Data.Function(on)
import Semantics import Semantics
import Data.Binary.IEEE754(floatToWord, doubleToWord) import Data.Binary.IEEE754(floatToWord, doubleToWord)
import qualified Codec.Binary.UTF8.String as UTF8 import qualified Codec.Binary.UTF8.String as UTF8
--
byte :: (Integral a, Bits a) => a -> Int -> Word8 byte :: (Integral a, Bits a) => a -> Int -> Word8
byte i amount = fromIntegral (shiftR i (amount * 8)) byte i amount = fromIntegral (shiftR i (amount * 8))
...@@ -43,119 +43,112 @@ padToWord b = let ...@@ -43,119 +43,112 @@ padToWord b = let
then b then b
else b ++ replicate (8 - trailing) 0 else b ++ replicate (8 - trailing) 0
roundUpToMultiple factor n = let data EncodedData = EncodedBit Bool
remainder = mod n factor | EncodedBytes [Word8]
in if remainder == 0
then n xorData (EncodedBit a) (EncodedBit b) = EncodedBit (a /= b)
else n + (factor - remainder) xorData (EncodedBytes a) (EncodedBytes b) = EncodedBytes (zipWith xor a b)
xorData _ _ = error "Value type mismatch when xor'ing."
encodeDataValue :: ValueDesc -> [Word8]
encodeDataValue VoidDesc = [] encodeDataValue :: TypeDesc -> ValueDesc -> EncodedData
encodeDataValue (BoolDesc _) = error "Bools must be handled specially." encodeDataValue _ VoidDesc = EncodedBytes []
encodeDataValue (Int8Desc v) = bytes v 1 encodeDataValue _ (BoolDesc v) = EncodedBit v
encodeDataValue (Int16Desc v) = bytes v 2 encodeDataValue _ (Int8Desc v) = EncodedBytes $ bytes v 1
encodeDataValue (Int32Desc v) = bytes v 4 encodeDataValue _ (Int16Desc v) = EncodedBytes $ bytes v 2
encodeDataValue (Int64Desc v) = bytes v 8 encodeDataValue _ (Int32Desc v) = EncodedBytes $ bytes v 4
encodeDataValue (UInt8Desc v) = bytes v 1 encodeDataValue _ (Int64Desc v) = EncodedBytes $ bytes v 8
encodeDataValue (UInt16Desc v) = bytes v 2 encodeDataValue _ (UInt8Desc v) = EncodedBytes $ bytes v 1
encodeDataValue (UInt32Desc v) = bytes v 4 encodeDataValue _ (UInt16Desc v) = EncodedBytes $ bytes v 2
encodeDataValue (UInt64Desc v) = bytes v 8 encodeDataValue _ (UInt32Desc v) = EncodedBytes $ bytes v 4
encodeDataValue (Float32Desc v) = bytes (floatToWord v) 4 encodeDataValue _ (UInt64Desc v) = EncodedBytes $ bytes v 8
encodeDataValue (Float64Desc v) = bytes (doubleToWord v) 8 encodeDataValue _ (Float32Desc v) = EncodedBytes $ bytes (floatToWord v) 4
encodeDataValue (TextDesc _) = error "Not fixed-width data." encodeDataValue _ (Float64Desc v) = EncodedBytes $ bytes (doubleToWord v) 8
encodeDataValue (DataDesc _) = error "Not fixed-width data." encodeDataValue _ (TextDesc _) = error "Not fixed-width data."
encodeDataValue (EnumerantValueDesc v) = bytes (enumerantNumber v) 2 encodeDataValue _ (DataDesc _) = error "Not fixed-width data."
encodeDataValue (StructValueDesc _) = error "Not fixed-width data." encodeDataValue _ (EnumerantValueDesc v) = EncodedBytes $ bytes (enumerantNumber v) 2
encodeDataValue (ListDesc _) = error "Not fixed-width data." encodeDataValue (StructType desc) (StructValueDesc assignments) = let
encodeMaskedDataValue v Nothing = encodeDataValue v
encodeMaskedDataValue v (Just d) = zipWith xor (encodeDataValue v) (encodeDataValue d)
packBits :: Bits a => Int -> [(Bool, Maybe Bool)] -> a
packBits _ [] = 0
packBits offset ((True, Nothing):bits) = setBit (packBits (offset + 1) bits) offset
packBits offset ((False, Nothing):bits) = packBits (offset + 1) bits
packBits offset ((b, Just d):bits) = packBits offset ((b /= d, Nothing):bits)
-- The tuples are (offsetInBits, type, value, defaultValue) for each field to encode.
encodeData :: Integer -> [(Integer, TypeDesc, ValueDesc, Maybe ValueDesc)] -> [Word8]
encodeData size = loop 0 where
loop bit [] | bit == size = []
loop bit [] | bit > size = error "Data values overran size."
loop bit [] = 0:loop (bit + 8) []
loop bit rest@((valuePos, _, BoolDesc _, _):_) | valuePos == bit = let
(bits, rest2) = popBits (bit + 8) rest
in packBits 0 bits : loop (bit + 8) rest2
loop bit ((valuePos, _, value, defaultValue):rest) | valuePos == bit =
encodeMaskedDataValue value defaultValue ++
loop (bit + sizeInBits (fieldValueSize value)) rest
loop bit rest@((valuePos, _, _, _):_) | valuePos > bit = 0 : loop (bit + 8) rest
loop _ _ = error "Data values were out-of-order."
popBits limit ((valuePos, _, BoolDesc b, d):rest) | valuePos < limit = let
(restBits, rest2) = popBits limit rest
defaultB = fmap (\(BoolDesc b2) -> b2) d
in ((b, defaultB):restBits, rest2)
popBits _ rest = ([], rest)
encodeReferences :: Integer -> Integer -> [(Integer, TypeDesc, ValueDesc)] -> ([Word8], [Word8])
encodeReferences o size = loop 0 (o + size - 1) where
loop idx offset ((pos, t, v):rest) | idx == pos = let
(ref, obj) = case (t, v) of
(StructType desc, StructValueDesc assignments) -> let
(dataBytes, refBytes, childBytes) = encodeStruct desc assignments 0 (dataBytes, refBytes, childBytes) = encodeStruct desc assignments 0
in (encodeStructReference desc offset, concat [dataBytes, refBytes, childBytes]) in if null refBytes && null childBytes
(ListType elementType, ListDesc items) -> then EncodedBytes dataBytes
(encodeListReference (elementSize elementType) (genericLength items) offset, else error "encodeDataValue called on struct that wasn't plain data."
encodeDataValue (InlineStructType desc) v = encodeDataValue (StructType desc) v
encodeDataValue _ (StructValueDesc _) = error "Type/value mismatch."
encodeDataValue _ (ListDesc _) = error "Not fixed-width data."
encodeMaskedDataValue t v Nothing = encodeDataValue t v
encodeMaskedDataValue t v (Just d) = xorData (encodeDataValue t v) (encodeDataValue t d)
encodePointerValue :: TypeDesc -> ValueDesc -> (Integer -> [Word8], [Word8])
encodePointerValue _ (TextDesc text) = let
encoded = UTF8.encode text ++ [0]
in (encodeListReference (SizeData Size8) (genericLength encoded), padToWord encoded)
encodePointerValue _ (DataDesc d) =
(encodeListReference (SizeData Size8) (genericLength d), padToWord d)
encodePointerValue (StructType desc) (StructValueDesc assignments) = let
(dataBytes, refBytes, childBytes) = encodeStruct desc assignments 0
in (encodeStructReference desc, concat [padToWord dataBytes, refBytes, childBytes])
encodePointerValue (InlineStructType desc) v = encodePointerValue (StructType desc) v
encodePointerValue (ListType elementType) (ListDesc items) =
(encodeListReference (elementSize elementType) (genericLength items),
encodeList elementType items) encodeList elementType items)
(BuiltinType BuiltinText, TextDesc text) -> let encodePointerValue _ _ = error "Unknown pointer type."
encoded = (UTF8.encode text ++ [0])
in (encodeListReference Size8 (genericLength encoded) offset, padToWord encoded) -- Given a sorted list of (bitOffset, data), pack into a byte array.
(BuiltinType BuiltinData, DataDesc d) -> let packBytes :: Integer -- Total size of array to pack, in bits.
in (encodeListReference Size8 (genericLength d) offset, padToWord d) -> [(Integer, EncodedData)] -- (offset, data) pairs to pack. Must be in order.
_ -> error "Unknown reference type." -> [Word8]
len = genericLength obj packBytes size = loop 0 where
wordLen = if mod len 8 == 0 then div len 8 else error "Child not word-aligned." loop :: Integer -> [(Integer, EncodedData)] -> [Word8]
(refs, objects) = loop (idx + 1) (offset + wordLen - 1) rest loop bit [] | bit <= size = genericReplicate (div (size - bit + 7) 8) 0
in (ref ++ refs, obj ++ objects) loop bit [] | bit > size = error "Data values overran size."
loop idx offset rest@((pos, _, _):_) = let loop bit values@((offset, _):_) | offset >= bit + 8 = 0:loop (bit + 8) values
loop bit ((offset, EncodedBit True):rest) = let
firstByte:restBytes = loop bit rest
in setBit firstByte (fromIntegral (offset - bit)) : restBytes
loop bit ((_, EncodedBit False):rest) = loop bit rest
loop bit ((offset, EncodedBytes encoded):rest) | offset == bit =
encoded ++ loop (bit + genericLength encoded * 8) rest
loop _ _ = error "Data values overlapped."
bytesToWords i = if mod i 8 == 0 then div i 8
else error "Byte count did not divide evenly into words."
packPointers :: Integer -- Total number of pointers to pack.
-> [(Integer, (Integer -> [Word8], [Word8]))]
-> Integer -- Word offset from end of pointer array to child area.
-> ([Word8], [Word8])
packPointers size items o = loop 0 items (o + size - 1) where
loop :: Integer -> [(Integer, (Integer -> [Word8], [Word8]))] -> Integer -> ([Word8], [Word8])
loop idx ((pos, (mkptrs, child)):rest) childOff | idx == pos = let
ptrs = mkptrs childOff
ptrCount = bytesToWords (genericLength ptrs)
newChildOff = childOff - ptrCount + bytesToWords (genericLength child)
(restPtrs, restChildren) = loop (idx + ptrCount) rest newChildOff
in (ptrs ++ restPtrs, child ++ restChildren)
loop idx rest@((pos, _):_) childOff = let
padCount = pos - idx padCount = pos - idx
(refs, objects) = loop pos (offset - padCount) rest (restPtrs, restChildren) = loop pos rest (childOff - padCount)
in (genericReplicate (padCount * 8) 0 ++ refs, objects) in (genericReplicate (padCount * 8) 0 ++ restPtrs, restChildren)
loop idx _ [] = (genericReplicate ((size - idx) * 8) 0, []) loop idx [] _ = (genericReplicate ((size - idx) * 8) 0, [])
encodeStructList :: Integer -> StructDesc -> [[(FieldDesc, ValueDesc)]] -> ([Word8], [Word8])
encodeStructList o desc elements = loop (o + eSize * genericLength elements) elements where
eSize = packingSize $ structPacking desc
loop _ [] = ([], [])
loop offset (element:rest) = let
offsetFromElementEnd = offset - eSize
(dataBytes, refBytes, childBytes) = encodeStruct desc element offsetFromElementEnd
childLen = genericLength childBytes
childWordLen = if mod childLen 8 == 0
then div childLen 8
else error "Child not word-aligned."
(restBytes, restChildren) = loop (offsetFromElementEnd + childWordLen) rest
in (dataBytes ++ refBytes ++ restBytes, childBytes ++ restChildren)
encodeStructReference desc offset = encodeStructReference desc offset =
bytes (offset * 4 + structTag) 4 ++ bytes (offset * 4 + structTag) 4 ++
bytes (packingDataSize $ structPacking desc) 2 ++ bytes (dataSectionWordSize $ structDataSize desc) 2 ++
bytes (packingReferenceCount $ structPacking desc) 2 bytes (structPointerCount desc) 2
encodeListReference elemSize@(SizeInlineComposite ds rc) elementCount offset = encodeListReference elemSize@(SizeInlineComposite ds rc) elementCount offset =
bytes (offset * 4 + listTag) 4 ++ bytes (offset * 4 + listTag) 4 ++
bytes (fieldSizeEnum elemSize + shiftL (elementCount * (ds + rc)) 3) 4 bytes (fieldSizeEnum elemSize + shiftL (elementCount * (dataSectionWordSize ds + rc)) 3) 4
encodeListReference elemSize elementCount offset = encodeListReference elemSize elementCount offset =
bytes (offset * 4 + listTag) 4 ++ bytes (offset * 4 + listTag) 4 ++
bytes (fieldSizeEnum elemSize + shiftL elementCount 3) 4 bytes (fieldSizeEnum elemSize + shiftL elementCount 3) 4
fieldSizeEnum Size0 = 0 fieldSizeEnum SizeVoid = 0
fieldSizeEnum Size1 = 1 fieldSizeEnum (SizeData Size1) = 1
fieldSizeEnum Size8 = 2 fieldSizeEnum (SizeData Size8) = 2
fieldSizeEnum Size16 = 3 fieldSizeEnum (SizeData Size16) = 3
fieldSizeEnum Size32 = 4 fieldSizeEnum (SizeData Size32) = 4
fieldSizeEnum Size64 = 5 fieldSizeEnum (SizeData Size64) = 5
fieldSizeEnum SizeReference = 6 fieldSizeEnum SizeReference = 6
fieldSizeEnum (SizeInlineComposite _ _) = 7 fieldSizeEnum (SizeInlineComposite _ _) = 7
...@@ -164,47 +157,94 @@ listTag = 1 ...@@ -164,47 +157,94 @@ listTag = 1
-- childOffset = number of words between the last reference and the location where children will -- childOffset = number of words between the last reference and the location where children will
-- be allocated. -- be allocated.
encodeStruct desc assignments childOffset = (dataBytes, referenceBytes, children) where encodeStruct desc assignments childOffset = let
-- Values explicitly assigned. dataSize = dataSectionBits $ structDataSize desc
explicitValues = [(fieldOffset f, fieldType f, v, fieldDefaultValue f) | (f, v) <- assignments] dataSection = packBytes dataSize $ sortBy (compare `on` fst)
$ structDataSectionValues assignments
-- Values of union tags.
unionValues = [(unionTagOffset u, BuiltinType BuiltinUInt16, UInt16Desc $ fromIntegral n, pointerCount = structPointerCount desc
Nothing) (pointerSection, children) = packPointers pointerCount
(sortBy (compare `on` fst) $ structPointerSectionValues assignments)
childOffset
in (dataSection, pointerSection, children)
dataBitOffset (DataOffset size off) = dataSizeInBits size * off
dataBitOffset (InlineCompositeOffset off _ dataSectionSize _) =
off * dataSizeInBits (dataSectionAlignment dataSectionSize)
dataBitOffset _ = error "Not a data field."
structDataSectionValues assignments = let
simpleValues = [(dataBitOffset $ fieldOffset f,
encodeMaskedDataValue (fieldType f) v (fieldDefaultValue f))
| (f@FieldDesc { fieldOffset = DataOffset _ _ }, v) <- assignments]
inlineStructValues = do -- List monad!
(FieldDesc { fieldOffset = InlineCompositeOffset off _ sectionSize _ },
StructValueDesc v) <- assignments
let bitOffset = off * dataSizeInBits (dataSectionAlignment sectionSize)
(pos, v2) <- structDataSectionValues v
return (pos + bitOffset, v2)
unionTags = [(unionTagOffset u * 16,
encodeDataValue (BuiltinType BuiltinUInt16) (UInt16Desc $ fromIntegral n))
| (FieldDesc {fieldUnion = Just (u, n)}, _) <- assignments] | (FieldDesc {fieldUnion = Just (u, n)}, _) <- assignments]
allValues = explicitValues ++ unionValues in simpleValues ++ inlineStructValues ++ unionTags
allData = [ (o * sizeInBits (fieldValueSize v), t, v, d)
| (o, t, v, d) <- allValues, isDataFieldSize $ fieldValueSize v ]
allReferences = [ (o, t, v) | (o, t, v, _) <- allValues
, not $ isDataFieldSize $ fieldValueSize v ]
sortedData = sortBy compareDataValues allData structPointerSectionValues :: [(FieldDesc, ValueDesc)] -> [(Integer, (Integer -> [Word8], [Word8]))]
compareDataValues (o1, _, _, _) (o2, _, _, _) = compare o1 o2 structPointerSectionValues assignments = let
sortedReferences = sortBy compareReferenceValues allReferences simpleValues = [(off, encodePointerValue (fieldType f) v)
compareReferenceValues (o1, _, _) (o2, _, _) = compare o1 o2 | (f@FieldDesc { fieldOffset = PointerOffset off }, v) <- assignments]
dataBytes = encodeData (packingDataSize (structPacking desc) * 64) sortedData inlineStructValues = do -- List monad!
(referenceBytes, children) = encodeReferences childOffset (FieldDesc { fieldOffset = InlineCompositeOffset _ off _ _ },
(packingReferenceCount $ structPacking desc) sortedReferences StructValueDesc v) <- assignments
(pos, v2) <- structPointerSectionValues v
return (pos + off, v2)
in simpleValues ++ inlineStructValues
encodeList elementType elements = case elementSize elementType of encodeList elementType elements = case elementSize elementType of
SizeInlineComposite _ _ -> case elementType of SizeVoid -> []
StructType desc -> let SizeInlineComposite _ _ -> let
handleStructType desc = let
count = genericLength elements count = genericLength elements
tag = encodeStructReference desc count tag = encodeStructReference desc count
(elemBytes, childBytes) = encodeStructList 0 desc [v | StructValueDesc v <- elements] (elemBytes, childBytes) = encodeStructList 0 desc [v | StructValueDesc v <- elements]
in concat [tag, elemBytes, childBytes] in concat [tag, elemBytes, childBytes]
in case elementType of
StructType desc -> handleStructType desc
InlineStructType desc -> handleStructType desc
_ -> error "Only structs can be inline composites." _ -> error "Only structs can be inline composites."
SizeReference -> refBytes ++ childBytes where SizeReference -> refBytes ++ childBytes where
(refBytes, childBytes) = encodeReferences 0 (genericLength elements) encodedElements = zip [0..] $ map (encodePointerValue elementType) elements
$ zipWith (\i v -> (i, elementType, v)) [0..] elements (refBytes, childBytes) = packPointers (genericLength elements) encodedElements 0
size -> encodeData (roundUpToMultiple 64 (genericLength elements * sizeInBits size)) SizeData size -> let
$ zipWith (\i v -> (i * sizeInBits size, elementType, v, Nothing)) [0..] elements bits = dataSizeInBits size
encodedElements = zip [0,bits..] $ map (encodeDataValue elementType) elements
in padToWord $ packBytes (genericLength elements * bits) encodedElements
-- Encode an inline-composite struct list. Not used in cases where the struct is data-only and
-- fits into 32 bits or less.
encodeStructList :: Integer -> StructDesc -> [[(FieldDesc, ValueDesc)]] -> ([Word8], [Word8])
encodeStructList o desc elements = loop (o + eSize * genericLength elements) elements where
eSize = dataSectionWordSize (structDataSize desc) + structPointerCount desc
loop _ [] = ([], [])
loop offset (element:rest) = let
offsetFromElementEnd = offset - eSize
(dataBytes, refBytes, childBytes) = encodeStruct desc element offsetFromElementEnd
childLen = genericLength childBytes
childWordLen = if mod childLen 8 == 0
then div childLen 8
else error "Child not word-aligned."
(restBytes, restChildren) = loop (offsetFromElementEnd + childWordLen) rest
in (padToWord dataBytes ++ refBytes ++ restBytes, childBytes ++ restChildren)
encodeMessage (StructType desc) (StructValueDesc assignments) = let encodeMessage (StructType desc) (StructValueDesc assignments) = let
(dataBytes, refBytes, childBytes) = encodeStruct desc assignments 0 (dataBytes, refBytes, childBytes) = encodeStruct desc assignments 0
in concat [encodeStructReference desc (0::Integer), dataBytes, refBytes, childBytes] in concat [encodeStructReference desc (0::Integer), padToWord dataBytes, refBytes, childBytes]
encodeMessage (InlineStructType desc) val = encodeMessage (StructType desc) val
encodeMessage (ListType elementType) (ListDesc elements) = encodeMessage (ListType elementType) (ListDesc elements) =
encodeListReference (elementSize elementType) (genericLength elements) (0::Integer) ++ encodeListReference (elementSize elementType) (genericLength elements) (0::Integer) ++
encodeList elementType elements encodeList elementType elements
......
...@@ -295,6 +295,7 @@ inline {{fieldType}}::Builder {{typeFullName}}::Builder::init{{fieldTitleCase}}( ...@@ -295,6 +295,7 @@ inline {{fieldType}}::Builder {{typeFullName}}::Builder::init{{fieldTitleCase}}(
{{/fieldIsBlob}} {{/fieldIsBlob}}
{{! ------------------------------------------------------------------------------------------- }} {{! ------------------------------------------------------------------------------------------- }}
{{#fieldIsStruct}} {{#fieldIsStruct}}
{{^fieldIsInlineStruct}}
inline {{fieldType}}::Reader {{typeFullName}}::Reader::get{{fieldTitleCase}}() { inline {{fieldType}}::Reader {{typeFullName}}::Reader::get{{fieldTitleCase}}() {
{{#fieldUnion}} {{#fieldUnion}}
CAPNPROTO_INLINE_DPRECOND(which() == {{unionTitleCase}}::{{fieldUpperCase}}, CAPNPROTO_INLINE_DPRECOND(which() == {{unionTitleCase}}::{{fieldUpperCase}},
...@@ -324,6 +325,34 @@ inline {{fieldType}}::Builder {{typeFullName}}::Builder::get{{fieldTitleCase}}() ...@@ -324,6 +325,34 @@ inline {{fieldType}}::Builder {{typeFullName}}::Builder::get{{fieldTitleCase}}()
{{#fieldDefaultBytes}}DEFAULT_{{fieldUpperCase}}.words{{/fieldDefaultBytes}} {{#fieldDefaultBytes}}DEFAULT_{{fieldUpperCase}}.words{{/fieldDefaultBytes}}
{{^fieldDefaultBytes}}nullptr{{/fieldDefaultBytes}})); {{^fieldDefaultBytes}}nullptr{{/fieldDefaultBytes}}));
} }
{{/fieldIsInlineStruct}}
{{#fieldIsInlineStruct}}
inline {{fieldType}}::Reader {{typeFullName}}::Reader::get{{fieldTitleCase}}() {
{{#fieldUnion}}
CAPNPROTO_INLINE_DPRECOND(which() == {{unionTitleCase}}::{{fieldUpperCase}},
"Must check which() before get()ing a union member.");
{{/fieldUnion}}
return {{fieldType}}::Reader(_reader.getInlineStructField(
{{fieldOffset}}));
}
inline {{fieldType}}::Builder {{typeFullName}}::Builder::init{{fieldTitleCase}}() {
{{#fieldUnion}}
_builder.setDataField<{{unionTitleCase}}::Which>(
{{unionTagOffset}} * ::capnproto::ELEMENTS, {{unionTitleCase}}::{{fieldUpperCase}});
{{/fieldUnion}}
return {{fieldType}}::Builder(_builder.initInlineStructField(
{{fieldOffset}}));
}
inline {{fieldType}}::Builder {{typeFullName}}::Builder::get{{fieldTitleCase}}() {
{{#fieldUnion}}
CAPNPROTO_INLINE_DPRECOND(which() == {{unionTitleCase}}::{{fieldUpperCase}},
"Must check which() before get()ing a union member.");
{{/fieldUnion}}
return {{fieldType}}::Builder(_builder.getInlineStructField(
{{fieldOffset}}));
}
{{/fieldIsInlineStruct}}
{{/fieldIsStruct}} {{/fieldIsStruct}}
{{! ------------------------------------------------------------------------------------------- }} {{! ------------------------------------------------------------------------------------------- }}
{{#fieldIsList}} {{#fieldIsList}}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment