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) {
return os << "}, " << us.dataOffset << ")";
}
template <typename T> T one() { return static_cast<T>(1); }
template <> Text::Reader one() { return "1"; }
template <> Void one() { return Void::VOID; }
template <typename T, typename U>
UnionState initUnion(U (TestUnion::Builder::*unionGetter)(),
void (U::Builder::*setter)(T value)) {
template <typename StructType, typename Func>
UnionState initUnion(Func&& initializer) {
// 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
// discriminants.
MallocMessageBuilder builder;
((builder.getRoot<TestUnion>().*unionGetter)().*setter)(one<T>());
initializer(builder.getRoot<StructType>());
ArrayPtr<const word> segment = builder.getSegmentsForOutput()[0];
CHECK(segment.size() > 2, segment.size());
......@@ -204,58 +199,60 @@ found:
offset);
}
#define INIT_UNION(unionName, fieldName) \
initUnion(&TestUnion::Builder::get##unionName, &TestUnion::unionName::Builder::set##fieldName)
TEST(Encoding, UnionLayout) {
EXPECT_EQ(UnionState({ 0,0,0,0}, -1), INIT_UNION(Union0, U0f0s0));
EXPECT_EQ(UnionState({ 1,0,0,0}, 0), INIT_UNION(Union0, U0f0s1));
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({ 4,0,0,0}, 0), INIT_UNION(Union0, U0f0s32));
EXPECT_EQ(UnionState({ 5,0,0,0}, 0), INIT_UNION(Union0, U0f0s64));
EXPECT_EQ(UnionState({ 6,0,0,0}, 448), INIT_UNION(Union0, U0f0sp));
EXPECT_EQ(UnionState({ 7,0,0,0}, -1), INIT_UNION(Union0, U0f1s0));
EXPECT_EQ(UnionState({ 8,0,0,0}, 0), INIT_UNION(Union0, U0f1s1));
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({11,0,0,0}, 0), INIT_UNION(Union0, U0f1s32));
EXPECT_EQ(UnionState({12,0,0,0}, 0), INIT_UNION(Union0, U0f1s64));
EXPECT_EQ(UnionState({13,0,0,0}, 448), INIT_UNION(Union0, U0f1sp));
EXPECT_EQ(UnionState({0, 0,0,0}, -1), INIT_UNION(Union1, U1f0s0));
EXPECT_EQ(UnionState({0, 1,0,0}, 65), INIT_UNION(Union1, U1f0s1));
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, 4,0,0}, 72), INIT_UNION(Union1, U1f1s8));
EXPECT_EQ(UnionState({0, 5,0,0}, 80), INIT_UNION(Union1, U1f0s16));
EXPECT_EQ(UnionState({0, 6,0,0}, 80), INIT_UNION(Union1, U1f1s16));
EXPECT_EQ(UnionState({0, 7,0,0}, 96), INIT_UNION(Union1, U1f0s32));
EXPECT_EQ(UnionState({0, 8,0,0}, 96), INIT_UNION(Union1, U1f1s32));
EXPECT_EQ(UnionState({0, 9,0,0}, 128), INIT_UNION(Union1, U1f0s64));
EXPECT_EQ(UnionState({0,10,0,0}, 128), INIT_UNION(Union1, U1f1s64));
EXPECT_EQ(UnionState({0,11,0,0}, 512), INIT_UNION(Union1, U1f0sp));
EXPECT_EQ(UnionState({0,12,0,0}, 512), INIT_UNION(Union1, U1f1sp));
EXPECT_EQ(UnionState({0,13,0,0}, -1), INIT_UNION(Union1, U1f2s0));
EXPECT_EQ(UnionState({0,14,0,0}, 128), INIT_UNION(Union1, U1f2s1));
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,17,0,0}, 128), INIT_UNION(Union1, U1f2s32));
EXPECT_EQ(UnionState({0,18,0,0}, 128), INIT_UNION(Union1, U1f2s64));
EXPECT_EQ(UnionState({0,19,0,0}, 512), INIT_UNION(Union1, U1f2sp));
EXPECT_EQ(UnionState({0,0,0,0}, 192), INIT_UNION(Union2, U2f0s1));
EXPECT_EQ(UnionState({0,0,0,0}, 193), INIT_UNION(Union3, U3f0s1));
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,2,0}, 224), INIT_UNION(Union2, U2f0s16));
EXPECT_EQ(UnionState({0,0,0,2}, 240), INIT_UNION(Union3, U3f0s16));
EXPECT_EQ(UnionState({0,0,3,0}, 256), INIT_UNION(Union2, U2f0s32));
EXPECT_EQ(UnionState({0,0,0,3}, 288), INIT_UNION(Union3, U3f0s32));
EXPECT_EQ(UnionState({0,0,4,0}, 320), INIT_UNION(Union2, U2f0s64));
EXPECT_EQ(UnionState({0,0,0,4}, 384), INIT_UNION(Union3, U3f0s64));
#define INIT_UNION(setter) \
initUnion<TestUnion>([](TestUnion::Builder b) {b.setter;})
EXPECT_EQ(UnionState({ 0,0,0,0}, -1), INIT_UNION(getUnion0().setU0f0s0(Void::VOID)));
EXPECT_EQ(UnionState({ 1,0,0,0}, 0), INIT_UNION(getUnion0().setU0f0s1(1)));
EXPECT_EQ(UnionState({ 2,0,0,0}, 0), INIT_UNION(getUnion0().setU0f0s8(1)));
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({ 5,0,0,0}, 0), INIT_UNION(getUnion0().setU0f0s64(1)));
EXPECT_EQ(UnionState({ 6,0,0,0}, 448), INIT_UNION(getUnion0().setU0f0sp("1")));
EXPECT_EQ(UnionState({ 7,0,0,0}, -1), INIT_UNION(getUnion0().setU0f1s0(Void::VOID)));
EXPECT_EQ(UnionState({ 8,0,0,0}, 0), INIT_UNION(getUnion0().setU0f1s1(1)));
EXPECT_EQ(UnionState({ 9,0,0,0}, 0), INIT_UNION(getUnion0().setU0f1s8(1)));
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({12,0,0,0}, 0), INIT_UNION(getUnion0().setU0f1s64(1)));
EXPECT_EQ(UnionState({13,0,0,0}, 448), INIT_UNION(getUnion0().setU0f1sp("1")));
EXPECT_EQ(UnionState({0, 0,0,0}, -1), INIT_UNION(getUnion1().setU1f0s0(Void::VOID)));
EXPECT_EQ(UnionState({0, 1,0,0}, 65), INIT_UNION(getUnion1().setU1f0s1(1)));
EXPECT_EQ(UnionState({0, 2,0,0}, 65), INIT_UNION(getUnion1().setU1f1s1(1)));
EXPECT_EQ(UnionState({0, 3,0,0}, 72), INIT_UNION(getUnion1().setU1f0s8(1)));
EXPECT_EQ(UnionState({0, 4,0,0}, 72), INIT_UNION(getUnion1().setU1f1s8(1)));
EXPECT_EQ(UnionState({0, 5,0,0}, 80), INIT_UNION(getUnion1().setU1f0s16(1)));
EXPECT_EQ(UnionState({0, 6,0,0}, 80), INIT_UNION(getUnion1().setU1f1s16(1)));
EXPECT_EQ(UnionState({0, 7,0,0}, 96), INIT_UNION(getUnion1().setU1f0s32(1)));
EXPECT_EQ(UnionState({0, 8,0,0}, 96), INIT_UNION(getUnion1().setU1f1s32(1)));
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,11,0,0}, 512), INIT_UNION(getUnion1().setU1f0sp("1")));
EXPECT_EQ(UnionState({0,12,0,0}, 512), INIT_UNION(getUnion1().setU1f1sp("1")));
EXPECT_EQ(UnionState({0,13,0,0}, -1), INIT_UNION(getUnion1().setU1f2s0(Void::VOID)));
EXPECT_EQ(UnionState({0,14,0,0}, 128), INIT_UNION(getUnion1().setU1f2s1(1)));
EXPECT_EQ(UnionState({0,15,0,0}, 128), INIT_UNION(getUnion1().setU1f2s8(1)));
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,18,0,0}, 128), INIT_UNION(getUnion1().setU1f2s64(1)));
EXPECT_EQ(UnionState({0,19,0,0}, 512), INIT_UNION(getUnion1().setU1f2sp("1")));
EXPECT_EQ(UnionState({0,0,0,0}, 192), INIT_UNION(getUnion2().setU2f0s1(1)));
EXPECT_EQ(UnionState({0,0,0,0}, 193), INIT_UNION(getUnion3().setU3f0s1(1)));
EXPECT_EQ(UnionState({0,0,1,0}, 200), INIT_UNION(getUnion2().setU2f0s8(1)));
EXPECT_EQ(UnionState({0,0,0,1}, 208), INIT_UNION(getUnion3().setU3f0s8(1)));
EXPECT_EQ(UnionState({0,0,2,0}, 224), INIT_UNION(getUnion2().setU2f0s16(1)));
EXPECT_EQ(UnionState({0,0,0,2}, 240), INIT_UNION(getUnion3().setU3f0s16(1)));
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) {
......@@ -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.
// TODO(cleanup): Move to a different test?
......
......@@ -415,7 +415,7 @@ struct WireHelpers {
ref->structRef.set(size);
// 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(
......@@ -442,7 +442,7 @@ struct WireHelpers {
"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(
......@@ -603,7 +603,7 @@ struct WireHelpers {
if (ref == nullptr || ref->isNull()) {
useDefault:
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());
}
segment = nullptr;
......@@ -637,7 +637,7 @@ struct WireHelpers {
return StructReader(
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(),
0 * BITS, nestingLimit - 1);
}
......@@ -752,7 +752,8 @@ struct WireHelpers {
}
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 {
// The elements of the list are NOT structs.
......@@ -774,28 +775,17 @@ struct WireHelpers {
// old version of the protocol. We need to verify that the struct's first field matches
// what the sender sent us.
WordCount dataSize;
WireReferenceCount referenceCount;
BitCount dataSize = 0 * BITS;
WireReferenceCount referenceCount = 0 * REFERENCES;
switch (ref->listRef.elementSize()) {
case FieldSize::VOID:
dataSize = 0 * WORDS;
referenceCount = 0 * REFERENCES;
break;
case FieldSize::BIT:
case FieldSize::BYTE:
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::VOID: break;
case FieldSize::BIT: dataSize = 1 * BITS; break;
case FieldSize::BYTE: dataSize = 8 * BITS; break;
case FieldSize::TWO_BYTES: dataSize = 16 * BITS; break;
case FieldSize::FOUR_BYTES: dataSize = 32 * BITS; break;
case FieldSize::EIGHT_BYTES: dataSize = 64 * BITS; break;
case FieldSize::REFERENCE: referenceCount = 1 * REFERENCES; break;
case FieldSize::INLINE_COMPOSITE:
FAIL_CHECK();
......@@ -978,7 +968,7 @@ StructReader StructBuilder::asReader() const {
static_assert(sizeof(WireReference::structRef.refCount) == 2,
"Has the maximum reference count changed?");
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) {
......@@ -998,7 +988,7 @@ StructReader StructReader::readRoot(
}
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());
}
......@@ -1031,7 +1021,7 @@ StructBuilder ListBuilder::getStructElement(
ElementCount index, decltype(WORDS/ELEMENTS) elementSize, WordCount structDataSize) const {
word* structPtr = ptr + elementSize * index;
return StructBuilder(segment, structPtr,
reinterpret_cast<WireReference*>(structPtr + structDataSize));
reinterpret_cast<WireReference*>(structPtr + structDataSize), 0 * BITS);
}
ListBuilder ListBuilder::initListElement(
......@@ -1087,9 +1077,9 @@ ListReader ListBuilder::asReader(FieldSize elementSize) const {
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,
(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());
}
......@@ -1103,7 +1093,7 @@ StructReader ListReader::getStructElement(ElementCount index) const {
const byte* structPtr = reinterpret_cast<const byte*>(ptr) + indexBit / BITS_PER_BYTE;
return StructReader(
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);
}
......
......@@ -28,6 +28,11 @@
// as does other parts of the Cap'n proto library which provide a higher-level interface for
// 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_
#define CAPNPROTO_LAYOUT_H_
......@@ -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,
// 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
// struct, new fields cannot be added to it. It's unclear if this is really useful so at this
// time it is not supported.
// struct, new fields cannot be added to it. The field's struct type is therefore required to
// be declared "inline" with a fixed width.
};
typedef decltype(BITS / ELEMENTS) BitsPerElement;
......@@ -276,6 +281,17 @@ public:
// 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.
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,
ElementCount elementCount) const;
// Allocates a new list of the given size for the field at the given index in the reference
......@@ -313,11 +329,16 @@ public:
private:
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.
inline StructBuilder(SegmentBuilder* segment, word* data, WireReference* references)
: segment(segment), data(data), references(references) {}
BitCount8 bit0Offset;
// 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 struct WireHelpers;
......@@ -351,6 +372,11 @@ public:
// 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.
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,
const word* defaultValue) const;
// Get the list field at the given index in the reference segment, or the default value if not
......@@ -370,20 +396,21 @@ private:
const void* data;
const WireReference* references;
WordCount8 dataSize; // Size of data segment.
WireReferenceCount8 referenceCount; // Size of the reference segment.
BitCount32 dataSize; // Size of data segment.
WireReferenceCount16 referenceCount; // Size of the reference segment.
BitCount8 bit0Offset;
// 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
// of structs.
// of structs, and to support 1-bit inline structs.
int nestingLimit;
// Limits the depth of message structures to guard against stack-overflow-based DoS attacks.
// 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,
WordCount dataSize, WireReferenceCount referenceCount,
BitCount dataSize, WireReferenceCount referenceCount,
BitCount bit0Offset, int nestingLimit)
: segment(segment), data(data), references(references),
dataSize(dataSize), referenceCount(referenceCount), bit0Offset(bit0Offset),
......@@ -448,7 +475,7 @@ public:
ListReader asReader(FieldSize elementSize) const;
// 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.
private:
......@@ -503,7 +530,7 @@ private:
// 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.
WordCount structDataSize;
BitCount structDataSize;
WireReferenceCount structReferenceCount;
// 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
......@@ -519,7 +546,7 @@ private:
structDataSize(0), structReferenceCount(0),
nestingLimit(nestingLimit) {}
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)
: segment(segment), ptr(ptr), elementCount(elementCount), stepBits(stepBits),
structDataSize(structDataSize), structReferenceCount(structReferenceCount),
......@@ -541,6 +568,10 @@ inline T StructBuilder::getDataField(ElementCount offset) const {
template <>
inline bool StructBuilder::getDataField<bool>(ElementCount offset) const {
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;
return (*reinterpret_cast<uint8_t*>(b) & (1 << (boffset % BITS_PER_BYTE / BITS))) != 0;
}
......@@ -564,6 +595,10 @@ inline void StructBuilder::setDataField(
template <>
inline void StructBuilder::setDataField<bool>(ElementCount offset, bool value) const {
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;
uint bitnum = boffset % BITS_PER_BYTE / BITS;
*reinterpret_cast<uint8_t*>(b) = (*reinterpret_cast<uint8_t*>(b) & ~(1 << bitnum))
......@@ -579,11 +614,37 @@ inline void StructBuilder::setDataField(
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>
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();
} else {
return static_cast<T>(0);
......@@ -597,7 +658,7 @@ inline bool StructReader::getDataField<bool>(ElementCount offset) const {
// This branch should always be optimized away when inlining.
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;
return (*reinterpret_cast<const uint8_t*>(b) & (1 << (boffset % BITS_PER_BYTE / BITS))) != 0;
} else {
......@@ -615,6 +676,19 @@ T StructReader::getDataField(ElementCount offset, typename MaskType<T>::Type mas
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; }
......
......@@ -57,6 +57,9 @@ using ::capnproto::test::TestUnion;
using ::capnproto::test::TestUnionDefaults;
using ::capnproto::test::TestNestedTypes;
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::TestDefaults::Builder builder);
......
......@@ -213,7 +213,6 @@ struct TestUnion {
bit5 @42: Bool;
bit6 @43: Bool;
bit7 @44: Bool;
byte0 @49: UInt8;
# Interleave two unions to be really annoying.
# Also declare in reverse order to make sure union discriminant values are sorted by field number
......@@ -233,6 +232,8 @@ struct TestUnion {
u3f0s8 @48: Int8;
u3f0s1 @46: Bool;
}
byte0 @49: UInt8;
}
struct TestUnionDefaults {
......@@ -274,3 +275,140 @@ struct TestUsing {
outerNestedEnum @1 :OuterNestedEnum = bar;
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() {
return sizeof(T) * BYTES / ELEMENTS;
}
template <typename T>
inline constexpr decltype(BITS / ELEMENTS) bitsPerElement() {
return sizeof(T) * 8 * BITS / ELEMENTS;
}
#ifndef __CDT_PARSER__
template <typename T, typename U>
......
......@@ -4,6 +4,13 @@ cabal-version: >=1.2
build-type: Simple
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
hs-source-dirs: src
main-is: Main.hs
......
......@@ -27,12 +27,12 @@ import Grammar
import Semantics
import Token(Located(Located), locatedPos, locatedValue)
import Parser(parseFile)
import Control.Monad(unless)
import Control.Monad(when, unless)
import qualified Data.Map as Map
import Data.Map((!))
import qualified Data.Set as Set
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.Error(ParseError, newErrorMessage, Message(Message, Expect))
import Text.Printf(printf)
......@@ -67,9 +67,16 @@ instance Monad Status where
return x = Active x []
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 _ (Active x e) = Active x e
recover x (Failed e) = Active x e
recover fallback status = Active value errs where
(value, errs) = case status of
Active v e -> (v, e)
Failed e -> (fallback, e)
succeed :: a -> Status a
succeed x = Active x []
......@@ -154,7 +161,7 @@ lookupDesc scope name = lookupDesc (descParent scope) name
builtinTypeMap :: Map.Map String Desc
builtinTypeMap = Map.fromList
([(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
return (StructValueDesc assignments)
compileValue pos (InlineStructType desc) v = compileValue pos (StructType desc) v
compileValue _ (ListType t) (ListFieldValue l) =
fmap ListDesc (doAll [ compileValue vpos t v | Located vpos v <- l ])
......@@ -254,6 +263,7 @@ compileValue pos (BuiltinType BuiltinData) _ = makeExpectError pos "string"
compileValue pos (EnumType _) _ = makeExpectError pos "enumerant name"
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 (ListType _) _ = makeExpectError pos "list"
......@@ -264,6 +274,8 @@ descAsType _ (DescBuiltinType desc) = succeed (BuiltinType desc)
descAsType name (DescUsing desc) = descAsType name (usingTarget desc)
descAsType name DescBuiltinList = makeError (declNamePos name) message where
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
message = printf "'%s' is not a type." (declNameString name)
......@@ -278,6 +290,18 @@ compileType scope (TypeExpression n (param:moreParams)) = do
if null moreParams
then fmap ListType (compileType scope param)
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."
compileAnnotation :: Desc -> AnnotationTarget -> Annotation
......@@ -378,10 +402,6 @@ requireNoDuplicateNames decls = Active () (loop (List.sort locatedNames)) where
dupError val = newErrorMessage (Message message) where
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
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 \
......@@ -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)
packValue Size64 s@(PackingState { packingDataSize = ds }) =
(ds, s { packingDataSize = ds + 1 })
initialPackingState = PackingState Map.empty 0 0
packValue :: FieldSize -> PackingState -> (FieldOffset, PackingState)
packValue SizeVoid s = (VoidOffset, s)
packValue SizeReference s@(PackingState { packingReferenceCount = rc }) =
(rc, s { packingReferenceCount = rc + 1 })
packValue (SizeInlineComposite _ _) _ = error "Inline fields not yet supported."
packValue Size32 s@(PackingState { packingHole32 = 0 }) =
case packValue Size64 s of
(o64, s2) -> (o64 * 2, s2 { packingHole32 = o64 * 2 + 1 })
packValue Size32 s@(PackingState { packingHole32 = h32 }) =
(h32, s { packingHole32 = 0 })
packValue Size16 s@(PackingState { packingHole16 = 0 }) =
case packValue Size32 s of
(o32, s2) -> (o32 * 2, s2 { packingHole16 = o32 * 2 + 1 })
packValue Size16 s@(PackingState { packingHole16 = h16 }) =
(h16, s { packingHole16 = 0 })
packValue Size8 s@(PackingState { packingHole8 = 0 }) =
case packValue Size16 s of
(o16, s2) -> (o16 * 2, s2 { packingHole8 = o16 * 2 + 1 })
packValue Size8 s@(PackingState { packingHole8 = h8 }) =
(h8, s { packingHole8 = 0 })
packValue Size1 s@(PackingState { packingHole1 = 0 }) =
case packValue Size8 s of
(o8, s2) -> (o8 * 8, s2 { packingHole1 = o8 * 8 + 1 })
packValue Size1 s@(PackingState { packingHole1 = h1 }) =
(h1, s { packingHole1 = if mod (h1 + 1) 8 == 0 then 0 else h1 + 1 })
packValue Size0 s = (0, s)
initialUnionPackingState = UnionPackingState Nothing Nothing
(PointerOffset rc, s { packingReferenceCount = rc + 1 })
packValue (SizeInlineComposite (DataSectionWords inlineDs) inlineRc)
s@(PackingState { packingDataSize = ds, packingReferenceCount = rc }) =
(InlineCompositeOffset ds rc (DataSectionWords inlineDs) inlineRc,
s { packingDataSize = ds + inlineDs
, packingReferenceCount = rc + inlineRc })
packValue (SizeInlineComposite inlineDs inlineRc)
s@(PackingState { packingReferenceCount = rc }) = let
size = (dataSectionAlignment inlineDs)
(offset, s2) = packData size s
in (InlineCompositeOffset offset rc inlineDs inlineRc,
s2 { packingReferenceCount = rc + inlineRc })
packValue (SizeData size) s = let (o, s2) = packData size s in (DataOffset size o, s2)
packData :: DataSize -> PackingState -> (Integer, PackingState)
packData Size64 s@(PackingState { packingDataSize = ds }) =
(ds, s { packingDataSize = ds + 1 })
packData size s = let
-- updateLookupWithKey doesn't quite work here because it returns the new value if updated, or
-- the old value if not. We really always want the old value and have no way to distinguish.
-- There appears to be no function that does this, AFAICT.
hole = Map.lookup size $ packingHoles s
newHoles = Map.update splitHole size $ packingHoles s
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.
-> UnionPackingState -- Current layout of the union
-> PackingState -- Current layout of the struct.
-> (Integer, UnionPackingState, PackingState)
packUnionizedValue (SizeInlineComposite _ _) _ _ = error "Can't put inline composite into union."
packUnionizedValue Size0 u s = (0, u, s)
-- Pack reference when we already have a reference slot allocated.
packUnionizedValue SizeReference u@(UnionPackingState _ (Just offset)) s = (offset, u, s)
-> (FieldOffset, UnionPackingState, PackingState)
packUnionizedValue SizeVoid u s = (VoidOffset, u, s)
-- Pack data when there is no existing slot.
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.
packUnionizedValue SizeReference (UnionPackingState d Nothing) s = (offset, u2, s2) where
(offset, s2) = packValue SizeReference s
u2 = UnionPackingState d (Just offset)
-- Pack data.
packUnionizedValue size (UnionPackingState d r) s =
case packUnionizedData (fromMaybe (0, Size0) d) s size of
Just (offset, slotOffset, slotSize, s2) ->
(offset, UnionPackingState (Just (slotOffset, slotSize)) r, s2)
Nothing -> let
(offset, s2) = packValue size s
in (offset, UnionPackingState (Just (offset, size)) r, s2)
packUnionizedValue SizeReference u@(UnionPackingState _ (UnionSlot 0 _)) s = let
(PointerOffset offset, s2) = packValue SizeReference s
u2 = u { unionPointerSlot = UnionSlot 1 offset }
in (PointerOffset offset, u2, s2)
-- Pack reference when we already have a reference slot allocated.
packUnionizedValue SizeReference u@(UnionPackingState _ (UnionSlot _ offset)) s =
(PointerOffset offset, u, s)
-- Pack inline composite.
packUnionizedValue (SizeInlineComposite dataSize pointerCount)
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
-> FieldSize -- desired field size
-> Maybe (Integer, -- Offset of the new field (in multiples of field size).
Integer, -- New offset of the slot (in multiples of slot size).
FieldSize, -- New size of the slot.
-> DataSize -- desired field size
-> Maybe (Integer, -- Offset of the new field.
(DataSize, Integer), -- New offset of the slot.
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.
packUnionizedData (slotOffset, slotSize) state desiredSize
| sizeInBits slotSize >= sizeInBits desiredSize =
Just (div (sizeInBits slotSize) (sizeInBits desiredSize) * slotOffset,
slotOffset, slotSize, state)
-- If slot is a bit, and it is the first bit in its byte, and the bit hole immediately follows
-- expand it to a byte.
packUnionizedData (slotOffset, Size1) p@(PackingState { packingHole1 = hole }) desiredSize
| mod slotOffset 8 == 0 && hole == slotOffset + 1 =
packUnionizedData (div slotOffset 8, Size8) (p { packingHole1 = 0 }) desiredSize
-- If slot is size N, and the next N bits are padding, expand.
packUnionizedData (slotOffset, Size8) p@(PackingState { packingHole8 = hole }) desiredSize
| hole == slotOffset + 1 =
packUnionizedData (div slotOffset 2, Size16) (p { packingHole8 = 0 }) desiredSize
packUnionizedData (slotOffset, Size16) p@(PackingState { packingHole16 = hole }) desiredSize
| hole == slotOffset + 1 =
packUnionizedData (div slotOffset 2, Size32) (p { packingHole16 = 0 }) desiredSize
packUnionizedData (slotOffset, Size32) p@(PackingState { packingHole32 = hole }) desiredSize
| hole == slotOffset + 1 =
packUnionizedData (div slotOffset 2, Size64) (p { packingHole32 = 0 }) desiredSize
-- Otherwise, we fail.
packUnionizedData _ _ _ = Nothing
tryExpandSubWordDataSlot (slotSize, slotOffset) state desiredSize
| dataSizeInBits slotSize >= dataSizeInBits desiredSize =
Just (div (dataSizeInBits slotSize) (dataSizeInBits desiredSize) * slotOffset,
(slotSize, slotOffset), state)
-- Try expanding the slot by combining it with subsequent padding.
tryExpandSubWordDataSlot (slotSize, slotOffset) state desiredSize = let
nextSize = succ slotSize
ratio = div (dataSizeInBits nextSize) (dataSizeInBits slotSize)
isAligned = mod slotOffset ratio == 0
nextOffset = div slotOffset ratio
deleteHole _ _ = Nothing
(maybeHole, newHoles) = Map.updateLookupWithKey deleteHole slotSize $ packingHoles state
newState = state { packingHoles = newHoles }
in if not isAligned
then Nothing -- Existing slot is not aligned properly.
else case maybeHole of
Just holeOffset | holeOffset == slotOffset + 1 ->
tryExpandSubWordDataSlot (nextSize, nextOffset) newState desiredSize
_ -> Nothing
-- Determine the offset for the given field, and update the packing states to include the field.
packField :: FieldDesc -> PackingState -> Map.Map Integer UnionPackingState
-> (Integer, PackingState, Map.Map Integer UnionPackingState)
-> (FieldOffset, PackingState, Map.Map Integer UnionPackingState)
packField fieldDesc state unionState =
case fieldUnion fieldDesc of
Nothing -> let
......@@ -511,13 +646,19 @@ packField fieldDesc state unionState =
-- 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.
packUnion :: UnionDesc -> PackingState -> Map.Map Integer UnionPackingState
-> (Integer, PackingState, Map.Map Integer UnionPackingState)
packUnion _ state unionState = (offset, newState, unionState) where
(offset, newState) = packValue Size16 state
packFields :: [FieldDesc] -> [UnionDesc]
-> (PackingState, Map.Map Integer UnionPackingState, Map.Map Integer (Integer, PackingState))
packFields fields unions = (finalState, finalUnionState, Map.fromList packedItems) where
-> (FieldOffset, PackingState, Map.Map Integer UnionPackingState)
packUnion _ state unionState = (DataOffset Size16 offset, newState, unionState) where
(offset, newState) = packData Size16 state
stripHolesFromFirstWord Size1 _ = Size1 -- Nothing left to strip.
stripHolesFromFirstWord size holes = let
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 (
[(fieldNumber d, packField d) | d <- fields]:
[(unionNumber d, packUnion d):[(fieldNumber d2, packField d2) | d2 <- unionFields d]
......@@ -526,13 +667,45 @@ packFields fields unions = (finalState, finalUnionState, Map.fromList packedItem
itemsByNumber = List.sortBy compareNumbers items
compareNumbers (a, _) (b, _) = compare a b
(finalState, finalUnionState, packedItems) =
(finalState, _, packedItems) =
foldl packItem (initialPackingState, Map.empty, []) itemsByNumber
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
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)
......@@ -604,7 +777,7 @@ compileDecl scope@(DescEnum parent)
compileDecl _ (EnumerantDecl (Located pos name) _ _) =
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
(members, memberMap) <- compileChildDecls desc decls
requireNoDuplicateNames decls
......@@ -612,15 +785,19 @@ compileDecl scope (StructDecl (Located _ name) annotations decls) =
requireSequentialNumbering "Fields" fieldNums
requireOrdinalsInRange fieldNums
(theId, compiledAnnotations) <- compileAnnotations scope StructAnnotation annotations
return (let
let (dataSize, pointerCount, fieldPackingMap) = packFields fields unions
fields = [d | DescField 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
{ structName = name
, structId = theId
, structParent = scope
, structPacking = packing
, structDataSize = finalDataSize
, structPointerCount = finalPointerCount
, structIsFixedWidth = isJust isFixed
, structFields = fields
, structUnions = unions
, structAnnotations = compiledAnnotations
......@@ -639,14 +816,13 @@ compileDecl scope@(DescStruct parent)
requireNoMoreThanOneFieldNumberLessThan name numPos number fields
(theId, compiledAnnotations) <- compileAnnotations scope UnionAnnotation annotations
return (let
(tagOffset, tagPacking) = structFieldPackingMap parent ! number
DataOffset Size16 tagOffset = structFieldPackingMap parent ! number
in DescUnion UnionDesc
{ unionName = name
, unionId = theId
, unionParent = parent
, unionNumber = number
, unionTagOffset = tagOffset
, unionTagPacking = tagPacking
, unionFields = fields
, unionAnnotations = compiledAnnotations
, unionMemberMap = memberMap
......@@ -668,18 +844,22 @@ compileDecl scope
_ -> Nothing
typeDesc <- compileType scope typeExp
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
(theId, compiledAnnotations) <- compileAnnotations scope FieldAnnotation annotations
return (let
(offset, packing) = structFieldPackingMap parent ! number
in DescField FieldDesc
{ fieldName = name
, fieldId = theId
, fieldParent = parent
, fieldNumber = number
, fieldOffset = offset
, fieldPacking = packing
, fieldOffset = structFieldPackingMap parent ! number
, fieldUnion = unionDesc
, fieldType = typeDesc
, fieldDefaultValue = defaultDesc
......
......@@ -88,6 +88,7 @@ hashString str =
isPrimitive t@(BuiltinType _) = not $ isBlob t
isPrimitive (EnumType _) = True
isPrimitive (StructType _) = False
isPrimitive (InlineStructType _) = False
isPrimitive (InterfaceType _) = False
isPrimitive (ListType _) = False
......@@ -96,8 +97,12 @@ isBlob (BuiltinType BuiltinData) = True
isBlob _ = False
isStruct (StructType _) = True
isStruct (InlineStructType _) = True
isStruct _ = False
isInlineStruct (InlineStructType _) = True
isInlineStruct _ = False
isList (ListType _) = True
isList _ = False
......@@ -130,18 +135,30 @@ cxxTypeString (BuiltinType BuiltinText) = " ::capnproto::Text"
cxxTypeString (BuiltinType BuiltinData) = " ::capnproto::Data"
cxxTypeString (EnumType desc) = globalName $ DescEnum desc
cxxTypeString (StructType desc) = globalName $ DescStruct desc
cxxTypeString (InlineStructType desc) = globalName $ DescStruct desc
cxxTypeString (InterfaceType desc) = globalName $ DescInterface desc
cxxTypeString (ListType t) = concat [" ::capnproto::List<", cxxTypeString t, ">"]
cxxFieldSizeString Size0 = "VOID";
cxxFieldSizeString Size1 = "BIT";
cxxFieldSizeString Size8 = "BYTE";
cxxFieldSizeString Size16 = "TWO_BYTES";
cxxFieldSizeString Size32 = "FOUR_BYTES";
cxxFieldSizeString Size64 = "EIGHT_BYTES";
cxxFieldSizeString SizeVoid = "VOID";
cxxFieldSizeString (SizeData Size1) = "BIT";
cxxFieldSizeString (SizeData Size8) = "BYTE";
cxxFieldSizeString (SizeData Size16) = "TWO_BYTES";
cxxFieldSizeString (SizeData Size32) = "FOUR_BYTES";
cxxFieldSizeString (SizeData Size64) = "EIGHT_BYTES";
cxxFieldSizeString SizeReference = "REFERENCE";
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 (BoolDesc b) = not b
isDefaultZero (Int8Desc i) = i == 0
......@@ -221,6 +238,7 @@ fieldContext parent desc = mkStrContext context where
context "fieldIsPrimitive" = MuBool $ isPrimitive $ fieldType desc
context "fieldIsBlob" = MuBool $ isBlob $ fieldType desc
context "fieldIsStruct" = MuBool $ isStruct $ fieldType desc
context "fieldIsInlineStruct" = MuBool $ isInlineStruct $ fieldType desc
context "fieldIsList" = MuBool $ isList $ fieldType desc
context "fieldIsNonStructList" = MuBool $ isNonStructList $ fieldType desc
context "fieldIsPrimitiveList" = MuBool $ isPrimitiveList $ fieldType desc
......@@ -231,7 +249,7 @@ fieldContext parent desc = mkStrContext context where
Nothing -> muNull
context "fieldType" = MuVariable $ cxxTypeString $ 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
Nothing -> MuVariable ""
Just v -> MuVariable (if isDefaultZero v then "" else ", " ++ defaultMask v)
......@@ -283,8 +301,8 @@ structContext parent desc = mkStrContext context where
context "structFullName" = MuVariable $ fullName (DescStruct desc)
context "structFields" = MuList $ map (fieldContext context) $ structFields desc
context "structUnions" = MuList $ map (unionContext context) $ structUnions desc
context "structDataSize" = MuVariable $ packingDataSize $ structPacking desc
context "structReferenceCount" = MuVariable $ packingReferenceCount $ structPacking desc
context "structDataSize" = MuVariable $ dataSectionWordSize $ structDataSize desc
context "structReferenceCount" = MuVariable $ structPointerCount desc
context "structNestedEnums" =
MuList $ map (enumContext context) [m | DescEnum m <- structMembers desc]
context "structNestedStructs" =
......
......@@ -95,7 +95,8 @@ data Declaration = UsingDecl (Located String) DeclName
| ConstantDecl (Located String) TypeExpression [Annotation] (Located FieldValue)
| EnumDecl (Located String) [Annotation] [Declaration]
| 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)
TypeExpression [Annotation] (Maybe (Located FieldValue))
| UnionDecl (Located String) (Located Integer) [Annotation] [Declaration]
......@@ -110,7 +111,7 @@ declarationName (UsingDecl n _) = Just n
declarationName (ConstantDecl n _ _ _) = Just n
declarationName (EnumDecl n _ _) = Just n
declarationName (EnumerantDecl n _ _) = Just n
declarationName (StructDecl n _ _) = Just n
declarationName (StructDecl n _ _ _) = Just n
declarationName (FieldDecl n _ _ _ _) = Just n
declarationName (UnionDecl n _ _ _) = Just n
declarationName (InterfaceDecl n _ _) = Just n
......@@ -122,7 +123,7 @@ declImports (UsingDecl _ name) = maybeToList (declNameImport name)
declImports (ConstantDecl _ t ann _) = typeImports t ++ concatMap annotationImports ann
declImports (EnumDecl _ ann decls) = concatMap annotationImports ann ++ concatMap declImports decls
declImports (EnumerantDecl _ _ ann) = concatMap annotationImports ann
declImports (StructDecl _ ann decls) = concatMap annotationImports ann ++
declImports (StructDecl _ _ ann decls) = concatMap annotationImports ann ++
concatMap declImports decls
declImports (FieldDecl _ _ t ann _) = typeImports t ++ concatMap annotationImports ann
declImports (UnionDecl _ _ ann decls) = concatMap annotationImports ann ++
......
......@@ -49,6 +49,7 @@ keywords =
, (UnionKeyword, "union")
, (InterfaceKeyword, "interface")
, (AnnotationKeyword, "annotation")
, (FixedKeyword, "fixed")
]
languageDef :: T.LanguageDef st
......
......@@ -24,6 +24,7 @@
module Parser (parseFile) where
import Data.Generics
import Data.Maybe(fromMaybe)
import Text.Parsec hiding (tokens)
import Token
import Grammar
......@@ -65,6 +66,7 @@ tokenErrorString StructKeyword = "keyword \"struct\""
tokenErrorString UnionKeyword = "keyword \"union\""
tokenErrorString InterfaceKeyword = "keyword \"interface\""
tokenErrorString AnnotationKeyword = "keyword \"annotation\""
tokenErrorString FixedKeyword = "keyword \"fixed\""
type TokenParser = Parsec [Located Token] [ParseError]
......@@ -120,6 +122,7 @@ structKeyword = tokenParser (matchSimpleToken StructKeyword) <?> "\"struct\""
unionKeyword = tokenParser (matchSimpleToken UnionKeyword) <?> "\"union\""
interfaceKeyword = tokenParser (matchSimpleToken InterfaceKeyword) <?> "\"interface\""
annotationKeyword = tokenParser (matchSimpleToken AnnotationKeyword) <?> "\"annotation\""
fixedKeyword = tokenParser (matchSimpleToken FixedKeyword) <?> "\"fixed\""
exactIdentifier s = tokenParser (matchSimpleToken $ Identifier s) <?> "\"" ++ s ++ "\""
......@@ -223,9 +226,39 @@ enumerantDecl = do
structDecl statements = do
structKeyword
name <- located typeIdentifier
fixed <- optionMaybe fixedSpec
annotations <- many annotation
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 Nothing = usingDecl <|> constantDecl <|> fieldDecl <|> annotationDecl
......
......@@ -55,6 +55,7 @@ data Desc = DescFile FileDesc
| DescAnnotation AnnotationDesc
| DescBuiltinType BuiltinType
| DescBuiltinList
| DescBuiltinInline
| DescBuiltinId
descName (DescFile _) = "(top-level)"
......@@ -71,6 +72,7 @@ descName (DescParam d) = paramName d
descName (DescAnnotation d) = annotationName d
descName (DescBuiltinType d) = builtinTypeName d
descName DescBuiltinList = "List"
descName DescBuiltinInline = "Inline"
descName DescBuiltinId = "id"
descId (DescFile d) = fileId d
......@@ -87,6 +89,7 @@ descId (DescParam d) = paramId d
descId (DescAnnotation d) = annotationId d
descId (DescBuiltinType _) = Nothing
descId DescBuiltinList = Nothing
descId DescBuiltinInline = Nothing
descId DescBuiltinId = Just "0U0T3e_SnatEfk6UcH2tcjTt1E0"
-- 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)
descParent (DescAnnotation d) = annotationParent d
descParent (DescBuiltinType _) = 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."
descFile (DescFile d) = d
......@@ -130,9 +134,10 @@ descAnnotations (DescParam d) = paramAnnotations d
descAnnotations (DescAnnotation d) = annotationAnnotations d
descAnnotations (DescBuiltinType _) = Map.empty
descAnnotations DescBuiltinList = Map.empty
descAnnotations DescBuiltinInline = 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 (DescConstant d) = constantRuntimeImports d
descRuntimeImports (DescEnum d) = enumRuntimeImports d
......@@ -146,6 +151,7 @@ descRuntimeImports (DescParam d) = paramRuntimeImports d
descRuntimeImports (DescAnnotation d) = annotationRuntimeImports d
descRuntimeImports (DescBuiltinType _) = []
descRuntimeImports DescBuiltinList = []
descRuntimeImports DescBuiltinInline = []
descRuntimeImports DescBuiltinId = []
type MemberMap = Map.Map String (Maybe Desc)
......@@ -212,101 +218,118 @@ valueString (ListDesc l) = "[" ++ delimit ", " (map valueString l) ++ "]" where
data TypeDesc = BuiltinType BuiltinType
| EnumType EnumDesc
| StructType StructDesc
| InlineStructType StructDesc
| InterfaceType InterfaceDesc
| ListType TypeDesc
typeRuntimeImports (BuiltinType _) = []
typeRuntimeImports (EnumType d) = [descFile (DescEnum d)]
typeRuntimeImports (StructType d) = [descFile (DescStruct d)]
typeRuntimeImports (InlineStructType d) = [descFile (DescStruct d)]
typeRuntimeImports (InterfaceType d) = [descFile (DescInterface d)]
typeRuntimeImports (ListType d) = typeRuntimeImports d
data PackingState = PackingState
{ packingHole1 :: Integer
, packingHole8 :: Integer
, packingHole16 :: Integer
, packingHole32 :: Integer
, packingDataSize :: Integer
, packingReferenceCount :: Integer
data DataSectionSize = DataSection1 | DataSection8 | DataSection16 | DataSection32
| DataSectionWords Integer
dataSectionWordSize ds = case ds of
DataSectionWords w -> w
_ -> 1
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
-- Represents the current packing state of a union. The parameters are:
-- - The offset of a 64-bit word in the data segment allocated to the union.
-- - The offset of a reference allocated to the union.
-- - 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,
-- this is the piece that had been allocated to that field, and is now retroactively part of the
-- union.
data UnionPackingState = UnionPackingState
{ unionPackDataOffset :: Maybe (Integer, FieldSize)
, unionPackReferenceOffset :: Maybe Integer
}
data FieldSize = Size0 | Size1 | Size8 | Size16 | Size32 | Size64 | SizeReference
| SizeInlineComposite Integer Integer
isDataFieldSize SizeReference = False
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
offsetToSize :: FieldOffset -> FieldSize
offsetToSize VoidOffset = SizeVoid
offsetToSize (DataOffset s _) = SizeData s
offsetToSize (PointerOffset _) = SizeReference
offsetToSize (InlineCompositeOffset _ _ d p) = SizeInlineComposite d p
fieldSize (BuiltinType BuiltinVoid) = SizeVoid
fieldSize (BuiltinType BuiltinBool) = SizeData Size1
fieldSize (BuiltinType BuiltinInt8) = SizeData Size8
fieldSize (BuiltinType BuiltinInt16) = SizeData Size16
fieldSize (BuiltinType BuiltinInt32) = SizeData Size32
fieldSize (BuiltinType BuiltinInt64) = SizeData Size64
fieldSize (BuiltinType BuiltinUInt8) = SizeData Size8
fieldSize (BuiltinType BuiltinUInt16) = SizeData Size16
fieldSize (BuiltinType BuiltinUInt32) = SizeData Size32
fieldSize (BuiltinType BuiltinUInt64) = SizeData Size64
fieldSize (BuiltinType BuiltinFloat32) = SizeData Size32
fieldSize (BuiltinType BuiltinFloat64) = SizeData Size64
fieldSize (BuiltinType BuiltinText) = SizeReference
fieldSize (BuiltinType BuiltinData) = SizeReference
fieldSize (EnumType _) = Size16 -- TODO: ??
fieldSize (EnumType _) = SizeData Size16
fieldSize (StructType _) = SizeReference
fieldSize (InlineStructType StructDesc { structDataSize = ds, structPointerCount = ps }) =
SizeInlineComposite ds ps
fieldSize (InterfaceType _) = SizeReference
fieldSize (ListType _) = SizeReference
fieldValueSize VoidDesc = Size0
fieldValueSize (BoolDesc _) = Size1
fieldValueSize (Int8Desc _) = Size8
fieldValueSize (Int16Desc _) = Size16
fieldValueSize (Int32Desc _) = Size32
fieldValueSize (Int64Desc _) = Size64
fieldValueSize (UInt8Desc _) = Size8
fieldValueSize (UInt16Desc _) = Size16
fieldValueSize (UInt32Desc _) = Size32
fieldValueSize (UInt64Desc _) = Size64
fieldValueSize (Float32Desc _) = Size32
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 (StructType StructDesc { structDataSize = DataSection1, structPointerCount = 0 }) =
SizeData Size1
elementSize (StructType StructDesc { structDataSize = DataSection8, structPointerCount = 0 }) =
SizeData Size8
elementSize (StructType StructDesc { structDataSize = DataSection16, structPointerCount = 0 }) =
SizeData Size16
elementSize (StructType StructDesc { structDataSize = DataSection32, structPointerCount = 0 }) =
SizeData Size32
elementSize (StructType StructDesc { structDataSize = ds, structPointerCount = pc }) =
SizeInlineComposite ds pc
elementSize (InlineStructType s) = elementSize (StructType s)
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.
typeName :: Desc -> TypeDesc -> String
typeName _ (BuiltinType t) = builtinTypeName t -- TODO: Check for shadowing.
typeName scope (EnumType desc) = descQualifiedName scope (DescEnum 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 (ListType t) = "List(" ++ typeName scope t ++ ")"
......@@ -386,7 +409,9 @@ data StructDesc = StructDesc
{ structName :: String
, structId :: Maybe String
, structParent :: Desc
, structPacking :: PackingState
, structDataSize :: DataSectionSize
, structPointerCount :: Integer
, structIsFixedWidth :: Bool
, structFields :: [FieldDesc]
, structUnions :: [UnionDesc]
, structAnnotations :: AnnotationMap
......@@ -396,7 +421,7 @@ data StructDesc = StructDesc
-- 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
-- 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
......@@ -407,7 +432,6 @@ data UnionDesc = UnionDesc
, unionParent :: StructDesc
, unionNumber :: Integer
, unionTagOffset :: Integer
, unionTagPacking :: PackingState
, unionFields :: [FieldDesc]
, unionAnnotations :: AnnotationMap
, unionMemberMap :: MemberMap
......@@ -424,8 +448,7 @@ data FieldDesc = FieldDesc
, fieldId :: Maybe String
, fieldParent :: StructDesc
, fieldNumber :: Integer
, fieldOffset :: Integer
, fieldPacking :: PackingState -- PackingState for the struct *if* this were the final field.
, fieldOffset :: FieldOffset
, fieldUnion :: Maybe (UnionDesc, Integer) -- Integer is value of union discriminant.
, fieldType :: TypeDesc
, fieldDefaultValue :: Maybe ValueDesc
......@@ -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
(enumerantName desc) (enumerantNumber desc)
(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)
(if structIsFixedWidth desc
then printf " fixed(%s, %d pointers) "
(dataSectionSizeString $ structDataSize desc)
(structPointerCount desc)
else "")
(annotationsCode self)
(blockCode indent (structMembers desc))
indent
......@@ -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))
(case fieldDefaultValue desc of { Nothing -> ""; Just v -> " = " ++ valueString v; })
(annotationsCode self)
(case fieldSize $ fieldType desc of
SizeReference -> printf "ref[%d]" $ fieldOffset desc
SizeInlineComposite _ _ -> "??"
s -> let
bits = sizeInBits s
offset = fieldOffset desc
(case fieldOffset desc of
PointerOffset o -> printf "ptr[%d]" o
InlineCompositeOffset dataOffset pointerOffset dataSize pointerSize ->
let dataBitOffset = dataOffset * dataSizeInBits (dataSectionAlignment dataSize)
in printf "bits[%d, %d), ptrs[%d, %d)"
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))
descToCode indent self@(DescUnion desc) = printf "%sunion %s@%d%s { # [%d, %d)\n%s%s}\n" indent
(unionName desc) (unionNumber desc)
......@@ -556,6 +588,7 @@ descToCode indent self@(DescAnnotation desc) = printf "%sannotation %s: %s on(%s
(annotationsCode self)
descToCode _ (DescBuiltinType _) = 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."
maybeBlockCode :: String -> [Desc] -> String
......
......@@ -74,6 +74,7 @@ data Token = Identifier String
| UnionKeyword
| InterfaceKeyword
| AnnotationKeyword
| FixedKeyword
deriving (Data, Typeable, Show, Eq)
data Statement = Line TokenSequence
......
......@@ -26,11 +26,11 @@ module WireFormat(encodeMessage) where
import Data.List(sortBy, genericLength, genericReplicate)
import Data.Word
import Data.Bits(shiftL, shiftR, Bits, setBit, xor)
import Data.Function(on)
import Semantics
import Data.Binary.IEEE754(floatToWord, doubleToWord)
import qualified Codec.Binary.UTF8.String as UTF8
--
byte :: (Integral a, Bits a) => a -> Int -> Word8
byte i amount = fromIntegral (shiftR i (amount * 8))
......@@ -43,119 +43,112 @@ padToWord b = let
then b
else b ++ replicate (8 - trailing) 0
roundUpToMultiple factor n = let
remainder = mod n factor
in if remainder == 0
then n
else n + (factor - remainder)
encodeDataValue :: ValueDesc -> [Word8]
encodeDataValue VoidDesc = []
encodeDataValue (BoolDesc _) = error "Bools must be handled specially."
encodeDataValue (Int8Desc v) = bytes v 1
encodeDataValue (Int16Desc v) = bytes v 2
encodeDataValue (Int32Desc v) = bytes v 4
encodeDataValue (Int64Desc v) = bytes v 8
encodeDataValue (UInt8Desc v) = bytes v 1
encodeDataValue (UInt16Desc v) = bytes v 2
encodeDataValue (UInt32Desc v) = bytes v 4
encodeDataValue (UInt64Desc v) = bytes v 8
encodeDataValue (Float32Desc v) = bytes (floatToWord v) 4
encodeDataValue (Float64Desc v) = bytes (doubleToWord v) 8
encodeDataValue (TextDesc _) = error "Not fixed-width data."
encodeDataValue (DataDesc _) = error "Not fixed-width data."
encodeDataValue (EnumerantValueDesc v) = bytes (enumerantNumber v) 2
encodeDataValue (StructValueDesc _) = error "Not fixed-width data."
encodeDataValue (ListDesc _) = error "Not fixed-width data."
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
data EncodedData = EncodedBit Bool
| EncodedBytes [Word8]
xorData (EncodedBit a) (EncodedBit b) = EncodedBit (a /= b)
xorData (EncodedBytes a) (EncodedBytes b) = EncodedBytes (zipWith xor a b)
xorData _ _ = error "Value type mismatch when xor'ing."
encodeDataValue :: TypeDesc -> ValueDesc -> EncodedData
encodeDataValue _ VoidDesc = EncodedBytes []
encodeDataValue _ (BoolDesc v) = EncodedBit v
encodeDataValue _ (Int8Desc v) = EncodedBytes $ bytes v 1
encodeDataValue _ (Int16Desc v) = EncodedBytes $ bytes v 2
encodeDataValue _ (Int32Desc v) = EncodedBytes $ bytes v 4
encodeDataValue _ (Int64Desc v) = EncodedBytes $ bytes v 8
encodeDataValue _ (UInt8Desc v) = EncodedBytes $ bytes v 1
encodeDataValue _ (UInt16Desc v) = EncodedBytes $ bytes v 2
encodeDataValue _ (UInt32Desc v) = EncodedBytes $ bytes v 4
encodeDataValue _ (UInt64Desc v) = EncodedBytes $ bytes v 8
encodeDataValue _ (Float32Desc v) = EncodedBytes $ bytes (floatToWord v) 4
encodeDataValue _ (Float64Desc v) = EncodedBytes $ bytes (doubleToWord v) 8
encodeDataValue _ (TextDesc _) = error "Not fixed-width data."
encodeDataValue _ (DataDesc _) = error "Not fixed-width data."
encodeDataValue _ (EnumerantValueDesc v) = EncodedBytes $ bytes (enumerantNumber v) 2
encodeDataValue (StructType desc) (StructValueDesc assignments) = let
(dataBytes, refBytes, childBytes) = encodeStruct desc assignments 0
in (encodeStructReference desc offset, concat [dataBytes, refBytes, childBytes])
(ListType elementType, ListDesc items) ->
(encodeListReference (elementSize elementType) (genericLength items) offset,
in if null refBytes && null childBytes
then EncodedBytes dataBytes
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)
(BuiltinType BuiltinText, TextDesc text) -> let
encoded = (UTF8.encode text ++ [0])
in (encodeListReference Size8 (genericLength encoded) offset, padToWord encoded)
(BuiltinType BuiltinData, DataDesc d) -> let
in (encodeListReference Size8 (genericLength d) offset, padToWord d)
_ -> error "Unknown reference type."
len = genericLength obj
wordLen = if mod len 8 == 0 then div len 8 else error "Child not word-aligned."
(refs, objects) = loop (idx + 1) (offset + wordLen - 1) rest
in (ref ++ refs, obj ++ objects)
loop idx offset rest@((pos, _, _):_) = let
encodePointerValue _ _ = error "Unknown pointer type."
-- Given a sorted list of (bitOffset, data), pack into a byte array.
packBytes :: Integer -- Total size of array to pack, in bits.
-> [(Integer, EncodedData)] -- (offset, data) pairs to pack. Must be in order.
-> [Word8]
packBytes size = loop 0 where
loop :: Integer -> [(Integer, EncodedData)] -> [Word8]
loop bit [] | bit <= size = genericReplicate (div (size - bit + 7) 8) 0
loop bit [] | bit > size = error "Data values overran size."
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
(refs, objects) = loop pos (offset - padCount) rest
in (genericReplicate (padCount * 8) 0 ++ refs, objects)
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)
(restPtrs, restChildren) = loop pos rest (childOff - padCount)
in (genericReplicate (padCount * 8) 0 ++ restPtrs, restChildren)
loop idx [] _ = (genericReplicate ((size - idx) * 8) 0, [])
encodeStructReference desc offset =
bytes (offset * 4 + structTag) 4 ++
bytes (packingDataSize $ structPacking desc) 2 ++
bytes (packingReferenceCount $ structPacking desc) 2
bytes (dataSectionWordSize $ structDataSize desc) 2 ++
bytes (structPointerCount desc) 2
encodeListReference elemSize@(SizeInlineComposite ds rc) elementCount offset =
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 =
bytes (offset * 4 + listTag) 4 ++
bytes (fieldSizeEnum elemSize + shiftL elementCount 3) 4
fieldSizeEnum Size0 = 0
fieldSizeEnum Size1 = 1
fieldSizeEnum Size8 = 2
fieldSizeEnum Size16 = 3
fieldSizeEnum Size32 = 4
fieldSizeEnum Size64 = 5
fieldSizeEnum SizeVoid = 0
fieldSizeEnum (SizeData Size1) = 1
fieldSizeEnum (SizeData Size8) = 2
fieldSizeEnum (SizeData Size16) = 3
fieldSizeEnum (SizeData Size32) = 4
fieldSizeEnum (SizeData Size64) = 5
fieldSizeEnum SizeReference = 6
fieldSizeEnum (SizeInlineComposite _ _) = 7
......@@ -164,47 +157,94 @@ listTag = 1
-- childOffset = number of words between the last reference and the location where children will
-- be allocated.
encodeStruct desc assignments childOffset = (dataBytes, referenceBytes, children) where
-- Values explicitly assigned.
explicitValues = [(fieldOffset f, fieldType f, v, fieldDefaultValue f) | (f, v) <- assignments]
-- Values of union tags.
unionValues = [(unionTagOffset u, BuiltinType BuiltinUInt16, UInt16Desc $ fromIntegral n,
Nothing)
encodeStruct desc assignments childOffset = let
dataSize = dataSectionBits $ structDataSize desc
dataSection = packBytes dataSize $ sortBy (compare `on` fst)
$ structDataSectionValues assignments
pointerCount = structPointerCount desc
(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]
allValues = explicitValues ++ unionValues
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 ]
in simpleValues ++ inlineStructValues ++ unionTags
sortedData = sortBy compareDataValues allData
compareDataValues (o1, _, _, _) (o2, _, _, _) = compare o1 o2
sortedReferences = sortBy compareReferenceValues allReferences
compareReferenceValues (o1, _, _) (o2, _, _) = compare o1 o2
structPointerSectionValues :: [(FieldDesc, ValueDesc)] -> [(Integer, (Integer -> [Word8], [Word8]))]
structPointerSectionValues assignments = let
simpleValues = [(off, encodePointerValue (fieldType f) v)
| (f@FieldDesc { fieldOffset = PointerOffset off }, v) <- assignments]
dataBytes = encodeData (packingDataSize (structPacking desc) * 64) sortedData
(referenceBytes, children) = encodeReferences childOffset
(packingReferenceCount $ structPacking desc) sortedReferences
inlineStructValues = do -- List monad!
(FieldDesc { fieldOffset = InlineCompositeOffset _ off _ _ },
StructValueDesc v) <- assignments
(pos, v2) <- structPointerSectionValues v
return (pos + off, v2)
in simpleValues ++ inlineStructValues
encodeList elementType elements = case elementSize elementType of
SizeInlineComposite _ _ -> case elementType of
StructType desc -> let
SizeVoid -> []
SizeInlineComposite _ _ -> let
handleStructType desc = let
count = genericLength elements
tag = encodeStructReference desc count
(elemBytes, childBytes) = encodeStructList 0 desc [v | StructValueDesc v <- elements]
in concat [tag, elemBytes, childBytes]
in case elementType of
StructType desc -> handleStructType desc
InlineStructType desc -> handleStructType desc
_ -> error "Only structs can be inline composites."
SizeReference -> refBytes ++ childBytes where
(refBytes, childBytes) = encodeReferences 0 (genericLength elements)
$ zipWith (\i v -> (i, elementType, v)) [0..] elements
size -> encodeData (roundUpToMultiple 64 (genericLength elements * sizeInBits size))
$ zipWith (\i v -> (i * sizeInBits size, elementType, v, Nothing)) [0..] elements
encodedElements = zip [0..] $ map (encodePointerValue elementType) elements
(refBytes, childBytes) = packPointers (genericLength elements) encodedElements 0
SizeData size -> let
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
(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) =
encodeListReference (elementSize elementType) (genericLength elements) (0::Integer) ++
encodeList elementType elements
......
......@@ -295,6 +295,7 @@ inline {{fieldType}}::Builder {{typeFullName}}::Builder::init{{fieldTitleCase}}(
{{/fieldIsBlob}}
{{! ------------------------------------------------------------------------------------------- }}
{{#fieldIsStruct}}
{{^fieldIsInlineStruct}}
inline {{fieldType}}::Reader {{typeFullName}}::Reader::get{{fieldTitleCase}}() {
{{#fieldUnion}}
CAPNPROTO_INLINE_DPRECOND(which() == {{unionTitleCase}}::{{fieldUpperCase}},
......@@ -324,6 +325,34 @@ inline {{fieldType}}::Builder {{typeFullName}}::Builder::get{{fieldTitleCase}}()
{{#fieldDefaultBytes}}DEFAULT_{{fieldUpperCase}}.words{{/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}}
{{! ------------------------------------------------------------------------------------------- }}
{{#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