Commit b4f80598 authored by Kenton Varda's avatar Kenton Varda

Text and data blobs.

parent 8b7e9096
// 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.
#include "blob.h"
#include <gtest/gtest.h>
#include <iostream>
#include <string>
namespace capnproto {
std::ostream& operator<<(std::ostream& os, const Data::Reader& value) {
return os.write(value.data(), value.size());
}
std::ostream& operator<<(std::ostream& os, const Data::Builder& value) {
return os.write(value.data(), value.size());
}
namespace {
TEST(Blob, Text) {
std::string str = "foo";
Text::Reader text = str;
EXPECT_STREQ("foo", text);
EXPECT_STREQ("foo", text.c_str());
EXPECT_STREQ("foo", text.data());
EXPECT_EQ(3u, text.size());
std::string str2 = text;
EXPECT_EQ("foo", str2);
Text::Reader text2 = "bar";
EXPECT_STREQ("bar", text2);
char c[4] = "baz";
Text::Reader text3(c);
EXPECT_STREQ("baz", text3);
Text::Builder builder(c, 3);
EXPECT_STREQ("baz", builder);
EXPECT_EQ(Data::Reader("az"), builder.slice(1, 3));
}
TEST(Blob, Data) {
std::string str = "foo";
Data::Reader data = str;
EXPECT_EQ(3u, data.size());
std::string str2 = data;
EXPECT_EQ("foo", str2);
Data::Reader data2 = "bar";
EXPECT_EQ("bar", data2.as<std::string>());
char c[4] = "baz";
Data::Reader data3(c);
EXPECT_EQ("baz", data3.as<std::string>());
Data::Builder builder(c, 3);
EXPECT_EQ("baz", builder.as<std::string>());
EXPECT_EQ(Data::Reader("az"), builder.slice(1, 3));
}
} // namespace
} // namespace capnproto
// 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.
#include "blob.h"
namespace capnproto {
char Text::Builder::nulstr[1] = "";
} // namespace capnproto
// Copyright (c) 2013, Kenton Varda <temporal@gmail.com>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef CAPNPROTO_BLOB_H_
#define CAPNPROTO_BLOB_H_
#include "macros.h"
#include "type-safety.h"
#include <string.h>
namespace capnproto {
struct Data {
class Reader;
class Builder;
};
struct Text {
class Reader;
class Builder;
};
class Data::Reader {
// Points to a blob of bytes. The usual Reader rules apply -- Data::Reader behaves like a simple
// pointer which does not own its target, can be passed by value, etc.
//
// Data::Reader can be implicitly converted to any type which has a constructor that takes a
// (const char*, size) pair, and can be implicitly constructed from any type that has data()
// and size() methods. Many types follow this pattern, such as std::string. Data::Reader can
// also be implicitly constructed from a NUL-terminated char*.
public:
inline Reader(): bytes(nullptr), size_(0) {}
inline Reader(const char* bytes): bytes(bytes), size_(strlen(bytes)) {}
inline Reader(char* bytes): bytes(bytes), size_(strlen(bytes)) {}
inline Reader(const char* bytes, uint size): bytes(bytes), size_(size) {}
template <typename T>
inline Reader(const T& other): bytes(other.data()), size_(other.size()) {}
// Primarily intended for converting from std::string.
template <typename T>
inline operator T() const { return T(bytes, size_); }
// Primarily intended for converting to std::string.
template <typename T>
inline T as() const { return T(bytes, size_); }
// Explicitly converts to the desired type, which must have a (const char*, size) constructor.
inline const char* data() const { return bytes; }
inline uint size() const { return size_; }
inline const char operator[](uint index) const { return bytes[index]; }
inline Reader slice(uint start, uint end) const {
CAPNPROTO_DEBUG_ASSERT(start <= end && end <= size_, "Out-of-bounds slice.");
return Reader(bytes + start, end - start);
}
inline bool operator==(const Reader& other) const {
return size_ == other.size_ && memcmp(bytes, other.bytes, size_) == 0;
}
inline bool operator<=(const Reader& other) const {
bool shorter = size_ <= other.size_;
int cmp = memcmp(bytes, other.bytes, shorter ? size_ : other.size_);
return cmp < 0 ? true : cmp > 0 ? false : size_ <= other.size_;
}
inline bool operator!=(const Reader& other) const { return !(*this == other); }
inline bool operator>=(const Reader& other) const { return other <= *this; }
inline bool operator< (const Reader& other) const { return !(other <= *this); }
inline bool operator> (const Reader& other) const { return !(*this <= other); }
private:
const char* bytes;
uint size_;
};
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
// 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.
public:
inline Reader(): Data::Reader("", 0) {}
inline Reader(const char* text): Data::Reader(text, strlen(text)) {
CAPNPROTO_DEBUG_ASSERT(text[size()] == '\0', "Text must be NUL-terminated.");
}
inline Reader(char* text): Data::Reader(text, strlen(text)) {
CAPNPROTO_DEBUG_ASSERT(text[size()] == '\0', "Text must be NUL-terminated.");
}
inline Reader(const char* text, uint size): Data::Reader(text, size) {
CAPNPROTO_DEBUG_ASSERT(text[size] == '\0', "Text must be NUL-terminated.");
}
template <typename T>
inline Reader(const T& other): Data::Reader(other.c_str(), other.size()) {
// Primarily intended for converting from std::string.
CAPNPROTO_DEBUG_ASSERT(data()[size()] == '\0',
"Text must be NUL-terminated.");
}
inline const char* c_str() const { return data(); }
inline operator const char*() const { return data(); }
};
class Data::Builder {
// Like Data::Reader except the pointers aren't const, and it can't be implicitly constructed from
// other types.
public:
inline Builder(): bytes(nullptr), size_(0) {}
inline Builder(char* bytes, uint size): bytes(bytes), size_(size) {}
template <typename T>
inline operator T() const { return T(bytes, size_); }
// Primarily intended for converting to std::string.
inline operator Data::Reader() const { return Data::Reader(bytes, size_); }
template <typename T>
inline T as() const { return T(bytes, size_); }
// Explicitly converts to the desired type, which must have a (char*, size) constructor.
inline char* data() const { return bytes; }
inline uint size() const { return size_; }
inline char& operator[](uint index) const { return bytes[index]; }
inline Builder slice(uint start, uint end) const {
CAPNPROTO_DEBUG_ASSERT(start <= end && end <= size_, "Out-of-bounds slice.");
return Builder(bytes + start, end - start);
}
inline bool operator==(const Builder& other) const {
return size_ == other.size_ && memcmp(bytes, other.bytes, size_) == 0;
}
inline bool operator<=(const Builder& other) const {
bool shorter = size_ <= other.size_;
int cmp = memcmp(bytes, other.bytes, shorter ? size_ : other.size_);
return cmp < 0 ? true : cmp > 0 ? false : size_ <= other.size_;
}
inline bool operator!=(const Builder& other) const { return !(*this == other); }
inline bool operator>=(const Builder& other) const { return other <= *this; }
inline bool operator< (const Builder& other) const { return !(other <= *this); }
inline bool operator> (const Builder& other) const { return !(*this <= other); }
template <typename T>
inline void copyFrom(const T& other) const {
CAPNPROTO_DEBUG_ASSERT(size() == other.size(), "Sizes must match to copy.");
memcpy(bytes, other.data(), other.size());
}
inline void copyFrom(const void* other) const {
memcpy(bytes, other, size_);
}
private:
char* bytes;
uint size_;
};
class Text::Builder: public Data::Builder {
// Like Text::Reader except the pointers aren't const, and it can't be implicitly constructed from
// other types. The Text::Builder automatically initializes the NUL terminator at construction,
// so it is never necessary for the caller to do so.
public:
inline Builder(): Data::Builder(nulstr, 0) {}
inline Builder(char* text, uint size): Data::Builder(text, size) { text[size] = '\0'; }
inline char* c_str() const { return data(); }
inline operator char*() const { return data(); }
inline operator const char*() const { return data(); }
private:
static char nulstr[1];
};
} // namespace capnproto
#endif // CAPNPROTO_BLOB_H_
......@@ -45,6 +45,7 @@ TEST(Encoding, Simple) {
builder.setB(45);
builder.setC(67);
builder.initD().setX(55.25);
builder.setTx("foo");
{
List<int32_t>::Builder listBuilder = builder.initNums(5);
......@@ -146,6 +147,7 @@ TEST(Encoding, Simple) {
EXPECT_EQ(45, builder.getB());
EXPECT_EQ(67, builder.getC());
EXPECT_EQ(55.25, builder.getD().getX());
EXPECT_STREQ("foo", builder.getTx());
Foo::Reader reader(StructReader::readRoot(
segment->getStartPtr(), Foo::DEFAULT.words, segment, 4));
......@@ -154,6 +156,7 @@ TEST(Encoding, Simple) {
EXPECT_EQ(45, reader.getB());
EXPECT_EQ(67, reader.getC());
EXPECT_EQ(55.25, reader.getD().getX());
EXPECT_STREQ("foo", reader.getTx());
{
List<int32_t>::Reader listReader = reader.getNums();
......
......@@ -98,24 +98,24 @@ class IndexingIterator {
public:
IndexingIterator() = default;
inline Element operator*() { return (*container)[index]; }
inline TemporaryPointer<Element> operator->() {
inline Element operator*() const { return (*container)[index]; }
inline TemporaryPointer<Element> operator->() const {
return TemporaryPointer<Element>((*container)[index]);
}
inline Element operator[]( int off) { return (*container)[index]; }
inline Element operator[](uint off) { return (*container)[index]; }
inline Element operator[]( int off) const { return (*container)[index]; }
inline Element operator[](uint off) const { return (*container)[index]; }
inline IndexingIterator& operator++() { ++index; return *this; }
inline IndexingIterator operator++(int) { IndexingIterator other = *this; ++index; return other; }
inline IndexingIterator& operator--() { --index; return *this; }
inline IndexingIterator operator--(int) { IndexingIterator other = *this; --index; return other; }
inline IndexingIterator operator+(uint amount) { return IndexingIterator(container, index + amount); }
inline IndexingIterator operator-(uint amount) { return IndexingIterator(container, index - amount); }
inline IndexingIterator operator+( int amount) { return IndexingIterator(container, index + amount); }
inline IndexingIterator operator-( int amount) { return IndexingIterator(container, index - amount); }
inline IndexingIterator operator+(uint amount) const { return IndexingIterator(container, index + amount); }
inline IndexingIterator operator-(uint amount) const { return IndexingIterator(container, index - amount); }
inline IndexingIterator operator+( int amount) const { return IndexingIterator(container, index + amount); }
inline IndexingIterator operator-( int amount) const { return IndexingIterator(container, index - amount); }
inline int operator-(const IndexingIterator& other) { return index - other.index; }
inline int operator-(const IndexingIterator& other) const { return index - other.index; }
inline IndexingIterator& operator+=(uint amount) { index += amount; return *this; }
inline IndexingIterator& operator-=(uint amount) { index -= amount; return *this; }
......@@ -124,12 +124,12 @@ public:
// STL says comparing iterators of different containers is not allowed, so we only compare
// indices here.
inline bool operator==(const IndexingIterator& other) { return index == other.index; }
inline bool operator!=(const IndexingIterator& other) { return index != other.index; }
inline bool operator<=(const IndexingIterator& other) { return index <= other.index; }
inline bool operator>=(const IndexingIterator& other) { return index >= other.index; }
inline bool operator< (const IndexingIterator& other) { return index < other.index; }
inline bool operator> (const IndexingIterator& other) { return index > other.index; }
inline bool operator==(const IndexingIterator& other) const { return index == other.index; }
inline bool operator!=(const IndexingIterator& other) const { return index != other.index; }
inline bool operator<=(const IndexingIterator& other) const { return index <= other.index; }
inline bool operator>=(const IndexingIterator& other) const { return index >= other.index; }
inline bool operator< (const IndexingIterator& other) const { return index < other.index; }
inline bool operator> (const IndexingIterator& other) const { return index > other.index; }
private:
Container* container;
......@@ -316,6 +316,90 @@ struct List<List<T>, false> {
};
};
template <>
struct List<Data, false> {
class Reader {
public:
Reader() = default;
inline explicit Reader(internal::ListReader reader): reader(reader) {}
inline uint size() { return reader.size() / ELEMENTS; }
inline Data::Reader operator[](uint index) {
return reader.getDataElement(index * REFERENCES);
}
typedef internal::IndexingIterator<Reader, Data::Reader> iterator;
inline iterator begin() { return iterator(this, 0); }
inline iterator end() { return iterator(this, size()); }
private:
internal::ListReader reader;
};
class Builder {
public:
Builder() = default;
inline explicit Builder(internal::ListBuilder builder): builder(builder) {}
inline uint size() { return builder.size() / ELEMENTS; }
inline Data::Builder operator[](uint index) {
return builder.getDataElement(index * REFERENCES);
}
inline Data::Builder init(uint index, uint size) {
return builder.initDataElement(index * REFERENCES, size * BYTES);
}
typedef internal::IndexingIterator<Builder, Data::Builder> iterator;
inline iterator begin() { return iterator(this, 0); }
inline iterator end() { return iterator(this, size()); }
private:
internal::ListBuilder builder;
};
};
template <>
struct List<Text, false> {
class Reader {
public:
Reader() = default;
inline explicit Reader(internal::ListReader reader): reader(reader) {}
inline uint size() { return reader.size() / ELEMENTS; }
inline Text::Reader operator[](uint index) {
return reader.getTextElement(index * REFERENCES);
}
typedef internal::IndexingIterator<Reader, Text::Reader> iterator;
inline iterator begin() { return iterator(this, 0); }
inline iterator end() { return iterator(this, size()); }
private:
internal::ListReader reader;
};
class Builder {
public:
Builder() = default;
inline explicit Builder(internal::ListBuilder builder): builder(builder) {}
inline uint size() { return builder.size() / ELEMENTS; }
inline Text::Builder operator[](uint index) {
return builder.getTextElement(index * REFERENCES);
}
inline Text::Builder init(uint index, uint size) {
return builder.initTextElement(index * REFERENCES, size * BYTES);
}
typedef internal::IndexingIterator<Builder, Text::Builder> iterator;
inline iterator begin() { return iterator(this, 0); }
inline iterator end() { return iterator(this, size()); }
private:
internal::ListBuilder builder;
};
};
} // namespace capnproto
#endif // CAPNPROTO_LIST_H_
......@@ -187,6 +187,12 @@ struct WireHelpers {
return ((bits2 >> 6) + ((bits2 & 63) != 0)) * WORDS;
}
static CAPNPROTO_ALWAYS_INLINE(WordCount roundUpToWords(ByteCount bytes)) {
static_assert(sizeof(word) == 8, "This code assumes 64-bit words.");
uint bytes2 = bytes / BYTES;
return ((bytes2 >> 3) + ((bytes2 & 7) != 0)) * WORDS;
}
static CAPNPROTO_ALWAYS_INLINE(word* allocate(
WireReference*& ref, SegmentBuilder*& segment, WordCount amount,
WireReference::Kind kind)) {
......@@ -529,6 +535,82 @@ struct WireHelpers {
}
}
static CAPNPROTO_ALWAYS_INLINE(Text::Builder initTextReference(
WireReference* ref, SegmentBuilder* segment, ByteCount size)) {
// The byte list must include a NUL terminator.
ByteCount byteSize = size + 1 * BYTES;
// Allocate the space.
word* ptr = allocate(ref, segment, roundUpToWords(byteSize), WireReference::LIST);
// Initialize the reference.
ref->listRef.set(FieldSize::BYTE, byteSize * (1 * ELEMENTS / BYTES));
// Build the Text::Builder. This will initialize the NUL terminator.
return Text::Builder(reinterpret_cast<char*>(ptr), size / BYTES);
}
static CAPNPROTO_ALWAYS_INLINE(void setTextReference(
WireReference* ref, SegmentBuilder* segment, Text::Reader value)) {
initTextReference(ref, segment, value.size() * BYTES).copyFrom(value);
}
static CAPNPROTO_ALWAYS_INLINE(Text::Builder getWritableTextReference(
WireReference* ref, SegmentBuilder* segment,
const void* defaultValue, ByteCount defaultSize)) {
if (ref->isNull()) {
Text::Builder builder = initTextReference(ref, segment, defaultSize);
builder.copyFrom(defaultValue);
return builder;
} else {
word* ptr = followFars(ref, segment);
CAPNPROTO_ASSERT(ref->kind() == WireReference::LIST,
"Called getText{Field,Element}() but existing reference is not a list.");
CAPNPROTO_ASSERT(ref->listRef.elementSize() == FieldSize::BYTE,
"Called getText{Field,Element}() but existing list reference is not byte-sized.");
// Subtract 1 from the size for the NUL terminator.
return Text::Builder(reinterpret_cast<char*>(ptr), ref->listRef.elementCount() / ELEMENTS - 1);
}
}
static CAPNPROTO_ALWAYS_INLINE(Data::Builder initDataReference(
WireReference* ref, SegmentBuilder* segment, ByteCount size)) {
// Allocate the space.
word* ptr = allocate(ref, segment, roundUpToWords(size), WireReference::LIST);
// Initialize the reference.
ref->listRef.set(FieldSize::BYTE, size * (1 * ELEMENTS / BYTES));
// Build the Data::Builder.
return Data::Builder(reinterpret_cast<char*>(ptr), size / BYTES);
}
static CAPNPROTO_ALWAYS_INLINE(void setDataReference(
WireReference* ref, SegmentBuilder* segment, Data::Reader value)) {
initDataReference(ref, segment, value.size() * BYTES).copyFrom(value);
}
static CAPNPROTO_ALWAYS_INLINE(Data::Builder getWritableDataReference(
WireReference* ref, SegmentBuilder* segment,
const void* defaultValue, ByteCount defaultSize)) {
if (ref->isNull()) {
Data::Builder builder = initDataReference(ref, segment, defaultSize);
builder.copyFrom(defaultValue);
return builder;
} else {
word* ptr = followFars(ref, segment);
CAPNPROTO_ASSERT(ref->kind() == WireReference::LIST,
"Called getData{Field,Element}() but existing reference is not a list.");
CAPNPROTO_ASSERT(ref->listRef.elementSize() == FieldSize::BYTE,
"Called getData{Field,Element}() but existing list reference is not byte-sized.");
return Data::Builder(reinterpret_cast<char*>(ptr), ref->listRef.elementCount() / ELEMENTS);
}
}
static CAPNPROTO_ALWAYS_INLINE(StructReader readStructReference(
SegmentReader* segment, const WireReference* ref, const word* defaultValue,
int recursionLimit)) {
......@@ -759,6 +841,104 @@ struct WireHelpers {
}
}
}
static CAPNPROTO_ALWAYS_INLINE(Text::Reader readTextReference(
SegmentReader* segment, const WireReference* ref,
const void* defaultValue, ByteCount defaultSize)) {
if (ref == nullptr || ref->isNull()) {
useDefault:
if (defaultValue == nullptr) {
defaultValue = "";
}
return Text::Reader(reinterpret_cast<const char*>(defaultValue), defaultSize / BYTES);
} else if (segment == nullptr) {
// Trusted message.
return Text::Reader(reinterpret_cast<const char*>(ref->target()),
ref->listRef.elementCount() / ELEMENTS - 1);
} else {
const word* ptr = followFars(ref, segment);
uint size = ref->listRef.elementCount() / ELEMENTS;
if (CAPNPROTO_EXPECT_FALSE(ptr == nullptr)) {
segment->getMessage()->reportInvalidData(
"Message contains out-of-bounds far reference.");
goto useDefault;
}
if (CAPNPROTO_EXPECT_FALSE(ref->kind() != WireReference::LIST)) {
segment->getMessage()->reportInvalidData(
"Message contains non-list reference where text was expected.");
goto useDefault;
}
if (CAPNPROTO_EXPECT_FALSE(ref->listRef.elementSize() != FieldSize::BYTE)) {
segment->getMessage()->reportInvalidData(
"Message contains list reference of non-bytes where text was expected.");
goto useDefault;
}
if (CAPNPROTO_EXPECT_FALSE(!segment->containsInterval(ptr, ptr +
roundUpToWords(ref->listRef.elementCount() * (1 * BYTES / ELEMENTS))))) {
segment->getMessage()->reportInvalidData(
"Message contained out-of-bounds text reference.");
goto useDefault;
}
const char* cptr = reinterpret_cast<const char*>(ptr);
--size; // NUL terminator
if (CAPNPROTO_EXPECT_FALSE(cptr[size] != '\0')) {
segment->getMessage()->reportInvalidData(
"Message contains text that is not NUL-terminated.");
goto useDefault;
}
return Text::Reader(cptr, size);
}
}
static CAPNPROTO_ALWAYS_INLINE(Data::Reader readDataReference(
SegmentReader* segment, const WireReference* ref,
const void* defaultValue, ByteCount defaultSize)) {
if (ref == nullptr || ref->isNull()) {
useDefault:
return Data::Reader(reinterpret_cast<const char*>(defaultValue), defaultSize / BYTES);
} else if (segment == nullptr) {
// Trusted message.
return Data::Reader(reinterpret_cast<const char*>(ref->target()),
ref->listRef.elementCount() / ELEMENTS);
} else {
const word* ptr = followFars(ref, segment);
uint size = ref->listRef.elementCount() / ELEMENTS;
if (CAPNPROTO_EXPECT_FALSE(ptr == nullptr)) {
segment->getMessage()->reportInvalidData(
"Message contains out-of-bounds far reference.");
goto useDefault;
}
if (CAPNPROTO_EXPECT_FALSE(ref->kind() != WireReference::LIST)) {
segment->getMessage()->reportInvalidData(
"Message contains non-list reference where data was expected.");
goto useDefault;
}
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;
}
if (CAPNPROTO_EXPECT_FALSE(!segment->containsInterval(ptr, ptr +
roundUpToWords(ref->listRef.elementCount() * (1 * BYTES / ELEMENTS))))) {
segment->getMessage()->reportInvalidData(
"Message contained out-of-bounds data reference.");
goto useDefault;
}
return Data::Reader(reinterpret_cast<const char*>(ptr), size);
}
}
};
// =======================================================================================
......@@ -799,6 +979,30 @@ ListBuilder StructBuilder::getListField(
references + refIndex, segment, defaultValue);
}
Text::Builder StructBuilder::initTextField(WireReferenceCount refIndex, ByteCount size) const {
return WireHelpers::initTextReference(references + refIndex, segment, size);
}
void StructBuilder::setTextField(WireReferenceCount refIndex, Text::Reader value) const {
WireHelpers::setTextReference(references + refIndex, segment, value);
}
Text::Builder StructBuilder::getTextField(
WireReferenceCount refIndex, const void* defaultValue, ByteCount defaultSize) const {
return WireHelpers::getWritableTextReference(
references + refIndex, segment, defaultValue, defaultSize);
}
Data::Builder StructBuilder::initDataField(WireReferenceCount refIndex, ByteCount size) const {
return WireHelpers::initDataReference(references + refIndex, segment, size);
}
void StructBuilder::setDataField(WireReferenceCount refIndex, Data::Reader value) const {
WireHelpers::setDataReference(references + refIndex, segment, value);
}
Data::Builder StructBuilder::getDataField(
WireReferenceCount refIndex, const void* defaultValue, ByteCount defaultSize) const {
return WireHelpers::getWritableDataReference(
references + refIndex, segment, defaultValue, defaultSize);
}
StructReader StructBuilder::asReader() const {
// HACK: We just give maxed-out field, data size, and reference counts because they are only
// used for checking for field presence.
......@@ -842,6 +1046,18 @@ ListReader StructReader::getListField(
segment, ref, defaultValue, expectedElementSize, recursionLimit);
}
Text::Reader StructReader::getTextField(
WireReferenceCount refIndex, const void* defaultValue, ByteCount defaultSize) const {
const WireReference* ref = refIndex >= referenceCount ? nullptr : references + refIndex;
return WireHelpers::readTextReference(segment, ref, defaultValue, defaultSize);
}
Data::Reader StructReader::getDataField(
WireReferenceCount refIndex, const void* defaultValue, ByteCount defaultSize) const {
const WireReference* ref = refIndex >= referenceCount ? nullptr : references + refIndex;
return WireHelpers::readDataReference(segment, ref, defaultValue, defaultSize);
}
StructBuilder ListBuilder::getStructElement(
ElementCount index, decltype(WORDS/ELEMENTS) elementSize, WordCount structDataSize) const {
word* structPtr = ptr + elementSize * index;
......@@ -868,6 +1084,32 @@ ListBuilder ListBuilder::getListElement(WireReferenceCount index) const {
reinterpret_cast<WireReference*>(ptr) + index, segment, nullptr);
}
Text::Builder ListBuilder::initTextElement(WireReferenceCount index, ByteCount size) const {
return WireHelpers::initTextReference(
reinterpret_cast<WireReference*>(ptr) + index, segment, size);
}
void ListBuilder::setTextElement(WireReferenceCount index, Text::Reader value) const {
WireHelpers::setTextReference(
reinterpret_cast<WireReference*>(ptr) + index, segment, value);
}
Text::Builder ListBuilder::getTextElement(WireReferenceCount index) const {
return WireHelpers::getWritableTextReference(
reinterpret_cast<WireReference*>(ptr) + index, segment, "", 0 * BYTES);
}
Data::Builder ListBuilder::initDataElement(WireReferenceCount index, ByteCount size) const {
return WireHelpers::initDataReference(
reinterpret_cast<WireReference*>(ptr) + index, segment, size);
}
void ListBuilder::setDataElement(WireReferenceCount index, Data::Reader value) const {
WireHelpers::setDataReference(
reinterpret_cast<WireReference*>(ptr) + index, segment, value);
}
Data::Builder ListBuilder::getDataElement(WireReferenceCount index) const {
return WireHelpers::getWritableDataReference(
reinterpret_cast<WireReference*>(ptr) + index, segment, nullptr, 0 * BYTES);
}
ListReader ListBuilder::asReader(FieldSize elementSize) const {
// TODO: For INLINE_COMPOSITE I suppose we could just check the tag?
CAPNPROTO_ASSERT(elementSize != FieldSize::INLINE_COMPOSITE,
......@@ -906,5 +1148,15 @@ ListReader ListReader::getListElement(
nullptr, expectedElementSize, recursionLimit);
}
Text::Reader ListReader::getTextElement(WireReferenceCount index) const {
return WireHelpers::readTextReference(segment,
reinterpret_cast<const WireReference*>(ptr) + index, "", 0 * BYTES);
}
Data::Reader ListReader::getDataElement(WireReferenceCount index) const {
return WireHelpers::readDataReference(segment,
reinterpret_cast<const WireReference*>(ptr) + index, nullptr, 0 * BYTES);
}
} // namespace internal
} // namespace capnproto
......@@ -32,6 +32,7 @@
#include "macros.h"
#include "type-safety.h"
#include "blob.h"
namespace capnproto {
class SegmentReader;
......@@ -124,6 +125,23 @@ public:
// already allocated, it is allocated as a deep copy of the given default value (a trusted
// message). If the default value is null, an empty list is used.
Text::Builder initTextField(WireReferenceCount refIndex, ByteCount size) const;
// Initialize the text field to the given size in bytes (not including NUL terminator) and return
// a Text::Builder which can be used to fill in the content.
void setTextField(WireReferenceCount refIndex, Text::Reader value) const;
// Set the text field to a copy of the given text.
Text::Builder getTextField(WireReferenceCount refIndex,
const void* defaultValue, ByteCount defaultSize) const;
// Get the text field. If it is not initialized, initialize it to a copy of the given default.
Data::Builder initDataField(WireReferenceCount refIndex, ByteCount size) const;
void setDataField(WireReferenceCount refIndex, Data::Reader value) const;
Data::Builder getDataField(WireReferenceCount refIndex,
const void* defaultValue, ByteCount defaultSize) const;
// Same as *Text*, but for data blobs.
StructReader asReader() const;
// Gets a StructReader pointing at the same memory.
......@@ -174,6 +192,14 @@ public:
// Get the list field at the given index in the reference segment, or the default value if not
// initialized. The default value is allowed to be null, in which case an empty list is used.
Text::Reader getTextField(WireReferenceCount refIndex,
const void* defaultValue, ByteCount defaultSize) const;
// Gets the text field, or the given default value if not initialized.
Data::Reader getDataField(WireReferenceCount refIndex,
const void* defaultValue, ByteCount defaultSize) const;
// Gets the data field, or the given default value if not initialized.
private:
SegmentReader* segment; // Memory segment in which the struct resides.
......@@ -244,6 +270,20 @@ public:
// Get the existing list element at the given index. Returns an empty list if the element is
// not initialized.
Text::Builder initTextElement(WireReferenceCount index, ByteCount size) const;
// Initialize the text element to the given size in bytes (not including NUL terminator) and
// return a Text::Builder which can be used to fill in the content.
void setTextElement(WireReferenceCount index, Text::Reader value) const;
// Set the text element to a copy of the given text.
Text::Builder getTextElement(WireReferenceCount index) const;
// Get the text element. If it is not initialized, returns an empty Text::Builder.
Data::Builder initDataElement(WireReferenceCount index, ByteCount size) const;
void setDataElement(WireReferenceCount index, Data::Reader value) const;
Data::Builder getDataElement(WireReferenceCount index) const;
ListReader asReader(FieldSize elementSize) const;
// Get a ListReader pointing at the same memory. Use this version only for non-struct lists.
......@@ -283,6 +323,12 @@ public:
ListReader getListElement(WireReferenceCount index, FieldSize expectedElementSize) const;
// Get the list element at the given index.
Text::Reader getTextElement(WireReferenceCount index) const;
// Get the text element. If it is not initialized, returns an empty Text::Reader.
Data::Reader getDataElement(WireReferenceCount index) const;
// Get the data element. If it is not initialized, returns an empty Data::Reader.
private:
SegmentReader* segment; // Memory segment in which the list resides.
......
......@@ -49,12 +49,16 @@ hashString str =
MD5.hash $
UTF8.encode str
isPrimitive (BuiltinType _) = True
isPrimitive t@(BuiltinType _) = not $ isBlob t
isPrimitive (EnumType _) = True
isPrimitive (StructType _) = False
isPrimitive (InterfaceType _) = False
isPrimitive (ListType _) = False
isBlob (BuiltinType BuiltinText) = True
isBlob (BuiltinType BuiltinData) = True
isBlob _ = False
isStruct (StructType _) = True
isStruct _ = False
......@@ -67,6 +71,10 @@ isNonStructList _ = False
isStructList (ListType t) = isStruct t
isStructList _ = False
blobTypeString (BuiltinType BuiltinText) = "Text"
blobTypeString (BuiltinType BuiltinData) = "Data"
blobTypeString _ = error "Not a blob."
cxxTypeString (BuiltinType BuiltinVoid) = "void"
cxxTypeString (BuiltinType BuiltinBool) = "bool"
cxxTypeString (BuiltinType BuiltinInt8) = " ::int8_t"
......@@ -79,8 +87,8 @@ cxxTypeString (BuiltinType BuiltinUInt32) = " ::uint32_t"
cxxTypeString (BuiltinType BuiltinUInt64) = " ::uint64_t"
cxxTypeString (BuiltinType BuiltinFloat32) = "float"
cxxTypeString (BuiltinType BuiltinFloat64) = "double"
cxxTypeString (BuiltinType BuiltinText) = "TODO"
cxxTypeString (BuiltinType BuiltinData) = "TODO"
cxxTypeString (BuiltinType BuiltinText) = " ::capnproto::Text"
cxxTypeString (BuiltinType BuiltinData) = " ::capnproto::Data"
cxxTypeString (EnumType desc) = enumName desc -- TODO: full name
cxxTypeString (StructType desc) = structName desc -- TODO: full name
cxxTypeString (InterfaceType desc) = interfaceName desc -- TODO: full name
......@@ -146,11 +154,15 @@ elementType _ = error "Called elementType on non-list."
repeatedlyTake _ [] = []
repeatedlyTake n l = take n l : repeatedlyTake n (drop n l)
defaultBytesContext :: Monad m => (String -> MuType m) -> [Word8] -> MuContext m
defaultBytesContext parent bytes = mkStrContext context where
defaultBytesContext :: Monad m => (String -> MuType m) -> TypeDesc -> [Word8] -> MuContext m
defaultBytesContext parent t bytes = mkStrContext context where
codeLines = map (delimit ", ") $ repeatedlyTake 8 $ map (printf "%3d") bytes
context "defaultByteList" = MuVariable $ delimit ",\n " codeLines
context "defaultWordCount" = MuVariable $ div (length bytes + 7) 8
context "defaultBlobSize" = case t of
BuiltinType BuiltinText -> MuVariable (length bytes - 1) -- Don't include NUL terminator.
BuiltinType BuiltinData -> MuVariable (length bytes)
_ -> error "defaultBlobSize used on non-blob."
context s = parent s
fieldContext parent desc = mkStrContext context where
......@@ -159,15 +171,17 @@ fieldContext parent desc = mkStrContext context where
context "fieldTitleCase" = MuVariable $ toTitleCase $ fieldName desc
context "fieldUpperCase" = MuVariable $ toUpperCaseWithUnderscores $ fieldName desc
context "fieldIsPrimitive" = MuBool $ isPrimitive $ fieldType desc
context "fieldIsBlob" = MuBool $ isBlob $ fieldType desc
context "fieldIsStruct" = MuBool $ isStruct $ fieldType desc
context "fieldIsList" = MuBool $ isList $ fieldType desc
context "fieldIsNonStructList" = MuBool $ isNonStructList $ fieldType desc
context "fieldIsStructList" = MuBool $ isStructList $ fieldType desc
context "fieldDefaultBytes" =
case fieldDefaultValue desc >>= defaultValueBytes (fieldType desc) of
Just v -> MuList [defaultBytesContext context v]
Just v -> MuList [defaultBytesContext context (fieldType desc) v]
Nothing -> muNull
context "fieldType" = MuVariable $ cxxTypeString $ fieldType desc
context "fieldBlobType" = MuVariable $ blobTypeString $ fieldType desc
context "fieldOffset" = MuVariable $ fieldOffset desc
context "fieldDefaultValue" = case fieldDefaultValue desc of
Just v -> MuVariable $ cxxValueString v
......@@ -184,7 +198,7 @@ structContext parent desc = mkStrContext context where
context "structDataSize" = MuVariable $ packingDataSize $ structPacking desc
context "structReferenceCount" = MuVariable $ packingReferenceCount $ structPacking desc
context "structChildren" = MuList [] -- TODO
context "structDefault" = MuList [defaultBytesContext context
context "structDefault" = MuList [defaultBytesContext context (StructType desc)
(encodeMessage (StructType desc) (StructValueDesc []))]
context s = parent s
......
......@@ -66,6 +66,9 @@ public:
{{#fieldIsPrimitive}}
inline {{fieldType}} get{{fieldTitleCase}}();
{{/fieldIsPrimitive}}
{{#fieldIsBlob}}
inline {{fieldType}}::Reader get{{fieldTitleCase}}();
{{/fieldIsBlob}}
{{#fieldIsStruct}}
inline {{fieldType}}::Reader get{{fieldTitleCase}}();
{{/fieldIsStruct}}
......@@ -90,6 +93,11 @@ public:
inline {{fieldType}} get{{fieldTitleCase}}();
inline void set{{fieldTitleCase}}({{fieldType}} value);
{{/fieldIsPrimitive}}
{{#fieldIsBlob}}
inline {{fieldType}}::Builder get{{fieldTitleCase}}();
inline void set{{fieldTitleCase}}({{fieldType}}::Reader value);
inline {{fieldType}}::Builder init{{fieldTitleCase}}(unsigned int size);
{{/fieldIsBlob}}
{{#fieldIsStruct}}
inline {{fieldType}}::Builder init{{fieldTitleCase}}();
inline {{fieldType}}::Builder get{{fieldTitleCase}}();
......@@ -117,12 +125,22 @@ inline {{fieldType}} {{structName}}::Reader::get{{fieldTitleCase}}() {
{{fieldOffset}} * ::capnproto::ELEMENTS, {{fieldDefaultValue}});
}
{{/fieldIsPrimitive}}
{{#fieldIsBlob}}
inline {{fieldType}}::Reader {{structName}}::Reader::get{{fieldTitleCase}}() {
return _reader.get{{fieldBlobType}}Field(
{{fieldOffset}} * ::capnproto::REFERENCES,
{{#fieldDefaultBytes}}
DEFAULT_{{fieldUpperCase}}.words, {{defaultBlobSize}} * ::capnproto::BYTES
{{/fieldDefaultBytes}}
{{^fieldDefaultBytes}}nullptr, 0 * ::capnproto::BYTES{{/fieldDefaultBytes}});
}
{{/fieldIsBlob}}
{{#fieldIsStruct}}
inline {{fieldType}}::Reader {{structName}}::Reader::get{{fieldTitleCase}}() {
{{! TODO: Support per-field default values. }}
return {{fieldType}}::Reader(_reader.getStructField(
{{fieldOffset}} * ::capnproto::REFERENCES,
{{#fieldDefaultBytes}}DEFAULT_{{fieldUpperCase}}{{/fieldDefaultBytes}}
{{#fieldDefaultBytes}}DEFAULT_{{fieldUpperCase}}.words{{/fieldDefaultBytes}}
{{^fieldDefaultBytes}}{{fieldType}}::DEFAULT.words{{/fieldDefaultBytes}}));
}
{{/fieldIsStruct}}
......@@ -150,6 +168,22 @@ inline void {{structName}}::Builder::set{{fieldTitleCase}}({{fieldType}} value)
{{fieldOffset}} * ::capnproto::ELEMENTS, value);
}
{{/fieldIsPrimitive}}
{{#fieldIsBlob}}
inline {{fieldType}}::Builder {{structName}}::Builder::get{{fieldTitleCase}}() {
return _builder.get{{fieldBlobType}}Field({{fieldOffset}} * ::capnproto::REFERENCES,
{{#fieldDefaultBytes}}
DEFAULT_{{fieldUpperCase}}.words, {{defaultBlobSize}} * ::capnproto::BYTES
{{/fieldDefaultBytes}}
{{^fieldDefaultBytes}}nullptr, 0 * ::capnproto::BYTES{{/fieldDefaultBytes}});
}
inline void {{structName}}::Builder::set{{fieldTitleCase}}({{fieldType}}::Reader value) {
_builder.set{{fieldBlobType}}Field({{fieldOffset}} * ::capnproto::REFERENCES, value);
}
inline {{fieldType}}::Builder {{structName}}::Builder::init{{fieldTitleCase}}(unsigned int size) {
return _builder.init{{fieldBlobType}}Field(
{{fieldOffset}} * ::capnproto::REFERENCES, size * ::capnproto::BYTES);
}
{{/fieldIsBlob}}
{{#fieldIsStruct}}
inline {{fieldType}}::Builder {{structName}}::Builder::init{{fieldTitleCase}}() {
return {{fieldType}}::Builder(_builder.initStructField(
......@@ -159,7 +193,7 @@ inline {{fieldType}}::Builder {{structName}}::Builder::get{{fieldTitleCase}}() {
{{! TODO: Support per-field default values. }}
return {{fieldType}}::Builder(_builder.getStructField(
{{fieldOffset}} * ::capnproto::REFERENCES,
{{#fieldDefaultBytes}}DEFAULT_{{fieldUpperCase}}{{/fieldDefaultBytes}}
{{#fieldDefaultBytes}}DEFAULT_{{fieldUpperCase}}.words{{/fieldDefaultBytes}}
{{^fieldDefaultBytes}}{{fieldType}}::DEFAULT.words{{/fieldDefaultBytes}}));
}
{{/fieldIsStruct}}
......
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