Commit 20b1ea55 authored by Kenton Varda's avatar Kenton Varda

Encoding test. Bug fixes. It works.

parent b4f80598
......@@ -42,7 +42,7 @@ TEST(Blob, Text) {
std::string str = "foo";
Text::Reader text = str;
EXPECT_STREQ("foo", text);
EXPECT_EQ("foo", text);
EXPECT_STREQ("foo", text.c_str());
EXPECT_STREQ("foo", text.data());
EXPECT_EQ(3u, text.size());
......@@ -51,14 +51,14 @@ TEST(Blob, Text) {
EXPECT_EQ("foo", str2);
Text::Reader text2 = "bar";
EXPECT_STREQ("bar", text2);
EXPECT_EQ("bar", text2);
char c[4] = "baz";
Text::Reader text3(c);
EXPECT_STREQ("baz", text3);
EXPECT_EQ("baz", text3);
Text::Builder builder(c, 3);
EXPECT_STREQ("baz", builder);
EXPECT_EQ("baz", builder);
EXPECT_EQ(Data::Reader("az"), builder.slice(1, 3));
}
......@@ -67,6 +67,7 @@ TEST(Blob, Data) {
std::string str = "foo";
Data::Reader data = str;
EXPECT_EQ("foo", data);
EXPECT_EQ(3u, data.size());
std::string str2 = data;
......
......@@ -98,7 +98,11 @@ class Text::Reader: public Data::Reader {
// Like Data::Reader, but points at NUL-terminated UTF-8 text. The NUL terminator is not counted
// in the size but must be present immediately after the last byte.
//
// TextReader can be implicitly converted to and from const char*. Additionally, it can be
// Text::Reader's interface contract is that its data MUST be NUL-terminated. The producer of
// the Text::Reader must guarantee this, so that the consumer need not check. The data SHOULD
// also be valid UTF-8, but this is NOT guaranteed -- the consumer must verify if it cares.
//
// Text::Reader can be implicitly converted to and from const char*. Additionally, it can be
// implicitly converted to any type that can be constructed from a (const char*, size) pair, as
// well as from any type which has c_str() and size() methods. Many types follow this pattern,
// such as std::string.
......@@ -197,6 +201,20 @@ private:
static char nulstr[1];
};
inline bool operator==(const char* a, Data::Reader b) { return Data::Reader(a) == b; }
inline bool operator==(const char* a, Data::Builder b) { return Data::Reader(a) == (Data::Reader)b; }
inline bool operator==(Data::Reader a, Data::Builder b) { return a == (Data::Reader)b; }
inline bool operator==(Data::Builder a, Data::Reader b) { return (Data::Reader)a == b; }
template <typename T>
T& operator<<(T& os, Data::Reader value) { return os.write(value.data(), value.size()); }
template <typename T>
T& operator<<(T& os, Data::Builder value) { return os.write(value.data(), value.size()); }
template <typename T>
T& operator<<(T& os, Text::Reader value) { return os.write(value.data(), value.size()); }
template <typename T>
T& operator<<(T& os, Text::Builder value) { return os.write(value.data(), value.size()); }
} // namespace capnproto
#endif // CAPNPROTO_BLOB_H_
......@@ -44,9 +44,9 @@ echo findProvider file:capnproto-compiler
read CAPNPC
if test "$CAPNPC" = ""; then
echo "error: couldn't find capnproto-compiler." >&2
exit 1
CAPNPC=capnpc
fi
# When exception stack traces are needed, add: +RTS -xc -RTS
LD_PRELOAD=$INTERCEPTOR DYLD_FORCE_FLAT_NAMESPACE= DYLD_INSERT_LIBRARIES=$INTERCEPTOR \
$CAPNPC "$INPUT" 3>&1 4<&0 >&2
This diff is collapsed.
......@@ -26,6 +26,7 @@
#include "wire-format.h"
#include "descriptor.h" // only for FieldSize; TODO: Eliminate this
#include <initializer_list>
namespace capnproto {
......@@ -40,7 +41,7 @@ namespace internal {
template <typename T> struct IsPrimitive { static constexpr bool value = false; };
template <> struct IsPrimitive<void> { static constexpr bool value = true; };
template <> struct IsPrimitive<Void> { static constexpr bool value = true; };
template <> struct IsPrimitive<bool> { static constexpr bool value = true; };
template <> struct IsPrimitive<int8_t> { static constexpr bool value = true; };
template <> struct IsPrimitive<int16_t> { static constexpr bool value = true; };
......@@ -58,7 +59,7 @@ template <typename T, bool b> struct IsPrimitive<List<T, b>> {
template <typename T> struct FieldSizeForType { static constexpr FieldSize value = FieldSize::INLINE_COMPOSITE; };
template <> struct FieldSizeForType<void> { static constexpr FieldSize value = FieldSize::VOID; };
template <> struct FieldSizeForType<Void> { static constexpr FieldSize value = FieldSize::VOID; };
template <> struct FieldSizeForType<bool> { static constexpr FieldSize value = FieldSize::BIT; };
template <> struct FieldSizeForType<int8_t> { static constexpr FieldSize value = FieldSize::BYTE; };
template <> struct FieldSizeForType<int16_t> { static constexpr FieldSize value = FieldSize::TWO_BYTES; };
......@@ -182,6 +183,23 @@ struct List<T, true> {
inline iterator begin() { return iterator(this, 0); }
inline iterator end() { return iterator(this, size()); }
template <typename Other>
void copyFrom(const Other& other) {
auto i = other.begin();
auto end = other.end();
uint pos = 0;
for (; i != end && pos < size(); ++i) {
set(pos, *i);
}
CAPNPROTO_DEBUG_ASSERT(pos == size() && i == end, "copyFrom() argument had different size.");
}
void copyFrom(std::initializer_list<T> other) {
CAPNPROTO_DEBUG_ASSERT(other.size() == size(), "copyFrom() argument had different size.");
for (uint i = 0; i < other.size(); i++) {
set(i, other.begin()[i]);
}
}
private:
internal::ListBuilder builder;
};
......@@ -223,6 +241,11 @@ struct List<T, false> {
inline iterator begin() { return iterator(this, 0); }
inline iterator end() { return iterator(this, size()); }
template <typename Other>
void copyFrom(const Other& other);
void copyFrom(std::initializer_list<typename T::Reader> other);
// TODO
private:
internal::ListBuilder builder;
};
......@@ -267,6 +290,11 @@ struct List<List<T>, true> {
inline iterator begin() { return iterator(this, 0); }
inline iterator end() { return iterator(this, size()); }
template <typename Other>
void copyFrom(const Other& other);
void copyFrom(std::initializer_list<typename List<T>::Reader> other);
// TODO
private:
internal::ListBuilder builder;
};
......@@ -311,6 +339,11 @@ struct List<List<T>, false> {
inline iterator begin() { return iterator(this, 0); }
inline iterator end() { return iterator(this, size()); }
template <typename Other>
void copyFrom(const Other& other);
void copyFrom(std::initializer_list<typename List<T>::Reader> other);
// TODO
private:
internal::ListBuilder builder;
};
......@@ -345,6 +378,9 @@ struct List<Data, false> {
inline Data::Builder operator[](uint index) {
return builder.getDataElement(index * REFERENCES);
}
inline void set(uint index, Data::Reader value) {
builder.setDataElement(index * REFERENCES, value);
}
inline Data::Builder init(uint index, uint size) {
return builder.initDataElement(index * REFERENCES, size * BYTES);
}
......@@ -353,6 +389,23 @@ struct List<Data, false> {
inline iterator begin() { return iterator(this, 0); }
inline iterator end() { return iterator(this, size()); }
template <typename Other>
void copyFrom(const Other& other) {
auto i = other.begin();
auto end = other.end();
uint pos = 0;
for (; i != end && pos < size(); ++i) {
set(pos, *i);
}
CAPNPROTO_DEBUG_ASSERT(pos == size() && i == end, "copyFrom() argument had different size.");
}
void copyFrom(std::initializer_list<Data::Reader> other) {
CAPNPROTO_DEBUG_ASSERT(other.size() == size(), "copyFrom() argument had different size.");
for (uint i = 0; i < other.size(); i++) {
set(i, other.begin()[i]);
}
}
private:
internal::ListBuilder builder;
};
......@@ -387,6 +440,9 @@ struct List<Text, false> {
inline Text::Builder operator[](uint index) {
return builder.getTextElement(index * REFERENCES);
}
inline void set(uint index, Text::Reader value) {
builder.setTextElement(index * REFERENCES, value);
}
inline Text::Builder init(uint index, uint size) {
return builder.initTextElement(index * REFERENCES, size * BYTES);
}
......@@ -395,6 +451,23 @@ struct List<Text, false> {
inline iterator begin() { return iterator(this, 0); }
inline iterator end() { return iterator(this, size()); }
template <typename Other>
void copyFrom(const Other& other) {
auto i = other.begin();
auto end = other.end();
uint pos = 0;
for (; i != end && pos < size(); ++i) {
set(pos, *i);
}
CAPNPROTO_DEBUG_ASSERT(pos == size() && i == end, "copyFrom() argument had different size.");
}
void copyFrom(std::initializer_list<Text::Reader> other) {
CAPNPROTO_DEBUG_ASSERT(other.size() == size(), "copyFrom() argument had different size.");
for (uint i = 0; i < other.size(); i++) {
set(i, other.begin()[i]);
}
}
private:
internal::ListBuilder builder;
};
......
......@@ -25,6 +25,8 @@
#include <memory>
#include "macros.h"
#include "type-safety.h"
#include "wire-format.h"
#include "list.h"
#ifndef CAPNPROTO_MESSAGE_H_
#define CAPNPROTO_MESSAGE_H_
......@@ -95,9 +97,24 @@ public:
// TODO: Methods to deal with bundled capabilities.
};
std::unique_ptr<MessageBuilder> newMallocMessage(WordCount preferredSegmentSize);
std::unique_ptr<MessageBuilder> newMallocMessage(WordCount preferredSegmentSize = 512 * WORDS);
// Returns a simple MessageBuilder implementation that uses standard allocation.
template <typename T>
struct MessageRoot {
std::unique_ptr<MessageBuilder> message;
typename T::Builder builder;
MessageRoot() = default;
MessageRoot(std::unique_ptr<MessageBuilder> message, typename T::Builder builder)
: message(move(message)), builder(builder) {}
};
template <typename T>
MessageRoot<T> newMallocMessageRoot(WordCount preferredSegmentSize = 512 * WORDS);
// Starts a new message with the given root type backed by a MallocMessage.
// T must be a Cap'n Proto struct type.
class ReadLimiter {
// Used to keep track of how much data has been processed from a message, and cut off further
// processing if and when a particular limit is reached. This is primarily intended to guard
......@@ -244,6 +261,18 @@ inline WordCount SegmentBuilder::available() {
return intervalLength(pos, end);
}
// -------------------------------------------------------------------
template <typename T>
MessageRoot<T> newMallocMessageRoot(WordCount preferredSegmentSize) {
std::unique_ptr<MessageBuilder> message = newMallocMessage(preferredSegmentSize);
SegmentBuilder* segment = message->getSegmentWithAvailable(1 * WORDS);
word* rootLocation = segment->allocate(1 * WORDS);
return MessageRoot<T>(move(message),
typename T::Builder(internal::StructBuilder::initRoot(
segment, rootLocation, T::DEFAULT.words)));
}
} // namespace capnproto
#endif // CAPNPROTO_MESSAGE_H_
# Copyright (c) 2013, Kenton Varda <temporal@gmail.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
struct TestAllTypes {
voidField @0 : Void;
boolField @1 : Bool;
int8Field @2 : Int8;
int16Field @3 : Int16;
int32Field @4 : Int32;
int64Field @5 : Int64;
uInt8Field @6 : UInt8;
uInt16Field @7 : UInt16;
uInt32Field @8 : UInt32;
uInt64Field @9 : UInt64;
float32Field @10 : Float32;
float64Field @11 : Float64;
textField @12 : Text;
dataField @13 : Data;
structField @14 : TestAllTypes;
enumField @15 : Void; # TODO
interfaceField @16 : Void; # TODO
voidList @17 : List(Void);
boolList @18 : List(Bool);
int8List @19 : List(Int8);
int16List @20 : List(Int16);
int32List @21 : List(Int32);
int64List @22 : List(Int64);
uInt8List @23 : List(UInt8);
uInt16List @24 : List(UInt16);
uInt32List @25 : List(UInt32);
uInt64List @26 : List(UInt64);
float32List @27 : List(Float32);
float64List @28 : List(Float64);
textList @29 : List(Text);
dataList @30 : List(Data);
structList @31 : List(TestAllTypes);
enumList @32 : Void; # TODO
interfaceList @33 : Void; # TODO
}
struct TestDefaults {
voidField @0 : Void = void;
boolField @1 : Bool = true;
int8Field @2 : Int8 = -123;
int16Field @3 : Int16 = -12345;
int32Field @4 : Int32 = -12345678;
int64Field @5 : Int64 = -123456789012345;
uInt8Field @6 : UInt8 = 234;
uInt16Field @7 : UInt16 = 45678;
uInt32Field @8 : UInt32 = 3456789012;
uInt64Field @9 : UInt64 = 12345678901234567890;
float32Field @10 : Float32 = 1234.5;
float64Field @11 : Float64 = -123e45;
textField @12 : Text = "foo";
dataField @13 : Data = "bar";
structField @14 : TestAllTypes = (
voidField = void,
boolField = true,
int8Field = -12,
int16Field = 3456,
int32Field = -78901234,
int64Field = 56789012345678,
uInt8Field = 90,
uInt16Field = 1234,
uInt32Field = 56789012,
uInt64Field = 345678901234567890,
float32Field = -1.25e-10,
float64Field = 345,
textField = "baz",
dataField = "qux",
structField = (
textField = "nested",
structField = (textField = "really nested")),
# enumField = TODO
# interfaceField can't have a default
voidList = [void, void, void],
boolList = [false, true, false, true, true],
int8List = [12, -34, -0x80, 0x7f],
int16List = [1234, -5678, -0x8000, 0x7fff],
int32List = [12345678, -90123456, -0x8000000, 0x7ffffff],
int64List = [123456789012345, -678901234567890, -0x8000000000000000, 0x7fffffffffffffff],
uInt8List = [12, 34, 0, 0xff],
uInt16List = [1234, 5678, 0, 0xffff],
uInt32List = [12345678, 90123456, 0, 0xffffffff],
uInt64List = [123456789012345, 678901234567890, 0, 0xffffffffffffffff],
float32List = [0, 1234567, 1e37, -1e37, 1e-37, -1e-37],
float64List = [0, 123456789012345, 1e306, -1e306, 1e-306, -1e-306],
textList = ["quux", "corge", "grault"],
dataList = ["garply", "waldo", "fred"],
structList = [
(textField = "x structlist 1"),
(textField = "x structlist 2"),
(textField = "x structlist 3")]
# enumList = TODO
# interfaceList can't have a default
);
enumField @15 : Void; # TODO
interfaceField @16 : Void; # TODO
voidList @17 : List(Void) = [void, void, void, void, void, void];
boolList @18 : List(Bool) = [true, false, false, true];
int8List @19 : List(Int8) = [111, -111];
int16List @20 : List(Int16) = [11111, -11111];
int32List @21 : List(Int32) = [111111111, -111111111];
int64List @22 : List(Int64) = [1111111111111111111, -1111111111111111111];
uInt8List @23 : List(UInt8) = [111, 222] ;
uInt16List @24 : List(UInt16) = [33333, 44444];
uInt32List @25 : List(UInt32) = [3333333333];
uInt64List @26 : List(UInt64) = [11111111111111111111];
float32List @27 : List(Float32) = [5555.5, 2222.25];
float64List @28 : List(Float64) = [7777.75, 1111.125];
textList @29 : List(Text) = ["plugh", "xyzzy", "thud"];
dataList @30 : List(Data) = ["oops", "exhausted", "rfc3092"];
structList @31 : List(TestAllTypes) = [
(textField = "structlist 1"),
(textField = "structlist 2"),
(textField = "structlist 3")];
enumList @32 : List(Void); # TODO
interfaceList @33 : List(Void); # TODO
}
......@@ -34,6 +34,14 @@ namespace capnproto {
typedef unsigned int uint;
enum class Void {
// Type used for Void fields. There is only one value. Using C++'s "void" type creates a bunch
// of issues since it behaves differently from other types.
VOID
};
template <typename T>
inline T& operator<<(T& os, Void) { return os << "void"; }
template <typename T>
struct NoInfer {
// Use NoInfer<T>::Type in place of T for a template function parameter to prevent inference of
......
......@@ -293,8 +293,7 @@ struct WireHelpers {
const WireReference* srcRefs = reinterpret_cast<const WireReference*>(src + dataSize);
WireReference* dstRefs = reinterpret_cast<WireReference*>(dst + dataSize);
uint n = referenceCount / REFERENCES;
for (uint i = 0; i < n; i++) {
for (uint i = 0; i < referenceCount / REFERENCES; i++) {
SegmentBuilder* subSegment = segment;
WireReference* dstRef = dstRefs + i;
copyMessage(subSegment, dstRef, srcRefs + i);
......@@ -313,11 +312,11 @@ struct WireHelpers {
const word* srcPtr = src->target();
word* dstPtr = allocate(dst, segment, src->structRef.wordSize(), WireReference::STRUCT);
copyStruct(segment, dstPtr, srcPtr, dst->structRef.dataSize.get(),
dst->structRef.refCount.get());
copyStruct(segment, dstPtr, srcPtr, src->structRef.dataSize.get(),
src->structRef.refCount.get());
dst->structRef.set(dst->structRef.fieldCount.get(), dst->structRef.dataSize.get(),
dst->structRef.refCount.get());
dst->structRef.set(src->structRef.fieldCount.get(), src->structRef.dataSize.get(),
src->structRef.refCount.get());
return dstPtr;
}
}
......@@ -418,7 +417,7 @@ struct WireHelpers {
reinterpret_cast<WireReference*>(ptr + defaultRef->structRef.dataSize.get()));
}
static CAPNPROTO_ALWAYS_INLINE(StructBuilder getStructReference(
static CAPNPROTO_ALWAYS_INLINE(StructBuilder getWritableStructReference(
WireReference* ref, SegmentBuilder* segment, const word* defaultValue)) {
const WireReference* defaultRef = reinterpret_cast<const WireReference*>(defaultValue);
word* ptr;
......@@ -631,7 +630,7 @@ struct WireHelpers {
ptr = followFars(ref, segment);
if (CAPNPROTO_EXPECT_FALSE(ptr == nullptr)) {
segment->getMessage()->reportInvalidData(
"Message contains out-of-bounds far reference.");
"Message contains invalid far reference.");
goto useDefault;
}
......@@ -681,7 +680,7 @@ struct WireHelpers {
ptr = followFars(ref, segment);
if (CAPNPROTO_EXPECT_FALSE(ptr == nullptr)) {
segment->getMessage()->reportInvalidData(
"Message contains out-of-bounds far reference.");
"Message contains invalid far reference.");
goto useDefault;
}
......@@ -861,7 +860,7 @@ struct WireHelpers {
if (CAPNPROTO_EXPECT_FALSE(ptr == nullptr)) {
segment->getMessage()->reportInvalidData(
"Message contains out-of-bounds far reference.");
"Message contains invalid far reference.");
goto useDefault;
}
......@@ -913,7 +912,7 @@ struct WireHelpers {
if (CAPNPROTO_EXPECT_FALSE(ptr == nullptr)) {
segment->getMessage()->reportInvalidData(
"Message contains out-of-bounds far reference.");
"Message contains invalid far reference.");
goto useDefault;
}
......@@ -923,7 +922,7 @@ struct WireHelpers {
goto useDefault;
}
if (CAPNPROTO_EXPECT_FALSE(ref->listRef.elementSize() == FieldSize::BYTE)) {
if (CAPNPROTO_EXPECT_FALSE(ref->listRef.elementSize() != FieldSize::BYTE)) {
segment->getMessage()->reportInvalidData(
"Message contains list reference of non-bytes where data was expected.");
goto useDefault;
......@@ -956,7 +955,7 @@ StructBuilder StructBuilder::initStructField(
StructBuilder StructBuilder::getStructField(
WireReferenceCount refIndex, const word* defaultValue) const {
return WireHelpers::getStructReference(references + refIndex, segment, defaultValue);
return WireHelpers::getWritableStructReference(references + refIndex, segment, defaultValue);
}
ListBuilder StructBuilder::initListField(
......
......@@ -387,6 +387,11 @@ inline bool StructBuilder::getDataField<bool>(ElementCount offset) const {
return (*reinterpret_cast<uint8_t*>(b) & (1 << (boffset % BITS_PER_BYTE / BITS))) != 0;
}
template <>
inline Void StructBuilder::getDataField<Void>(ElementCount offset) const {
return Void::VOID;
}
template <typename T>
inline void StructBuilder::setDataField(
ElementCount offset, typename NoInfer<T>::Type value) const {
......@@ -402,6 +407,9 @@ inline void StructBuilder::setDataField<bool>(ElementCount offset, bool value) c
| (static_cast<uint8_t>(value) << bitnum);
}
template <>
inline void StructBuilder::setDataField<Void>(ElementCount offset, Void value) const {}
// -------------------------------------------------------------------
template <typename T>
......@@ -428,6 +436,11 @@ inline bool StructReader::getDataField<bool>(ElementCount offset, bool defaultVa
}
}
template <>
inline Void StructReader::getDataField<Void>(ElementCount offset, Void defaultValue) const {
return Void::VOID;
}
template <typename T>
T StructReader::getDataFieldCheckingNumber(
FieldNumber fieldNumber, ElementCount offset, typename NoInfer<T>::Type defaultValue) const {
......@@ -457,6 +470,12 @@ inline bool StructReader::getDataFieldCheckingNumber<bool>(
}
}
template <>
inline Void StructReader::getDataFieldCheckingNumber<Void>(
FieldNumber fieldNumber, ElementCount offset, Void defaultValue) const {
return Void::VOID;
}
// -------------------------------------------------------------------
inline ElementCount ListBuilder::size() { return elementCount; }
......@@ -473,6 +492,11 @@ inline bool ListBuilder::getDataElement<bool>(ElementCount index) const {
return (*reinterpret_cast<uint8_t*>(b) & (1 << (bindex % BITS_PER_BYTE / BITS))) != 0;
}
template <>
inline Void ListBuilder::getDataElement<Void>(ElementCount index) const {
return Void::VOID;
}
template <typename T>
inline void ListBuilder::setDataElement(ElementCount index, typename NoInfer<T>::Type value) const {
reinterpret_cast<WireValue<T>*>(ptr)[index / ELEMENTS].set(value);
......@@ -487,6 +511,9 @@ inline void ListBuilder::setDataElement<bool>(ElementCount index, bool value) co
| (static_cast<uint8_t>(value) << bitnum);
}
template <>
inline void ListBuilder::setDataElement<Void>(ElementCount index, Void value) const {}
// -------------------------------------------------------------------
inline ElementCount ListReader::size() { return elementCount; }
......@@ -504,6 +531,11 @@ inline bool ListReader::getDataElement<bool>(ElementCount index) const {
return (*reinterpret_cast<const uint8_t*>(b) & (1 << (bindex % BITS_PER_BYTE / BITS))) != 0;
}
template <>
inline Void ListReader::getDataElement<Void>(ElementCount index) const {
return Void::VOID;
}
} // namespace internal
} // namespace capnproto
......
......@@ -20,6 +20,7 @@ executable capnproto-compiler
array,
data-binary-ieee754,
filepath
-- When profiling is needed, add: -prof -fprof-auto -osuf p_o -hisuf p_hi
ghc-options: -Wall -fno-warn-missing-signatures
other-modules:
Lexer,
......
......@@ -68,6 +68,9 @@ isList _ = False
isNonStructList (ListType t) = not $ isStruct t
isNonStructList _ = False
isPrimitiveList (ListType t) = isPrimitive t
isPrimitiveList _ = False
isStructList (ListType t) = isStruct t
isStructList _ = False
......@@ -75,7 +78,7 @@ blobTypeString (BuiltinType BuiltinText) = "Text"
blobTypeString (BuiltinType BuiltinData) = "Data"
blobTypeString _ = error "Not a blob."
cxxTypeString (BuiltinType BuiltinVoid) = "void"
cxxTypeString (BuiltinType BuiltinVoid) = " ::capnproto::Void"
cxxTypeString (BuiltinType BuiltinBool) = "bool"
cxxTypeString (BuiltinType BuiltinInt8) = " ::int8_t"
cxxTypeString (BuiltinType BuiltinInt16) = " ::int16_t"
......@@ -103,7 +106,7 @@ cxxFieldSizeString Size64 = "EIGHT_BYTES";
cxxFieldSizeString SizeReference = "REFERENCE";
cxxFieldSizeString (SizeInlineComposite _ _) = "INLINE_COMPOSITE";
cxxValueString VoidDesc = error "Can't stringify void value."
cxxValueString VoidDesc = " ::capnproto::Void::VOID"
cxxValueString (BoolDesc b) = if b then "true" else "false"
cxxValueString (Int8Desc i) = show i
cxxValueString (Int16Desc i) = show i
......@@ -129,7 +132,7 @@ defaultValueBytes t v@(StructValueDesc _) = Just $ encodeMessage t v
defaultValueBytes t v@(ListDesc _) = Just $ encodeMessage t v
defaultValueBytes _ _ = Nothing
cxxDefaultDefault (BuiltinType BuiltinVoid) = error "Can't stringify void value."
cxxDefaultDefault (BuiltinType BuiltinVoid) = " ::capnproto::Void::VOID"
cxxDefaultDefault (BuiltinType BuiltinBool) = "false"
cxxDefaultDefault (BuiltinType BuiltinInt8) = "0"
cxxDefaultDefault (BuiltinType BuiltinInt16) = "0"
......@@ -175,6 +178,7 @@ fieldContext parent desc = mkStrContext context where
context "fieldIsStruct" = MuBool $ isStruct $ fieldType desc
context "fieldIsList" = MuBool $ isList $ fieldType desc
context "fieldIsNonStructList" = MuBool $ isNonStructList $ fieldType desc
context "fieldIsPrimitiveList" = MuBool $ isPrimitiveList $ fieldType desc
context "fieldIsStructList" = MuBool $ isStructList $ fieldType desc
context "fieldDefaultBytes" =
case fieldDefaultValue desc >>= defaultValueBytes (fieldType desc) of
......
......@@ -32,7 +32,8 @@ import Token
import Data.Char (isUpper, isLower)
keywords =
[ (TrueKeyword, "true")
[ (VoidKeyword, "void")
, (TrueKeyword, "true")
, (FalseKeyword, "false")
, (InKeyword, "in")
, (OfKeyword, "of")
......@@ -105,11 +106,16 @@ identifier = do
\names into the target language's preferred style."
return (if isTypeName text then TypeIdentifier text else Identifier text)
tokenSequence = do
tokens <- many1 locatedToken
endPos <- getPosition
return (TokenSequence tokens endPos)
token :: Parser Token
token = keyword
<|> identifier
<|> liftM ParenthesizedList (parens (sepBy (many locatedToken) (symbol ",")))
<|> liftM BracketedList (brackets (sepBy (many locatedToken) (symbol ",")))
<|> liftM ParenthesizedList (parens (sepBy tokenSequence (symbol ",")))
<|> liftM BracketedList (brackets (sepBy tokenSequence (symbol ",")))
<|> liftM toLiteral naturalOrFloat
<|> liftM LiteralString stringLiteral
<|> liftM (const AtSign) (symbol "@")
......@@ -126,13 +132,13 @@ statementEnd :: Parser (Maybe [Located Statement])
statementEnd = (symbol ";" >>= \_ -> return Nothing)
<|> (braces (many locatedStatement) >>= \statements -> return (Just statements))
compileStatement :: [Located Token] -> Maybe [Located Statement] -> Statement
compileStatement :: TokenSequence -> Maybe [Located Statement] -> Statement
compileStatement tokens Nothing = Line tokens
compileStatement tokens (Just statements) = Block tokens statements
statement :: Parser Statement
statement = do
tokens <- many locatedToken
tokens <- tokenSequence
end <- statementEnd
return (compileStatement tokens end)
......
......@@ -25,6 +25,7 @@ module Parser (parseFile) where
import Text.Parsec hiding (tokens)
import Text.Parsec.Error(newErrorMessage, Message(Message))
import Text.Parsec.Pos(newPos)
import Text.Printf(printf)
import Token
import Grammar
......@@ -48,6 +49,7 @@ tokenErrorString Period = "\".\""
tokenErrorString EqualsSign = "\"=\""
tokenErrorString MinusSign = "\"-\""
tokenErrorString ExclamationPoint = "\"!\""
tokenErrorString VoidKeyword = "keyword \"void\""
tokenErrorString TrueKeyword = "keyword \"true\""
tokenErrorString FalseKeyword = "keyword \"false\""
tokenErrorString InKeyword = "keyword \"in\""
......@@ -103,6 +105,7 @@ literalInt = tokenParser matchLiteralInt <?> "integer"
literalFloat = tokenParser matchLiteralFloat <?> "floating-point number"
literalString = tokenParser matchLiteralString <?> "string"
literalBool = tokenParser matchLiteralBool <?> "boolean"
literalVoid = tokenParser (matchSimpleToken VoidKeyword) <?> "\"void\""
atSign = tokenParser (matchSimpleToken AtSign) <?> "\"@\""
colon = tokenParser (matchSimpleToken Colon) <?> "\":\""
......@@ -238,7 +241,8 @@ fieldDecl statements = do
negativeFieldValue = liftM (IntegerFieldValue . negate) literalInt
<|> liftM (FloatFieldValue . negate) literalFloat
fieldValue = liftM BoolFieldValue literalBool
fieldValue = (literalVoid >> return VoidFieldValue)
<|> liftM BoolFieldValue literalBool
<|> liftM IntegerFieldValue literalInt
<|> liftM FloatFieldValue literalFloat
<|> liftM StringFieldValue literalString
......@@ -302,30 +306,33 @@ failNonFatal :: SourcePos -> String -> TokenParser ()
failNonFatal pos msg = modifyState (newError:) where
newError = newErrorMessage (Message msg) pos
parseList parser items = finish where
results = map (parseCollectingErrors parser) items
finish = do
modifyState (\old -> concat (old:map extractErrors results))
return [ result | Right (result, _) <- results ]
parseList parser items = do
let results = map (parseCollectingErrors parser) items
modifyState (\old -> concat (old:map extractErrors results))
return [ result | Right (result, _) <- results ]
parseBlock :: (Maybe [Located Statement] -> TokenParser Declaration)
-> [Located Statement] -> TokenParser [Declaration]
parseBlock parser statements = finish where
results = map (parseStatement parser) statements
finish = do
modifyState (\old -> concat (old:map extractErrors results))
return [ result | Right (result, _) <- results ]
parseCollectingErrors :: TokenParser a -> [Located Token] -> Either ParseError (a, [ParseError])
parseCollectingErrors parser tokens = runParser parser' [] "" tokens where
parseBlock parser statements = do
let results = map (parseStatement parser) statements
modifyState (\old -> concat (old:map extractErrors results))
return [ result | Right (result, _) <- results ]
parseCollectingErrors :: TokenParser a -> TokenSequence
-> Either ParseError (a, [ParseError])
parseCollectingErrors parser tokenSequence = runParser parser' [] "" tokens where
TokenSequence tokens endPos = tokenSequence
parser' = do
-- Work around Parsec bug: Text.Parsec.Print.token is supposed to produce a parser that
-- sets the position by using the provided function to extract it from each token. However,
-- it doesn't bother to call this function for the *first* token, only subsequent tokens.
-- The first token is always assumed to be at 1:1. To fix this, set it manually.
case tokens of
Located pos _:_ -> setPosition pos
[] -> return ()
--
-- TODO: There's still a problem when a parse error occurs at end-of-input: Parsec will
-- report the error at the location of the previous token.
setPosition (case tokens of
Located pos2 _:_ -> pos2
[] -> endPos)
result <- parser
eof
......@@ -349,4 +356,4 @@ parseFileTokens statements = (decls, errors) where
parseFile :: String -> String -> ([Declaration], [ParseError])
parseFile filename text = case parse lexer filename text of
Left e -> ([], [e])
Right tokens -> parseFileTokens tokens
Right statements -> parseFileTokens statements
......@@ -122,7 +122,7 @@ data ValueDesc = VoidDesc
| ListDesc [ValueDesc]
deriving (Show)
valueString VoidDesc = error "Can't stringify void value."
valueString VoidDesc = "void"
valueString (BoolDesc b) = if b then "true" else "false"
valueString (Int8Desc i) = show i
valueString (Int16Desc i) = show i
......@@ -156,6 +156,8 @@ data PackingState = PackingState
, packingReferenceCount :: 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.
......@@ -416,7 +418,7 @@ descToCode indent (DescField desc) = printf "%s%s@%d%s: %s%s; # %s\n" indent
SizeReference -> printf "ref[%d]" $ fieldOffset desc
SizeInlineComposite _ _ -> "??"
s -> let
bits = (sizeInBits s)
bits = sizeInBits s
offset = fieldOffset desc
in printf "bits[%d, %d)" (offset * bits) ((offset + 1) * bits))
-- (maybeBlockCode indent $ fieldStatements desc)
......
......@@ -37,13 +37,16 @@ instance Eq a => Eq (Located a) where
instance Ord a => Ord (Located a) where
compare (Located _ a) (Located _ b) = compare a b
data TokenSequence = TokenSequence [Located Token] SourcePos deriving(Show, Eq)
data Token = Identifier String
| TypeIdentifier String
| ParenthesizedList [[Located Token]]
| BracketedList [[Located Token]]
| ParenthesizedList [TokenSequence]
| BracketedList [TokenSequence]
| LiteralInt Integer
| LiteralFloat Double
| LiteralString String
| VoidKeyword
| TrueKeyword
| FalseKeyword
| AtSign
......@@ -68,6 +71,6 @@ data Token = Identifier String
| OptionKeyword
deriving (Show, Eq)
data Statement = Line [Located Token]
| Block [Located Token] [Located Statement]
data Statement = Line TokenSequence
| Block TokenSequence [Located Statement]
deriving (Show)
......@@ -100,7 +100,7 @@ encodeReferences o size = loop 0 (o + size) where
(dataBytes, refBytes, childBytes) = encodeStruct desc assignments 0
in (encodeStructReference desc offset, concat [dataBytes, refBytes, childBytes])
(ListType elementType, ListDesc items) ->
(encodeListReference (fieldSize elementType) (genericLength items) offset,
(encodeListReference (elementSize elementType) (genericLength items) offset,
encodeList elementType items)
(BuiltinType BuiltinText, TextDesc text) -> let
encoded = (UTF8.encode text ++ [0])
......@@ -118,6 +118,20 @@ encodeReferences o size = loop 0 (o + size) where
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)
encodeStructReference desc offset =
bytes (offset * 4 + structTag) 4 ++
[ fromIntegral (length (structFields desc) + length (structUnions desc))
......@@ -205,21 +219,17 @@ encodeStruct desc assignments childOffset = (dataBytes, referenceBytes, children
(packingReferenceCount $ structPacking desc) sortedReferences
encodeList elementType elements = case elementSize elementType of
SizeInlineComposite ds rc -> case elementType of
SizeInlineComposite _ _ -> case elementType of
StructType desc -> let
count = genericLength elements
tag = encodeStructReference desc count
elemWords = ds + rc
(elemBytes, childBytes) = unzip
[ (d ++ r, c)
| (i, StructValueDesc assignments) <- zip [1..] elements
, let (d, r, c) = encodeStruct desc assignments ((count - i) * elemWords)]
in concat $ concat [[tag], elemBytes, childBytes]
(elemBytes, childBytes) = encodeStructList 0 desc [v | StructValueDesc v <- elements]
in concat [tag, elemBytes, childBytes]
_ -> 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 (genericLength elements * sizeInBits size) 64)
size -> encodeData (roundUpToMultiple 64 (genericLength elements * sizeInBits size))
$ zipWith (\i v -> (i * sizeInBits size, elementType, v)) [0..] elements
encodeMessage (StructType desc) (StructValueDesc assignments) = let
......
......@@ -86,6 +86,8 @@ class {{structName}}::Builder {
public:
Builder() = default;
inline explicit Builder(::capnproto::internal::StructBuilder base): _builder(base) {}
inline operator Reader() { return Reader(_builder.asReader()); }
inline Reader asReader() { return *this; }
{{#structFields}}
// {{fieldDecl}}
......@@ -105,6 +107,14 @@ public:
{{#fieldIsNonStructList}}
inline {{fieldType}}::Builder init{{fieldTitleCase}}(unsigned int size);
inline {{fieldType}}::Builder get{{fieldTitleCase}}();
template <typename _t>
inline void set{{fieldTitleCase}}(const _t& other);
{{#fieldIsPrimitiveList}}
inline void set{{fieldTitleCase}}(std::initializer_list<{{fieldElementType}}> other);
{{/fieldIsPrimitiveList}}
{{^fieldIsPrimitiveList}}
inline void set{{fieldTitleCase}}(std::initializer_list<{{fieldElementType}}::Reader> other);
{{/fieldIsPrimitiveList}}
{{/fieldIsNonStructList}}
{{#fieldIsStructList}}
inline {{fieldType}}::Builder init{{fieldTitleCase}}(unsigned int size);
......@@ -210,6 +220,22 @@ inline {{fieldType}}::Builder {{structName}}::Builder::get{{fieldTitleCase}}() {
{{#fieldDefaultBytes}}DEFAULT_{{fieldUpperCase}}.words{{/fieldDefaultBytes}}
{{^fieldDefaultBytes}}nullptr{{/fieldDefaultBytes}}));
}
template <typename _t>
inline void {{structName}}::Builder::set{{fieldTitleCase}}(const _t& other) {
init{{fieldTitleCase}}(other.size()).copyFrom(other);
}
{{#fieldIsPrimitiveList}}
inline void {{structName}}::Builder::set{{fieldTitleCase}}(
std::initializer_list<{{fieldElementType}}> other) {
init{{fieldTitleCase}}(other.size()).copyFrom(other);
}
{{/fieldIsPrimitiveList}}
{{^fieldIsPrimitiveList}}
inline void {{structName}}::Builder::set{{fieldTitleCase}}(
std::initializer_list<{{fieldElementType}}::Reader> other) {
init{{fieldTitleCase}}(other.size()).copyFrom(other);
}
{{/fieldIsPrimitiveList}}
{{/fieldIsNonStructList}}
{{#fieldIsStructList}}
inline {{fieldType}}::Builder {{structName}}::Builder::init{{fieldTitleCase}}(unsigned int size) {
......
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