Commit 5dcbfe3a authored by Kenton Varda's avatar Kenton Varda

Replace most CAPNPROTO_ASSERTs with the new logging/error macros. Also add a…

Replace most CAPNPROTO_ASSERTs with the new logging/error macros.  Also add a SYSCALL macro and use it.  Also add commentary.
parent bdc568b8
...@@ -21,8 +21,10 @@ ...@@ -21,8 +21,10 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define CAPNPROTO_PRIVATE
#include "arena.h" #include "arena.h"
#include "message.h" #include "message.h"
#include "logging.h"
#include <vector> #include <vector>
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
...@@ -170,9 +172,9 @@ ArrayPtr<const ArrayPtr<const word>> BuilderArena::getSegmentsForOutput() { ...@@ -170,9 +172,9 @@ ArrayPtr<const ArrayPtr<const word>> BuilderArena::getSegmentsForOutput() {
return arrayPtr(&segment0ForOutput, 1); return arrayPtr(&segment0ForOutput, 1);
} }
} else { } else {
CAPNPROTO_DEBUG_ASSERT(moreSegments->forOutput.size() == moreSegments->builders.size() + 1, DCHECK(moreSegments->forOutput.size() == moreSegments->builders.size() + 1,
"Bug in capnproto::internal::BuilderArena: moreSegments->forOutput wasn't resized " "moreSegments->forOutput wasn't resized correctly when the last builder was added.",
"correctly when the last builder was added."); moreSegments->forOutput.size(), moreSegments->builders.size());
ArrayPtr<ArrayPtr<const word>> result( ArrayPtr<ArrayPtr<const word>> result(
&moreSegments->forOutput[0], moreSegments->forOutput.size()); &moreSegments->forOutput[0], moreSegments->forOutput.size());
......
...@@ -24,9 +24,11 @@ ...@@ -24,9 +24,11 @@
#ifndef CAPNPROTO_BENCHMARK_CAPNPROTO_COMMON_H_ #ifndef CAPNPROTO_BENCHMARK_CAPNPROTO_COMMON_H_
#define CAPNPROTO_BENCHMARK_CAPNPROTO_COMMON_H_ #define CAPNPROTO_BENCHMARK_CAPNPROTO_COMMON_H_
#define CAPNPROTO_PRIVATE
#include "common.h" #include "common.h"
#include <capnproto/serialize.h> #include <capnproto/serialize.h>
#include <capnproto/serialize-packed.h> #include <capnproto/serialize-packed.h>
#include <capnproto/logging.h>
#if HAVE_SNAPPY #if HAVE_SNAPPY
#include <capnproto/serialize-snappy.h> #include <capnproto/serialize-snappy.h>
#endif // HAVE_SNAPPY #endif // HAVE_SNAPPY
...@@ -179,7 +181,7 @@ struct UseScratch { ...@@ -179,7 +181,7 @@ struct UseScratch {
word* words; word* words;
ScratchSpace() { ScratchSpace() {
CAPNPROTO_ASSERT(scratchCounter < 6, "Too many scratch spaces needed at once."); PRECOND(scratchCounter < 6, "Too many scratch spaces needed at once.");
words = scratchSpace + scratchCounter++ * SCRATCH_SIZE; words = scratchSpace + scratchCounter++ * SCRATCH_SIZE;
} }
~ScratchSpace() { ~ScratchSpace() {
......
...@@ -72,7 +72,7 @@ public: ...@@ -72,7 +72,7 @@ public:
inline const char operator[](uint index) const { return bytes[index]; } inline const char operator[](uint index) const { return bytes[index]; }
inline Reader slice(uint start, uint end) const { inline Reader slice(uint start, uint end) const {
CAPNPROTO_DEBUG_ASSERT(start <= end && end <= size_, "Out-of-bounds slice."); CAPNPROTO_INLINE_DPRECOND(start <= end && end <= size_, "Out-of-bounds slice.");
return Reader(bytes + start, end - start); return Reader(bytes + start, end - start);
} }
...@@ -110,20 +110,19 @@ class Text::Reader: public Data::Reader { ...@@ -110,20 +110,19 @@ class Text::Reader: public Data::Reader {
public: public:
inline Reader(): Data::Reader("", 0) {} inline Reader(): Data::Reader("", 0) {}
inline Reader(const char* text): Data::Reader(text, strlen(text)) { inline Reader(const char* text): Data::Reader(text, strlen(text)) {
CAPNPROTO_DEBUG_ASSERT(text[size()] == '\0', "Text must be NUL-terminated."); CAPNPROTO_INLINE_DPRECOND(text[size()] == '\0', "Text must be NUL-terminated.");
} }
inline Reader(char* text): Data::Reader(text, strlen(text)) { inline Reader(char* text): Data::Reader(text, strlen(text)) {
CAPNPROTO_DEBUG_ASSERT(text[size()] == '\0', "Text must be NUL-terminated."); CAPNPROTO_INLINE_DPRECOND(text[size()] == '\0', "Text must be NUL-terminated.");
} }
inline Reader(const char* text, uint size): Data::Reader(text, size) { inline Reader(const char* text, uint size): Data::Reader(text, size) {
CAPNPROTO_DEBUG_ASSERT(text[size] == '\0', "Text must be NUL-terminated."); CAPNPROTO_INLINE_DPRECOND(text[size] == '\0', "Text must be NUL-terminated.");
} }
template <typename T> template <typename T>
inline Reader(const T& other): Data::Reader(other.c_str(), other.size()) { inline Reader(const T& other): Data::Reader(other.c_str(), other.size()) {
// Primarily intended for converting from std::string. // Primarily intended for converting from std::string.
CAPNPROTO_DEBUG_ASSERT(data()[size()] == '\0', CAPNPROTO_INLINE_DPRECOND(data()[size()] == '\0', "Text must be NUL-terminated.");
"Text must be NUL-terminated.");
} }
inline const char* c_str() const { return data(); } inline const char* c_str() const { return data(); }
...@@ -153,7 +152,7 @@ public: ...@@ -153,7 +152,7 @@ public:
inline char& operator[](uint index) const { return bytes[index]; } inline char& operator[](uint index) const { return bytes[index]; }
inline Builder slice(uint start, uint end) const { inline Builder slice(uint start, uint end) const {
CAPNPROTO_DEBUG_ASSERT(start <= end && end <= size_, "Out-of-bounds slice."); CAPNPROTO_INLINE_DPRECOND(start <= end && end <= size_, "Out-of-bounds slice.");
return Builder(bytes + start, end - start); return Builder(bytes + start, end - start);
} }
...@@ -172,7 +171,7 @@ public: ...@@ -172,7 +171,7 @@ public:
template <typename T> template <typename T>
inline void copyFrom(const T& other) const { inline void copyFrom(const T& other) const {
CAPNPROTO_DEBUG_ASSERT(size() == other.size(), "Sizes must match to copy."); CAPNPROTO_INLINE_DPRECOND(size() == other.size(), "Sizes must match to copy.");
memcpy(bytes, other.data(), other.size()); memcpy(bytes, other.data(), other.size());
} }
inline void copyFrom(const void* other) const { inline void copyFrom(const void* other) const {
......
...@@ -21,8 +21,10 @@ ...@@ -21,8 +21,10 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define CAPNPROTO_PRIVATE
#include "test.capnp.h" #include "test.capnp.h"
#include "message.h" #include "message.h"
#include "logging.h"
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include "test-util.h" #include "test-util.h"
...@@ -177,7 +179,7 @@ UnionState initUnion(void (TestUnion::Builder::*setter)(T value)) { ...@@ -177,7 +179,7 @@ UnionState initUnion(void (TestUnion::Builder::*setter)(T value)) {
(builder.getRoot<TestUnion>().*setter)(one<T>()); (builder.getRoot<TestUnion>().*setter)(one<T>());
ArrayPtr<const word> segment = builder.getSegmentsForOutput()[0]; ArrayPtr<const word> segment = builder.getSegmentsForOutput()[0];
CAPNPROTO_ASSERT(segment.size() > 2, "bug"); CHECK(segment.size() > 2, segment.size());
// Find the offset of the first set bit after the union discriminants. // Find the offset of the first set bit after the union discriminants.
int offset = 0; int offset = 0;
......
...@@ -34,6 +34,7 @@ ArrayPtr<const char> operator*(const Stringifier&, Exception::Nature nature) { ...@@ -34,6 +34,7 @@ ArrayPtr<const char> operator*(const Stringifier&, Exception::Nature nature) {
"precondition not met", "precondition not met",
"bug in code", "bug in code",
"invalid input data", "invalid input data",
"error from OS",
"network failure", "network failure",
"error" "error"
}; };
...@@ -96,15 +97,19 @@ ExceptionCallback::~ExceptionCallback() { ...@@ -96,15 +97,19 @@ ExceptionCallback::~ExceptionCallback() {
void ExceptionCallback::onRecoverableException(Exception&& exception) { void ExceptionCallback::onRecoverableException(Exception&& exception) {
#if __GNUC__ && !__EXCEPTIONS #if __GNUC__ && !__EXCEPTIONS
Log::writeRaw(str(exception.what(), '\n')); logMessage(str(exception.what(), '\n'));
#else #else
throw std::move(exception); if (std::uncaught_exception()) {
logMessage(str("unwind: ", exception.what(), '\n'));
} else {
throw std::move(exception);
}
#endif #endif
} }
void ExceptionCallback::onFatalException(Exception&& exception) { void ExceptionCallback::onFatalException(Exception&& exception) {
#if __GNUC__ && !__EXCEPTIONS #if __GNUC__ && !__EXCEPTIONS
Log::writeRaw(str(exception.what(), '\n')); logMessage(str(exception.what(), '\n'));
#else #else
throw std::move(exception); throw std::move(exception);
#endif #endif
......
...@@ -32,6 +32,12 @@ namespace capnproto { ...@@ -32,6 +32,12 @@ namespace capnproto {
class Exception: public std::exception { class Exception: public std::exception {
// Exception thrown in case of fatal errors. // Exception thrown in case of fatal errors.
#ifdef __CDT_PARSER__
// For some reason Eclipse gets confused by the definition of Nature if it's the first thing
// in the class.
typedef void WorkAroundCdtBug;
#endif
public: public:
enum class Nature { enum class Nature {
// What kind of failure? This is informational, not intended for programmatic use. // What kind of failure? This is informational, not intended for programmatic use.
...@@ -42,6 +48,7 @@ public: ...@@ -42,6 +48,7 @@ public:
PRECONDITION, PRECONDITION,
LOCAL_BUG, LOCAL_BUG,
INPUT, INPUT,
OS_ERROR,
NETWORK_FAILURE, NETWORK_FAILURE,
OTHER OTHER
......
...@@ -21,25 +21,15 @@ ...@@ -21,25 +21,15 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define CAPNPROTO_PRIVATE
#include "io.h" #include "io.h"
#include <errno.h> #include "logging.h"
#include <unistd.h> #include <unistd.h>
#include <sys/uio.h> #include <sys/uio.h>
#include <string> #include <string>
#include <string.h>
namespace capnproto { namespace capnproto {
class PrematureEofException: public std::exception {
public:
PrematureEofException() {}
~PrematureEofException() noexcept {}
const char* what() const noexcept override {
return "Stream ended prematurely.";
}
};
InputStream::~InputStream() {} InputStream::~InputStream() {}
OutputStream::~OutputStream() {} OutputStream::~OutputStream() {}
BufferedInputStream::~BufferedInputStream() {} BufferedInputStream::~BufferedInputStream() {}
...@@ -198,17 +188,18 @@ ArrayPtr<const byte> ArrayInputStream::getReadBuffer() { ...@@ -198,17 +188,18 @@ ArrayPtr<const byte> ArrayInputStream::getReadBuffer() {
size_t ArrayInputStream::read(void* dst, size_t minBytes, size_t maxBytes) { size_t ArrayInputStream::read(void* dst, size_t minBytes, size_t maxBytes) {
size_t n = std::min(maxBytes, array.size()); size_t n = std::min(maxBytes, array.size());
if (n < minBytes) { size_t result = n;
throw PrematureEofException(); VALIDATE_INPUT(n >= minBytes, "ArrayInputStream ended prematurely.") {
result = minBytes; // garbage
} }
memcpy(dst, array.begin(), n); memcpy(dst, array.begin(), n);
array = array.slice(n, array.size()); array = array.slice(n, array.size());
return n; return result;
} }
void ArrayInputStream::skip(size_t bytes) { void ArrayInputStream::skip(size_t bytes) {
if (array.size() < bytes) { VALIDATE_INPUT(array.size() >= bytes, "ArrayInputStream ended prematurely.") {
throw PrematureEofException(); bytes = array.size();
} }
array = array.slice(bytes, array.size()); array = array.slice(bytes, array.size());
} }
...@@ -227,8 +218,8 @@ void ArrayOutputStream::write(const void* src, size_t size) { ...@@ -227,8 +218,8 @@ void ArrayOutputStream::write(const void* src, size_t size) {
// Oh goody, the caller wrote directly into our buffer. // Oh goody, the caller wrote directly into our buffer.
fillPos += size; fillPos += size;
} else { } else {
CAPNPROTO_ASSERT(size <= (size_t)(array.end() - fillPos), PRECOND(size <= (size_t)(array.end() - fillPos),
"ArrayOutputStream's array is not big enough."); "ArrayOutputStream's backing array was not large enough for the data written.");
memcpy(fillPos, src, size); memcpy(fillPos, src, size);
fillPos += size; fillPos += size;
} }
...@@ -236,31 +227,9 @@ void ArrayOutputStream::write(const void* src, size_t size) { ...@@ -236,31 +227,9 @@ void ArrayOutputStream::write(const void* src, size_t size) {
// ======================================================================================= // =======================================================================================
class OsException: public std::exception {
public:
OsException(const char* function, int error) {
char buffer[256];
message = function;
message += ": ";
message.append(strerror_r(error, buffer, sizeof(buffer)));
}
~OsException() noexcept {}
const char* what() const noexcept override {
return message.c_str();
}
private:
std::string message;
};
AutoCloseFd::~AutoCloseFd() { AutoCloseFd::~AutoCloseFd() {
if (fd >= 0 && close(fd) < 0) { if (fd >= 0 && close(fd) < 0) {
if (std::uncaught_exception()) { FAIL_RECOVERABLE_SYSCALL("close", errno, fd);
// TODO: Devise some way to report secondary errors during unwind.
} else {
throw OsException("close", errno);
}
} }
} }
...@@ -272,21 +241,10 @@ size_t FdInputStream::read(void* buffer, size_t minBytes, size_t maxBytes) { ...@@ -272,21 +241,10 @@ size_t FdInputStream::read(void* buffer, size_t minBytes, size_t maxBytes) {
byte* max = pos + maxBytes; byte* max = pos + maxBytes;
while (pos < min) { while (pos < min) {
ssize_t n = ::read(fd, pos, max - pos); ssize_t n = SYSCALL(::read(fd, pos, max - pos), fd);
if (n <= 0) { VALIDATE_INPUT(n > 0, "Premature EOF") {
if (n < 0) { return minBytes;
int error = errno;
if (error == EINTR) {
continue;
} else {
throw OsException("read", error);
}
} else if (n == 0) {
throw PrematureEofException();
}
return false;
} }
pos += n; pos += n;
} }
...@@ -299,16 +257,8 @@ void FdOutputStream::write(const void* buffer, size_t size) { ...@@ -299,16 +257,8 @@ void FdOutputStream::write(const void* buffer, size_t size) {
const char* pos = reinterpret_cast<const char*>(buffer); const char* pos = reinterpret_cast<const char*>(buffer);
while (size > 0) { while (size > 0) {
ssize_t n = ::write(fd, pos, size); ssize_t n = SYSCALL(::write(fd, pos, size), fd);
if (n <= 0) { CHECK(n > 0, "write() returned zero.");
CAPNPROTO_ASSERT(n < 0, "write() returned zero.");
int error = errno;
if (error == EINTR) {
continue;
} else {
throw OsException("write", error);
}
}
pos += n; pos += n;
size -= n; size -= n;
} }
...@@ -331,14 +281,8 @@ void FdOutputStream::write(ArrayPtr<const ArrayPtr<const byte>> pieces) { ...@@ -331,14 +281,8 @@ void FdOutputStream::write(ArrayPtr<const ArrayPtr<const byte>> pieces) {
} }
while (current < iov.end()) { while (current < iov.end()) {
ssize_t n = ::writev(fd, current, iov.end() - current); ssize_t n = SYSCALL(::writev(fd, current, iov.end() - current), fd);
CHECK(n > 0, "writev() returned zero.");
if (n <= 0) {
if (n <= 0) {
CAPNPROTO_ASSERT(n < 0, "write() returned zero.");
throw OsException("writev", errno);
}
}
while (static_cast<size_t>(n) >= current->iov_len) { while (static_cast<size_t>(n) >= current->iov_len) {
n -= current->iov_len; n -= current->iov_len;
......
...@@ -21,7 +21,9 @@ ...@@ -21,7 +21,9 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define CAPNPROTO_PRIVATE
#include "layout.h" #include "layout.h"
#include "logging.h"
#include "arena.h" #include "arena.h"
#include <string.h> #include <string.h>
#include <limits> #include <limits>
...@@ -96,12 +98,12 @@ struct WireReference { ...@@ -96,12 +98,12 @@ struct WireReference {
} }
CAPNPROTO_ALWAYS_INLINE(WordCount farPositionInSegment() const) { CAPNPROTO_ALWAYS_INLINE(WordCount farPositionInSegment() const) {
CAPNPROTO_DEBUG_ASSERT(kind() == FAR, DPRECOND(kind() == FAR,
"positionInSegment() should only be called on FAR references."); "positionInSegment() should only be called on FAR references.");
return (offsetAndKind.get() >> 3) * WORDS; return (offsetAndKind.get() >> 3) * WORDS;
} }
CAPNPROTO_ALWAYS_INLINE(bool isDoubleFar() const) { CAPNPROTO_ALWAYS_INLINE(bool isDoubleFar() const) {
CAPNPROTO_DEBUG_ASSERT(kind() == FAR, DPRECOND(kind() == FAR,
"isDoubleFar() should only be called on FAR references."); "isDoubleFar() should only be called on FAR references.");
return (offsetAndKind.get() >> 2) & 1; return (offsetAndKind.get() >> 2) & 1;
} }
...@@ -149,14 +151,12 @@ struct WireReference { ...@@ -149,14 +151,12 @@ struct WireReference {
} }
CAPNPROTO_ALWAYS_INLINE(void set(FieldSize es, ElementCount ec)) { CAPNPROTO_ALWAYS_INLINE(void set(FieldSize es, ElementCount ec)) {
CAPNPROTO_DEBUG_ASSERT(ec < (1 << 29) * ELEMENTS, DPRECOND(ec < (1 << 29) * ELEMENTS, "Lists are limited to 2**29 elements.");
"Lists are limited to 2**29 elements.");
elementSizeAndCount.set(((ec / ELEMENTS) << 3) | static_cast<int>(es)); elementSizeAndCount.set(((ec / ELEMENTS) << 3) | static_cast<int>(es));
} }
CAPNPROTO_ALWAYS_INLINE(void setInlineComposite(WordCount wc)) { CAPNPROTO_ALWAYS_INLINE(void setInlineComposite(WordCount wc)) {
CAPNPROTO_DEBUG_ASSERT(wc < (1 << 29) * WORDS, DPRECOND(wc < (1 << 29) * WORDS, "Inline composite lists are limited to 2**29 words.");
"Inline composite lists are limited to 2**29 words.");
elementSizeAndCount.set(((wc / WORDS) << 3) | elementSizeAndCount.set(((wc / WORDS) << 3) |
static_cast<int>(FieldSize::INLINE_COMPOSITE)); static_cast<int>(FieldSize::INLINE_COMPOSITE));
} }
...@@ -379,7 +379,7 @@ struct WireHelpers { ...@@ -379,7 +379,7 @@ struct WireHelpers {
const word* srcElement = srcPtr + REFERENCE_SIZE_IN_WORDS; const word* srcElement = srcPtr + REFERENCE_SIZE_IN_WORDS;
word* dstElement = dstPtr + REFERENCE_SIZE_IN_WORDS; word* dstElement = dstPtr + REFERENCE_SIZE_IN_WORDS;
CAPNPROTO_ASSERT(srcTag->kind() == WireReference::STRUCT, CHECK(srcTag->kind() == WireReference::STRUCT,
"INLINE_COMPOSITE of lists is not yet supported."); "INLINE_COMPOSITE of lists is not yet supported.");
uint n = srcTag->inlineCompositeListElementCount() / ELEMENTS; uint n = srcTag->inlineCompositeListElementCount() / ELEMENTS;
...@@ -395,7 +395,7 @@ struct WireHelpers { ...@@ -395,7 +395,7 @@ struct WireHelpers {
break; break;
} }
default: default:
CAPNPROTO_ASSERT(false, "Copy source message contained unexpected kind."); FAIL_PRECOND("Copy source message contained unexpected kind.");
break; break;
} }
...@@ -430,12 +430,12 @@ struct WireHelpers { ...@@ -430,12 +430,12 @@ struct WireHelpers {
} else { } else {
ptr = followFars(ref, segment); ptr = followFars(ref, segment);
CAPNPROTO_DEBUG_ASSERT(ref->kind() == WireReference::STRUCT, DPRECOND(ref->kind() == WireReference::STRUCT,
"Called getStruct{Field,Element}() but existing reference is not a struct."); "Called getStruct{Field,Element}() but existing reference is not a struct.");
CAPNPROTO_DEBUG_ASSERT( DPRECOND(
ref->structRef.dataSize.get() == size.data, ref->structRef.dataSize.get() == size.data,
"Trying to update struct with incorrect data size."); "Trying to update struct with incorrect data size.");
CAPNPROTO_DEBUG_ASSERT( DPRECOND(
ref->structRef.refCount.get() == size.pointers, ref->structRef.refCount.get() == size.pointers,
"Trying to update struct with incorrect reference count."); "Trying to update struct with incorrect reference count.");
} }
...@@ -446,7 +446,7 @@ struct WireHelpers { ...@@ -446,7 +446,7 @@ struct WireHelpers {
static CAPNPROTO_ALWAYS_INLINE(ListBuilder initListReference( static CAPNPROTO_ALWAYS_INLINE(ListBuilder initListReference(
WireReference* ref, SegmentBuilder* segment, ElementCount elementCount, WireReference* ref, SegmentBuilder* segment, ElementCount elementCount,
FieldSize elementSize)) { FieldSize elementSize)) {
CAPNPROTO_DEBUG_ASSERT(elementSize != FieldSize::INLINE_COMPOSITE, DPRECOND(elementSize != FieldSize::INLINE_COMPOSITE,
"Should have called initStructListReference() instead."); "Should have called initStructListReference() instead.");
// Calculate size of the list. // Calculate size of the list.
...@@ -499,14 +499,14 @@ struct WireHelpers { ...@@ -499,14 +499,14 @@ struct WireHelpers {
} else { } else {
ptr = followFars(ref, segment); ptr = followFars(ref, segment);
CAPNPROTO_ASSERT(ref->kind() == WireReference::LIST, PRECOND(ref->kind() == WireReference::LIST,
"Called getList{Field,Element}() but existing reference is not a list."); "Called getList{Field,Element}() but existing reference is not a list.");
} }
if (ref->listRef.elementSize() == FieldSize::INLINE_COMPOSITE) { if (ref->listRef.elementSize() == FieldSize::INLINE_COMPOSITE) {
// Read the tag to get the actual element count. // Read the tag to get the actual element count.
WireReference* tag = reinterpret_cast<WireReference*>(ptr); WireReference* tag = reinterpret_cast<WireReference*>(ptr);
CAPNPROTO_ASSERT(tag->kind() == WireReference::STRUCT, PRECOND(tag->kind() == WireReference::STRUCT,
"INLINE_COMPOSITE list with non-STRUCT elements not supported."); "INLINE_COMPOSITE list with non-STRUCT elements not supported.");
ElementCount elementCount = tag->inlineCompositeListElementCount(); ElementCount elementCount = tag->inlineCompositeListElementCount();
...@@ -547,9 +547,9 @@ struct WireHelpers { ...@@ -547,9 +547,9 @@ struct WireHelpers {
} else { } else {
word* ptr = followFars(ref, segment); word* ptr = followFars(ref, segment);
CAPNPROTO_ASSERT(ref->kind() == WireReference::LIST, PRECOND(ref->kind() == WireReference::LIST,
"Called getText{Field,Element}() but existing reference is not a list."); "Called getText{Field,Element}() but existing reference is not a list.");
CAPNPROTO_ASSERT(ref->listRef.elementSize() == FieldSize::BYTE, PRECOND(ref->listRef.elementSize() == FieldSize::BYTE,
"Called getText{Field,Element}() but existing list reference is not byte-sized."); "Called getText{Field,Element}() but existing list reference is not byte-sized.");
// Subtract 1 from the size for the NUL terminator. // Subtract 1 from the size for the NUL terminator.
...@@ -584,9 +584,9 @@ struct WireHelpers { ...@@ -584,9 +584,9 @@ struct WireHelpers {
} else { } else {
word* ptr = followFars(ref, segment); word* ptr = followFars(ref, segment);
CAPNPROTO_ASSERT(ref->kind() == WireReference::LIST, PRECOND(ref->kind() == WireReference::LIST,
"Called getData{Field,Element}() but existing reference is not a list."); "Called getData{Field,Element}() but existing reference is not a list.");
CAPNPROTO_ASSERT(ref->listRef.elementSize() == FieldSize::BYTE, PRECOND(ref->listRef.elementSize() == FieldSize::BYTE,
"Called getData{Field,Element}() but existing list reference is not byte-sized."); "Called getData{Field,Element}() but existing list reference is not byte-sized.");
return Data::Builder(reinterpret_cast<char*>(ptr), ref->listRef.elementCount() / ELEMENTS); return Data::Builder(reinterpret_cast<char*>(ptr), ref->listRef.elementCount() / ELEMENTS);
...@@ -810,14 +810,14 @@ struct WireHelpers { ...@@ -810,14 +810,14 @@ struct WireHelpers {
break; break;
case FieldSize::INLINE_COMPOSITE: case FieldSize::INLINE_COMPOSITE:
CAPNPROTO_ASSERT(false, "can't get here"); FAIL_CHECK();
break; break;
} }
return ListReader(segment, ptr, ref->listRef.elementCount(), step, return ListReader(segment, ptr, ref->listRef.elementCount(), step,
dataSize, referenceCount, nestingLimit - 1); dataSize, referenceCount, nestingLimit - 1);
} else { } else {
CAPNPROTO_ASSERT(segment != nullptr, "Trusted message had incompatible list element type."); PRECOND(segment != nullptr, "Trusted message had incompatible list element type.");
segment->getArena()->reportInvalidData("A list had incompatible element type."); segment->getArena()->reportInvalidData("A list had incompatible element type.");
goto useDefault; goto useDefault;
} }
...@@ -1102,7 +1102,7 @@ Data::Builder ListBuilder::getDataElement(WireReferenceCount index) const { ...@@ -1102,7 +1102,7 @@ Data::Builder ListBuilder::getDataElement(WireReferenceCount index) const {
ListReader ListBuilder::asReader(FieldSize elementSize) const { ListReader ListBuilder::asReader(FieldSize elementSize) const {
// TODO: For INLINE_COMPOSITE I suppose we could just check the tag? // TODO: For INLINE_COMPOSITE I suppose we could just check the tag?
CAPNPROTO_ASSERT(elementSize != FieldSize::INLINE_COMPOSITE, PRECOND(elementSize != FieldSize::INLINE_COMPOSITE,
"Need to call the other asReader() overload for INLINE_COMPOSITE lists."); "Need to call the other asReader() overload for INLINE_COMPOSITE lists.");
return ListReader(segment, ptr, elementCount, bitsPerElement(elementSize), return ListReader(segment, ptr, elementCount, bitsPerElement(elementSize),
std::numeric_limits<int>::max()); std::numeric_limits<int>::max());
......
...@@ -193,10 +193,12 @@ struct List<T, true> { ...@@ -193,10 +193,12 @@ struct List<T, true> {
for (; i != end && pos < size(); ++i) { for (; i != end && pos < size(); ++i) {
set(pos, *i); set(pos, *i);
} }
CAPNPROTO_DEBUG_ASSERT(pos == size() && i == end, "copyFrom() argument had different size."); CAPNPROTO_INLINE_DPRECOND(pos == size() && i == end,
"List::copyFrom() argument had different size.");
} }
void copyFrom(std::initializer_list<T> other) { void copyFrom(std::initializer_list<T> other) {
CAPNPROTO_DEBUG_ASSERT(other.size() == size(), "copyFrom() argument had different size."); CAPNPROTO_INLINE_DPRECOND(other.size() == size(),
"List::copyFrom() argument had different size.");
for (uint i = 0; i < other.size(); i++) { for (uint i = 0; i < other.size(); i++) {
set(i, other.begin()[i]); set(i, other.begin()[i]);
} }
...@@ -398,10 +400,12 @@ struct List<Data, false> { ...@@ -398,10 +400,12 @@ struct List<Data, false> {
for (; i != end && pos < size(); ++i) { for (; i != end && pos < size(); ++i) {
set(pos, *i); set(pos, *i);
} }
CAPNPROTO_DEBUG_ASSERT(pos == size() && i == end, "copyFrom() argument had different size."); CAPNPROTO_INLINE_DPRECOND(pos == size() && i == end,
"List::copyFrom() argument had different size.");
} }
void copyFrom(std::initializer_list<Data::Reader> other) { void copyFrom(std::initializer_list<Data::Reader> other) {
CAPNPROTO_DEBUG_ASSERT(other.size() == size(), "copyFrom() argument had different size."); CAPNPROTO_INLINE_DPRECOND(other.size() == size(),
"List::copyFrom() argument had different size.");
for (uint i = 0; i < other.size(); i++) { for (uint i = 0; i < other.size(); i++) {
set(i, other.begin()[i]); set(i, other.begin()[i]);
} }
...@@ -460,10 +464,12 @@ struct List<Text, false> { ...@@ -460,10 +464,12 @@ struct List<Text, false> {
for (; i != end && pos < size(); ++i) { for (; i != end && pos < size(); ++i) {
set(pos, *i); set(pos, *i);
} }
CAPNPROTO_DEBUG_ASSERT(pos == size() && i == end, "copyFrom() argument had different size."); CAPNPROTO_INLINE_DPRECOND(pos == size() && i == end,
"List::copyFrom() argument had different size.");
} }
void copyFrom(std::initializer_list<Text::Reader> other) { void copyFrom(std::initializer_list<Text::Reader> other) {
CAPNPROTO_DEBUG_ASSERT(other.size() == size(), "copyFrom() argument had different size."); CAPNPROTO_INLINE_DPRECOND(other.size() == size(),
"List::copyFrom() argument had different size.");
for (uint i = 0; i < other.size(); i++) { for (uint i = 0; i < other.size(); i++) {
set(i, other.begin()[i]); set(i, other.begin()[i]);
} }
......
...@@ -27,6 +27,9 @@ ...@@ -27,6 +27,9 @@
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <string> #include <string>
#include <stdio.h> #include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
namespace capnproto { namespace capnproto {
namespace internal { namespace internal {
...@@ -111,6 +114,35 @@ TEST(Logging, Log) { ...@@ -111,6 +114,35 @@ TEST(Logging, Log) {
EXPECT_EQ("fatal exception: " + fileLine(__FILE__, line) + ": precondition not met: expected " EXPECT_EQ("fatal exception: " + fileLine(__FILE__, line) + ": precondition not met: expected "
"1 == 2; i = 123; hi; str = foo\n", mockCallback.text); "1 == 2; i = 123; hi; str = foo\n", mockCallback.text);
mockCallback.text.clear(); mockCallback.text.clear();
EXPECT_THROW(CHECK(false, "foo"), MockException); line = __LINE__;
EXPECT_EQ("fatal exception: " + fileLine(__FILE__, line) + ": bug in code: foo\n",
mockCallback.text);
mockCallback.text.clear();
}
TEST(Logging, Syscall) {
MockExceptionCallback mockCallback;
MockExceptionCallback::ScopedRegistration reg(&mockCallback);
int line;
int i = 123;
const char* str = "foo";
int fd = SYSCALL(dup(STDIN_FILENO));
SYSCALL(close(fd));
EXPECT_THROW(SYSCALL(close(fd), i, "bar", str), MockException); line = __LINE__;
EXPECT_EQ("fatal exception: " + fileLine(__FILE__, line) + ": error from OS: close(fd): "
+ strerror(EBADF) + "; i = 123; bar; str = foo\n", mockCallback.text);
mockCallback.text.clear();
int result = 0;
bool recovered = false;
RECOVERABLE_SYSCALL(result = close(fd), i, "bar", str) { recovered = true; } line = __LINE__;
EXPECT_EQ("recoverable exception: " + fileLine(__FILE__, line) + ": error from OS: close(fd): "
+ strerror(EBADF) + "; i = 123; bar; str = foo\n", mockCallback.text);
EXPECT_LT(result, 0);
EXPECT_TRUE(recovered);
} }
} // namespace } // namespace
......
...@@ -25,10 +25,12 @@ ...@@ -25,10 +25,12 @@
#include "logging.h" #include "logging.h"
#include <stdlib.h> #include <stdlib.h>
#include <ctype.h> #include <ctype.h>
#include <string.h>
#include <errno.h>
namespace capnproto { namespace capnproto {
Log::Severity Log::minSeverity = Log::Severity::WARNING; Log::Severity Log::minSeverity = Log::Severity::INFO;
ArrayPtr<const char> operator*(const Stringifier&, Log::Severity severity) { ArrayPtr<const char> operator*(const Stringifier&, Log::Severity severity) {
static const char* SEVERITY_STRINGS[] = { static const char* SEVERITY_STRINGS[] = {
...@@ -42,8 +44,16 @@ ArrayPtr<const char> operator*(const Stringifier&, Log::Severity severity) { ...@@ -42,8 +44,16 @@ ArrayPtr<const char> operator*(const Stringifier&, Log::Severity severity) {
return arrayPtr(s, strlen(s)); return arrayPtr(s, strlen(s));
} }
static Array<char> makeDescription(const char* condition, const char* macroArgs, namespace {
ArrayPtr<Array<char>> argValues) {
enum DescriptionStyle {
LOG,
ASSERTION,
SYSCALL
};
static Array<char> makeDescription(DescriptionStyle style, const char* code, int errorNumber,
const char* macroArgs, ArrayPtr<Array<char>> argValues) {
ArrayPtr<const char> argNames[argValues.size()]; ArrayPtr<const char> argNames[argValues.size()];
if (argValues.size() > 0) { if (argValues.size() > 0) {
...@@ -89,18 +99,58 @@ static Array<char> makeDescription(const char* condition, const char* macroArgs, ...@@ -89,18 +99,58 @@ static Array<char> makeDescription(const char* condition, const char* macroArgs,
} }
} }
if (style == SYSCALL) {
// Strip off leading "foo = " from code, since callers will sometimes write things like:
// ssize_t n;
// RECOVERABLE_SYSCALL(n = read(fd, buffer, sizeof(buffer))) { return ""; }
// return std::string(buffer, n);
const char* equalsPos = strchr(code, '=');
if (equalsPos != nullptr && equalsPos[1] != '=') {
code = equalsPos + 1;
while (isspace(*code)) ++code;
}
}
{ {
ArrayPtr<const char> expected = arrayPtr("expected "); ArrayPtr<const char> expected = arrayPtr("expected ");
ArrayPtr<const char> conditionArray = condition == nullptr ? nullptr : arrayPtr(condition); ArrayPtr<const char> codeArray = style == LOG ? nullptr : arrayPtr(code);
ArrayPtr<const char> sep = arrayPtr(" = "); ArrayPtr<const char> sep = arrayPtr(" = ");
ArrayPtr<const char> delim = arrayPtr("; "); ArrayPtr<const char> delim = arrayPtr("; ");
ArrayPtr<const char> colon = arrayPtr(": ");
if (style == ASSERTION && strcmp(code, "false") == 0) {
// Don't print "expected false", that's silly.
style = LOG;
}
ArrayPtr<const char> sysErrorArray;
#if __USE_GNU
char buffer[256];
if (style == SYSCALL) {
sysErrorArray = arrayPtr(strerror_r(errorNumber, buffer, sizeof(buffer)));
}
#else
// TODO: Other unixes should have strerror_r but it may have a different signature. Port for
// thread-safety.
sysErrorArray = arrayPtr(strerror(errorNumber));
#endif
size_t totalSize = 0; size_t totalSize = 0;
if (condition != nullptr) { switch (style) {
totalSize += expected.size() + conditionArray.size(); case LOG:
break;
case ASSERTION:
totalSize += expected.size() + codeArray.size();
break;
case SYSCALL:
totalSize += codeArray.size() + colon.size() + sysErrorArray.size();
break;
} }
for (size_t i = 0; i < argValues.size(); i++) { for (size_t i = 0; i < argValues.size(); i++) {
if (i > 0 || condition != nullptr) totalSize += delim.size(); if (i > 0 || style != LOG) {
totalSize += delim.size();
}
if (argNames[i].size() > 0 && argNames[i][0] != '\"') { if (argNames[i].size() > 0 && argNames[i][0] != '\"') {
totalSize += argNames[i].size() + sep.size(); totalSize += argNames[i].size() + sep.size();
} }
...@@ -109,12 +159,23 @@ static Array<char> makeDescription(const char* condition, const char* macroArgs, ...@@ -109,12 +159,23 @@ static Array<char> makeDescription(const char* condition, const char* macroArgs,
ArrayBuilder<char> result(totalSize); ArrayBuilder<char> result(totalSize);
if (condition != nullptr) { switch (style) {
result.addAll(expected); case LOG:
result.addAll(conditionArray); break;
case ASSERTION:
result.addAll(expected);
result.addAll(codeArray);
break;
case SYSCALL:
result.addAll(codeArray);
result.addAll(colon);
result.addAll(sysErrorArray);
break;
} }
for (size_t i = 0; i < argValues.size(); i++) { for (size_t i = 0; i < argValues.size(); i++) {
if (i > 0 || condition != nullptr) result.addAll(delim); if (i > 0 || style != LOG) {
result.addAll(delim);
}
if (argNames[i].size() > 0 && argNames[i][0] != '\"') { if (argNames[i].size() > 0 && argNames[i][0] != '\"') {
result.addAll(argNames[i]); result.addAll(argNames[i]);
result.addAll(sep); result.addAll(sep);
...@@ -126,11 +187,13 @@ static Array<char> makeDescription(const char* condition, const char* macroArgs, ...@@ -126,11 +187,13 @@ static Array<char> makeDescription(const char* condition, const char* macroArgs,
} }
} }
} // namespace
void Log::logInternal(const char* file, int line, Severity severity, const char* macroArgs, void Log::logInternal(const char* file, int line, Severity severity, const char* macroArgs,
ArrayPtr<Array<char>> argValues) { ArrayPtr<Array<char>> argValues) {
getExceptionCallback()->logMessage( getExceptionCallback()->logMessage(
str(severity, ": ", file, ":", line, ": ", str(severity, ": ", file, ":", line, ": ",
makeDescription(nullptr, macroArgs, argValues), '\n')); makeDescription(LOG, nullptr, 0, macroArgs, argValues), '\n'));
} }
void Log::recoverableFaultInternal( void Log::recoverableFaultInternal(
...@@ -138,7 +201,7 @@ void Log::recoverableFaultInternal( ...@@ -138,7 +201,7 @@ void Log::recoverableFaultInternal(
const char* condition, const char* macroArgs, ArrayPtr<Array<char>> argValues) { const char* condition, const char* macroArgs, ArrayPtr<Array<char>> argValues) {
getExceptionCallback()->onRecoverableException( getExceptionCallback()->onRecoverableException(
Exception(nature, Exception::Durability::PERMANENT, file, line, Exception(nature, Exception::Durability::PERMANENT, file, line,
makeDescription(condition, macroArgs, argValues))); makeDescription(ASSERTION, condition, 0, macroArgs, argValues)));
} }
void Log::fatalFaultInternal( void Log::fatalFaultInternal(
...@@ -146,8 +209,30 @@ void Log::fatalFaultInternal( ...@@ -146,8 +209,30 @@ void Log::fatalFaultInternal(
const char* condition, const char* macroArgs, ArrayPtr<Array<char>> argValues) { const char* condition, const char* macroArgs, ArrayPtr<Array<char>> argValues) {
getExceptionCallback()->onFatalException( getExceptionCallback()->onFatalException(
Exception(nature, Exception::Durability::PERMANENT, file, line, Exception(nature, Exception::Durability::PERMANENT, file, line,
makeDescription(condition, macroArgs, argValues))); makeDescription(ASSERTION, condition, 0, macroArgs, argValues)));
abort(); abort();
} }
void Log::recoverableFailedSyscallInternal(
const char* file, int line, const char* call,
int errorNumber, const char* macroArgs, ArrayPtr<Array<char>> argValues) {
getExceptionCallback()->onRecoverableException(
Exception(Exception::Nature::OS_ERROR, Exception::Durability::PERMANENT, file, line,
makeDescription(SYSCALL, call, errorNumber, macroArgs, argValues)));
}
void Log::fatalFailedSyscallInternal(
const char* file, int line, const char* call,
int errorNumber, const char* macroArgs, ArrayPtr<Array<char>> argValues) {
getExceptionCallback()->onFatalException(
Exception(Exception::Nature::OS_ERROR, Exception::Durability::PERMANENT, file, line,
makeDescription(SYSCALL, call, errorNumber, macroArgs, argValues)));
abort();
}
int Log::getOsErrorNumber() {
int result = errno;
return result == EINTR ? -1 : result;
}
} // namespace capnproto } // namespace capnproto
...@@ -21,6 +21,70 @@ ...@@ -21,6 +21,70 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// This file declares convenient macros for debug logging and error handling. The macros make
// it excessively easy to extract useful context information from code. Example:
//
// CHECK(a == b, a, b, "a and b must be the same.");
//
// On failure, this will throw an exception whose description looks like:
//
// myfile.c++:43: bug in code: expected a == b; a = 14; b = 72; a and b must be the same.
//
// As you can see, all arguments after the first provide additional context.
//
// The macros available are:
//
// * `LOG(severity, ...)`: Just writes a log message, to stderr by default (but you can intercept
// messages by implementing an ExceptionCallback). `severity` is `INFO`, `WARNING`, `ERROR`, or
// `FATAL`. If the severity is not higher than the global logging threshold, nothing will be
// written and in fact the log message won't even be evaluated.
//
// * `CHECK(condition, ...)`: Throws an exception if `condition` is false, or aborts if exceptions
// are disabled. This macro should be used to check for bugs in the surrounding code and its
// dependencies, but NOT to check for invalid input.
//
// * `PRECOND(condition, ...)`: Like `CHECK` but used to check preconditions -- i.e. to validate
// parameters passed from a caller. A failure indicates that the caller is buggy.
//
// * `RECOVERABLE_CHECK(condition, ...) { ... }`: Like `CHECK` except that if exceptions are
// disabled, instead of aborting, the following code block will be executed. This block should
// do whatever it can to fill in dummy values so that the code can continue executing, even if
// this means the eventual output will be garbage.
//
// * `RECOVERABLE_PRECOND(condition, ...) { ... }`: Like `RECOVERABLE_CHECK` and `PRECOND`.
//
// * `VALIDATE_INPUT(condition, ...) { ... }`: Like `RECOVERABLE_PRECOND` but used to validate
// input that may have come from the user or some other untrusted source. Recoverability is
// required in this case.
//
// * `SYSCALL(code, ...)`: Executes `code` assuming it makes a system call. A negative return
// value is considered an error. EINTR is handled by retrying. Other errors are handled by
// throwing an exception. The macro also returns the call's result. For example, the following
// calls `open()` and includes the file name in any error message:
//
// int fd = SYSCALL(open(filename, O_RDONLY), filename);
//
// * `RECOVERABLE_SYSCALL(code, ...) { ... }`: Like `RECOVERABLE_CHECK` and `SYSCALL`. Note that
// unfortunately this macro cannot return a value since it implements control flow, but you can
// assign to a variable *inside* the parameter instead:
//
// int fd;
// RECOVERABLE_SYSCALL(fd = open(filename, O_RDONLY), filename) {
// // Failed. Open /dev/null instead.
// fd = SYSCALL(open("/dev/null", O_RDONLY));
// }
//
// Notes:
// * Do not write expressions with side-effects in the message content part of the macro, as the
// message will not necessarily be evaluated.
// * For every macro `FOO` above except `LOG`, there is also a `FAIL_FOO` macro used to report
// failures that already happened. For the macros that check a boolean condition, `FAIL_FOO`
// omits the first parameter and behaves like it was `false`. `FAIL_SYSCALL` and
// `FAIL_RECOVERABLE_SYSCALL` take a string and an OS error number as the first two parameters.
// The string should be the name of the failed system call.
// * For every macro `FOO` above, there is a `DFOO` version (or `RECOVERABLE_DFOO`) which is only
// executed in debug mode. When `NDEBUG` is defined, these macros expand to nothing.
#ifndef CAPNPROTO_LOGGING_H_ #ifndef CAPNPROTO_LOGGING_H_
#define CAPNPROTO_LOGGING_H_ #define CAPNPROTO_LOGGING_H_
...@@ -45,7 +109,10 @@ public: ...@@ -45,7 +109,10 @@ public:
}; };
static inline bool shouldLog(Severity severity) { return severity >= minSeverity; } static inline bool shouldLog(Severity severity) { return severity >= minSeverity; }
// Returns whether messages of the given severity should be logged.
static inline void setLogLevel(Severity severity) { minSeverity = severity; } static inline void setLogLevel(Severity severity) { minSeverity = severity; }
// Set the minimum message severity which will be logged.
template <typename... Params> template <typename... Params>
static void log(const char* file, int line, Severity severity, const char* macroArgs, static void log(const char* file, int line, Severity severity, const char* macroArgs,
...@@ -60,6 +127,24 @@ public: ...@@ -60,6 +127,24 @@ public:
const char* condition, const char* macroArgs, Params&&... params) const char* condition, const char* macroArgs, Params&&... params)
CAPNPROTO_NORETURN; CAPNPROTO_NORETURN;
template <typename Call, typename... Params>
static bool recoverableSyscall(Call&& call, const char* file, int line, const char* callText,
const char* macroArgs, Params&&... params);
template <typename Call, typename... Params>
static auto syscall(Call&& call, const char* file, int line, const char* callText,
const char* macroArgs, Params&&... params) -> decltype(call());
template <typename... Params>
static void reportFailedRecoverableSyscall(
int errorNumber, const char* file, int line, const char* callText,
const char* macroArgs, Params&&... params);
template <typename... Params>
static void reportFailedSyscall(
int errorNumber, const char* file, int line, const char* callText,
const char* macroArgs, Params&&... params);
private: private:
static Severity minSeverity; static Severity minSeverity;
...@@ -72,6 +157,16 @@ private: ...@@ -72,6 +157,16 @@ private:
const char* file, int line, Exception::Nature nature, const char* file, int line, Exception::Nature nature,
const char* condition, const char* macroArgs, ArrayPtr<Array<char>> argValues) const char* condition, const char* macroArgs, ArrayPtr<Array<char>> argValues)
CAPNPROTO_NORETURN; CAPNPROTO_NORETURN;
static void recoverableFailedSyscallInternal(
const char* file, int line, const char* call,
int errorNumber, const char* macroArgs, ArrayPtr<Array<char>> argValues);
static void fatalFailedSyscallInternal(
const char* file, int line, const char* call,
int errorNumber, const char* macroArgs, ArrayPtr<Array<char>> argValues)
CAPNPROTO_NORETURN;
static int getOsErrorNumber();
// Get the error code of the last error (e.g. from errno). Returns -1 on EINTR.
}; };
ArrayPtr<const char> operator*(const Stringifier&, Log::Severity severity); ArrayPtr<const char> operator*(const Stringifier&, Log::Severity severity);
...@@ -98,6 +193,37 @@ ArrayPtr<const char> operator*(const Stringifier&, Log::Severity severity); ...@@ -98,6 +193,37 @@ ArrayPtr<const char> operator*(const Stringifier&, Log::Severity severity);
#define RECOVERABLE_PRECOND(...) RECOVERABLE_FAULT(PRECONDITION, __VA_ARGS__) #define RECOVERABLE_PRECOND(...) RECOVERABLE_FAULT(PRECONDITION, __VA_ARGS__)
#define VALIDATE_INPUT(...) RECOVERABLE_FAULT(INPUT, __VA_ARGS__) #define VALIDATE_INPUT(...) RECOVERABLE_FAULT(INPUT, __VA_ARGS__)
#define FAIL_CHECK(...) CHECK(false, ##__VA_ARGS__)
#define FAIL_RECOVERABLE_CHECK(...) RECOVERABLE_CHECK(false, ##__VA_ARGS__)
#define FAIL_PRECOND(...) PRECOND(false, ##__VA_ARGS__)
#define FAIL_RECOVERABLE_PRECOND(...) RECOVERABLE_PRECOND(false, ##__VA_ARGS__)
#define FAIL_VALIDATE_INPUT(...) VALIDATE_INPUT(false, ##__VA_ARGS__)
#define SYSCALL(call, ...) \
::capnproto::Log::syscall( \
[&](){return (call);}, __FILE__, __LINE__, #call, #__VA_ARGS__, ##__VA_ARGS__)
#define RECOVERABLE_SYSCALL(call, ...) \
if (::capnproto::Log::recoverableSyscall( \
[&](){return (call);}, __FILE__, __LINE__, #call, #__VA_ARGS__, ##__VA_ARGS__)) {} \
else
#define FAIL_SYSCALL(code, errorNumber, ...) \
do { \
/* make sure to read error number before doing anything else that could change it */ \
int _errorNumber = errorNumber; \
::capnproto::Log::reportFailedSyscall( \
_errorNumber, __FILE__, __LINE__, #code, #__VA_ARGS__, ##__VA_ARGS__); \
} while (false)
#define FAIL_RECOVERABLE_SYSCALL(code, errorNumber, ...) \
do { \
/* make sure to read error number before doing anything else that could change it */ \
int _errorNumber = errorNumber; \
::capnproto::Log::reportFailedRecoverableSyscall( \
_errorNumber, __FILE__, __LINE__, #code, #__VA_ARGS__, ##__VA_ARGS__); \
} while (false)
#ifdef NDEBUG #ifdef NDEBUG
#define DLOG(...) do {} while (false) #define DLOG(...) do {} while (false)
#define DCHECK(...) do {} while (false) #define DCHECK(...) do {} while (false)
...@@ -135,6 +261,59 @@ void Log::fatalFault(const char* file, int line, Exception::Nature nature, ...@@ -135,6 +261,59 @@ void Log::fatalFault(const char* file, int line, Exception::Nature nature,
arrayPtr(argValues, sizeof...(Params))); arrayPtr(argValues, sizeof...(Params)));
} }
template <typename Call, typename... Params>
bool Log::recoverableSyscall(Call&& call, const char* file, int line, const char* callText,
const char* macroArgs, Params&&... params) {
int result;
while ((result = call()) < 0) {
int errorNum = getOsErrorNumber();
// getOsErrorNumber() returns -1 to indicate EINTR
if (errorNum != -1) {
Array<char> argValues[sizeof...(Params)] = {str(params)...};
recoverableFailedSyscallInternal(file, line, callText, errorNum,
macroArgs, arrayPtr(argValues, sizeof...(Params)));
return false;
}
}
return true;
}
#ifndef __CDT_PARSER__ // Eclipse dislikes the late return spec.
template <typename Call, typename... Params>
auto Log::syscall(Call&& call, const char* file, int line, const char* callText,
const char* macroArgs, Params&&... params) -> decltype(call()) {
decltype(call()) result;
while ((result = call()) < 0) {
int errorNum = getOsErrorNumber();
// getOsErrorNumber() returns -1 to indicate EINTR
if (errorNum != -1) {
Array<char> argValues[sizeof...(Params)] = {str(params)...};
fatalFailedSyscallInternal(file, line, callText, errorNum,
macroArgs, arrayPtr(argValues, sizeof...(Params)));
}
}
return result;
}
#endif
template <typename... Params>
void Log::reportFailedRecoverableSyscall(
int errorNumber, const char* file, int line, const char* callText,
const char* macroArgs, Params&&... params) {
Array<char> argValues[sizeof...(Params)] = {str(params)...};
recoverableFailedSyscallInternal(file, line, callText, errorNumber, macroArgs,
arrayPtr(argValues, sizeof...(Params)));
}
template <typename... Params>
void Log::reportFailedSyscall(
int errorNumber, const char* file, int line, const char* callText,
const char* macroArgs, Params&&... params) {
Array<char> argValues[sizeof...(Params)] = {str(params)...};
fatalFailedSyscallInternal(file, line, callText, errorNumber, macroArgs,
arrayPtr(argValues, sizeof...(Params)));
}
} // namespace capnproto } // namespace capnproto
#endif // CAPNPROTO_LOGGING_H_ #endif // CAPNPROTO_LOGGING_H_
...@@ -23,18 +23,23 @@ ...@@ -23,18 +23,23 @@
#define CAPNPROTO_PRIVATE #define CAPNPROTO_PRIVATE
#include "macros.h" #include "macros.h"
#include "exception.h" #include "logging.h"
#include <unistd.h> #include <stdlib.h>
#include <stdio.h>
#include "exception.h"
#include "util.h"
namespace capnproto { namespace capnproto {
namespace internal { namespace internal {
void assertionFailure(const char* file, int line, const char* expectation, const char* message) { void inlinePreconditionFailure(const char* file, int line, const char* expectation,
throw Exception(Exception::Nature::LOCAL_BUG, Exception::Durability::PERMANENT, const char* macroArgs, const char* message) {
file, line, str(expectation)); if (message == nullptr) {
Log::fatalFault(file, line, Exception::Nature::PRECONDITION, expectation, macroArgs);
} else {
Log::fatalFault(file, line, Exception::Nature::PRECONDITION, expectation, macroArgs, message);
}
// GCC prints a warning that this function returns even though Log::fatalFault() is clearly
// marked noreturn. Make the warning go away by calling abort()...
abort();
} }
} // namespace internal } // namespace internal
......
...@@ -57,19 +57,22 @@ namespace internal { ...@@ -57,19 +57,22 @@ namespace internal {
#define CAPNPROTO_NORETURN __attribute__((noreturn)); #define CAPNPROTO_NORETURN __attribute__((noreturn));
void assertionFailure(const char* file, int line, const char* expectation, const char* message) void inlinePreconditionFailure(
CAPNPROTO_NORETURN; const char* file, int line, const char* expectation, const char* macroArgs,
const char* message = nullptr) CAPNPROTO_NORETURN;
#define CAPNPROTO_INLINE_PRECOND(condition, ...) \
if (CAPNPROTO_EXPECT_TRUE(condition)); else ::capnproto::internal::inlinePreconditionFailure( \
__FILE__, __LINE__, #condition, #__VA_ARGS__, ##__VA_ARGS__)
// Version of PRECOND() which is safe to use in headers that are #included by users. Used to check
// preconditions inside inline methods. CAPNPROTO_INLINE_DPRECOND is particularly useful in that
// it will be enabled depending on whether the application is compiled in debug mode rather than
// whether libcapnproto is.
#define CAPNPROTO_ASSERT(condition, message) \
if (CAPNPROTO_EXPECT_TRUE(condition)); else ::capnproto::internal::assertionFailure(\
__FILE__, __LINE__, #condition, message)
// CAPNPROTO_ASSERT is just like assert() except it avoids polluting the global namespace with an
// unqualified macro name and it throws an exception (derived from std::exception).
#ifdef NDEBUG #ifdef NDEBUG
#define CAPNPROTO_DEBUG_ASSERT(condition, message) #define CAPNPROTO_INLINE_DPRECOND(...)
#else #else
#define CAPNPROTO_DEBUG_ASSERT(condition, message) CAPNPROTO_ASSERT(condition, message) #define CAPNPROTO_INLINE_DPRECOND CAPNPROTO_INLINE_PRECOND
#endif #endif
// Allocate an array, preferably on the stack, unless it is too big. On GCC this will use // Allocate an array, preferably on the stack, unless it is too big. On GCC this will use
......
...@@ -21,7 +21,9 @@ ...@@ -21,7 +21,9 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define CAPNPROTO_PRIVATE
#include "message.h" #include "message.h"
#include "logging.h"
#include "arena.h" #include "arena.h"
#include "stdlib.h" #include "stdlib.h"
#include <exception> #include <exception>
...@@ -78,10 +80,10 @@ internal::SegmentBuilder* MessageBuilder::getRootSegment() { ...@@ -78,10 +80,10 @@ internal::SegmentBuilder* MessageBuilder::getRootSegment() {
WordCount refSize = 1 * REFERENCES * WORDS_PER_REFERENCE; WordCount refSize = 1 * REFERENCES * WORDS_PER_REFERENCE;
internal::SegmentBuilder* segment = arena()->getSegmentWithAvailable(refSize); internal::SegmentBuilder* segment = arena()->getSegmentWithAvailable(refSize);
CAPNPROTO_ASSERT(segment->getSegmentId() == SegmentId(0), CHECK(segment->getSegmentId() == SegmentId(0),
"First allocated word of new arena was not in segment ID 0."); "First allocated word of new arena was not in segment ID 0.");
word* location = segment->allocate(refSize); word* location = segment->allocate(refSize);
CAPNPROTO_ASSERT(location == segment->getPtrUnchecked(0 * WORDS), CHECK(location == segment->getPtrUnchecked(0 * WORDS),
"First allocated word of new arena was not the first word in its segment."); "First allocated word of new arena was not the first word in its segment.");
return segment; return segment;
} }
...@@ -111,28 +113,12 @@ ArrayPtr<const ArrayPtr<const word>> MessageBuilder::getSegmentsForOutput() { ...@@ -111,28 +113,12 @@ ArrayPtr<const ArrayPtr<const word>> MessageBuilder::getSegmentsForOutput() {
ErrorReporter::~ErrorReporter() {} ErrorReporter::~ErrorReporter() {}
class ParseException: public std::exception {
public:
ParseException(std::string message)
: message(message) {}
~ParseException() noexcept {}
const char* what() const noexcept override {
return message.c_str();
}
private:
std::string message;
};
class ThrowingErrorReporter: public ErrorReporter { class ThrowingErrorReporter: public ErrorReporter {
public: public:
virtual ~ThrowingErrorReporter() {} virtual ~ThrowingErrorReporter() {}
void reportError(const char* description) override { void reportError(const char* description) override {
std::string message("Cap'n Proto message was invalid: "); FAIL_VALIDATE_INPUT("Invalid Cap'n Proto message", description);
message += description;
throw ParseException(std::move(message));
} }
}; };
...@@ -201,11 +187,11 @@ MallocMessageBuilder::MallocMessageBuilder( ...@@ -201,11 +187,11 @@ MallocMessageBuilder::MallocMessageBuilder(
ArrayPtr<word> firstSegment, AllocationStrategy allocationStrategy) ArrayPtr<word> firstSegment, AllocationStrategy allocationStrategy)
: nextSize(firstSegment.size()), allocationStrategy(allocationStrategy), : nextSize(firstSegment.size()), allocationStrategy(allocationStrategy),
ownFirstSegment(false), returnedFirstSegment(false), firstSegment(firstSegment.begin()) { ownFirstSegment(false), returnedFirstSegment(false), firstSegment(firstSegment.begin()) {
CAPNPROTO_ASSERT(firstSegment.size() > 0, "First segment size must be non-zero."); PRECOND(firstSegment.size() > 0, "First segment size must be non-zero.");
// Checking just the first word should catch most cases of failing to zero the segment. // Checking just the first word should catch most cases of failing to zero the segment.
CAPNPROTO_ASSERT(*reinterpret_cast<uint64_t*>(firstSegment.begin()) == 0, PRECOND(*reinterpret_cast<uint64_t*>(firstSegment.begin()) == 0,
"First segment must be zeroed."); "First segment must be zeroed.");
} }
MallocMessageBuilder::~MallocMessageBuilder() { MallocMessageBuilder::~MallocMessageBuilder() {
...@@ -216,7 +202,7 @@ MallocMessageBuilder::~MallocMessageBuilder() { ...@@ -216,7 +202,7 @@ MallocMessageBuilder::~MallocMessageBuilder() {
// Must zero first segment. // Must zero first segment.
ArrayPtr<const ArrayPtr<const word>> segments = getSegmentsForOutput(); ArrayPtr<const ArrayPtr<const word>> segments = getSegmentsForOutput();
if (segments.size() > 0) { if (segments.size() > 0) {
CAPNPROTO_ASSERT(segments[0].begin() == firstSegment, CHECK(segments[0].begin() == firstSegment,
"First segment in getSegmentsForOutput() is not the first segment allocated?"); "First segment in getSegmentsForOutput() is not the first segment allocated?");
memset(firstSegment, 0, segments[0].size() * sizeof(word)); memset(firstSegment, 0, segments[0].size() * sizeof(word));
} }
...@@ -247,7 +233,7 @@ ArrayPtr<word> MallocMessageBuilder::allocateSegment(uint minimumSize) { ...@@ -247,7 +233,7 @@ ArrayPtr<word> MallocMessageBuilder::allocateSegment(uint minimumSize) {
void* result = calloc(size, sizeof(word)); void* result = calloc(size, sizeof(word));
if (result == nullptr) { if (result == nullptr) {
throw std::bad_alloc(); FAIL_SYSCALL("calloc(size, sizeof(word))", ENOMEM, size);
} }
if (!returnedFirstSegment) { if (!returnedFirstSegment) {
......
...@@ -21,8 +21,10 @@ ...@@ -21,8 +21,10 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "test.capnp.h" #define CAPNPROTO_PRIVATE
#include "serialize-packed.h" #include "serialize-packed.h"
#include "test.capnp.h"
#include "logging.h"
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <string> #include <string>
#include <stdlib.h> #include <stdlib.h>
...@@ -60,7 +62,7 @@ public: ...@@ -60,7 +62,7 @@ public:
} }
size_t read(void* buffer, size_t minBytes, size_t maxBytes) override { size_t read(void* buffer, size_t minBytes, size_t maxBytes) override {
CAPNPROTO_ASSERT(maxBytes <= data.size() - readPos, "Overran end of stream."); CHECK(maxBytes <= data.size() - readPos, "Overran end of stream.");
size_t amount = std::min(maxBytes, std::max(minBytes, preferredReadSize)); size_t amount = std::min(maxBytes, std::max(minBytes, preferredReadSize));
memcpy(buffer, data.data() + readPos, amount); memcpy(buffer, data.data() + readPos, amount);
readPos += amount; readPos += amount;
...@@ -68,7 +70,7 @@ public: ...@@ -68,7 +70,7 @@ public:
} }
void skip(size_t bytes) override { void skip(size_t bytes) override {
CAPNPROTO_ASSERT(bytes <= data.size() - readPos, "Overran end of stream."); CHECK(bytes <= data.size() - readPos, "Overran end of stream.");
readPos += bytes; readPos += bytes;
} }
......
...@@ -21,7 +21,9 @@ ...@@ -21,7 +21,9 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define CAPNPROTO_PRIVATE
#include "serialize-packed.h" #include "serialize-packed.h"
#include "logging.h"
#include "layout.h" #include "layout.h"
#include <vector> #include <vector>
...@@ -37,23 +39,25 @@ size_t PackedInputStream::read(void* dst, size_t minBytes, size_t maxBytes) { ...@@ -37,23 +39,25 @@ size_t PackedInputStream::read(void* dst, size_t minBytes, size_t maxBytes) {
return 0; return 0;
} }
CAPNPROTO_DEBUG_ASSERT(minBytes % sizeof(word) == 0, DPRECOND(minBytes % sizeof(word) == 0, "PackedInputStream reads must be word-aligned.");
"PackedInputStream reads must be word-aligned."); DPRECOND(maxBytes % sizeof(word) == 0, "PackedInputStream reads must be word-aligned.");
CAPNPROTO_DEBUG_ASSERT(maxBytes % sizeof(word) == 0,
"PackedInputStream reads must be word-aligned.");
uint8_t* __restrict__ out = reinterpret_cast<uint8_t*>(dst); uint8_t* __restrict__ out = reinterpret_cast<uint8_t*>(dst);
uint8_t* const outEnd = reinterpret_cast<uint8_t*>(dst) + maxBytes; uint8_t* const outEnd = reinterpret_cast<uint8_t*>(dst) + maxBytes;
uint8_t* const outMin = reinterpret_cast<uint8_t*>(dst) + minBytes; uint8_t* const outMin = reinterpret_cast<uint8_t*>(dst) + minBytes;
ArrayPtr<const byte> buffer = inner.getReadBuffer(); ArrayPtr<const byte> buffer = inner.getReadBuffer();
CAPNPROTO_ASSERT(buffer.size() > 0, "Premature end of packed input."); VALIDATE_INPUT(buffer.size() > 0, "Premature end of packed input.") {
return minBytes; // garbage
}
const uint8_t* __restrict__ in = reinterpret_cast<const uint8_t*>(buffer.begin()); const uint8_t* __restrict__ in = reinterpret_cast<const uint8_t*>(buffer.begin());
#define REFRESH_BUFFER() \ #define REFRESH_BUFFER() \
inner.skip(buffer.size()); \ inner.skip(buffer.size()); \
buffer = inner.getReadBuffer(); \ buffer = inner.getReadBuffer(); \
CAPNPROTO_ASSERT(buffer.size() > 0, "Premature end of packed input."); \ VALIDATE_INPUT(buffer.size() > 0, "Premature end of packed input.") { \
return minBytes; /* garbage */ \
} \
in = reinterpret_cast<const uint8_t*>(buffer.begin()) in = reinterpret_cast<const uint8_t*>(buffer.begin())
#define BUFFER_END (reinterpret_cast<const uint8_t*>(buffer.end())) #define BUFFER_END (reinterpret_cast<const uint8_t*>(buffer.end()))
...@@ -62,8 +66,8 @@ size_t PackedInputStream::read(void* dst, size_t minBytes, size_t maxBytes) { ...@@ -62,8 +66,8 @@ size_t PackedInputStream::read(void* dst, size_t minBytes, size_t maxBytes) {
for (;;) { for (;;) {
uint8_t tag; uint8_t tag;
CAPNPROTO_DEBUG_ASSERT((out - reinterpret_cast<uint8_t*>(dst)) % sizeof(word) == 0, DCHECK((out - reinterpret_cast<uint8_t*>(dst)) % sizeof(word) == 0,
"Output pointer should always be aligned here."); "Output pointer should always be aligned here.");
if (BUFFER_REMAINING < 10) { if (BUFFER_REMAINING < 10) {
if (out >= outMin) { if (out >= outMin) {
...@@ -118,24 +122,26 @@ size_t PackedInputStream::read(void* dst, size_t minBytes, size_t maxBytes) { ...@@ -118,24 +122,26 @@ size_t PackedInputStream::read(void* dst, size_t minBytes, size_t maxBytes) {
} }
if (tag == 0) { if (tag == 0) {
CAPNPROTO_DEBUG_ASSERT(BUFFER_REMAINING > 0, DCHECK(BUFFER_REMAINING > 0, "Should always have non-empty buffer here.");
"Bug in this function: Should always have non-empty buffer here.");
uint runLength = *in++ * sizeof(word); uint runLength = *in++ * sizeof(word);
CAPNPROTO_ASSERT(runLength <= outEnd - out, VALIDATE_INPUT(runLength <= outEnd - out,
"Packed input did not end cleanly on a segment boundary."); "Packed input did not end cleanly on a segment boundary.") {
return std::max<size_t>(minBytes, out - reinterpret_cast<uint8_t*>(dst)); // garbage
}
memset(out, 0, runLength); memset(out, 0, runLength);
out += runLength; out += runLength;
} else if (tag == 0xffu) { } else if (tag == 0xffu) {
CAPNPROTO_DEBUG_ASSERT(BUFFER_REMAINING > 0, DCHECK(BUFFER_REMAINING > 0, "Should always have non-empty buffer here.");
"Bug in this function: Should always have non-empty buffer here.");
uint runLength = *in++ * sizeof(word); uint runLength = *in++ * sizeof(word);
CAPNPROTO_ASSERT(runLength <= outEnd - out, VALIDATE_INPUT(runLength <= outEnd - out,
"Packed input did not end cleanly on a segment boundary."); "Packed input did not end cleanly on a segment boundary.") {
return std::max<size_t>(minBytes, out - reinterpret_cast<uint8_t*>(dst)); // garbage
}
uint inRemaining = BUFFER_REMAINING; uint inRemaining = BUFFER_REMAINING;
if (inRemaining >= runLength) { if (inRemaining >= runLength) {
...@@ -171,8 +177,9 @@ size_t PackedInputStream::read(void* dst, size_t minBytes, size_t maxBytes) { ...@@ -171,8 +177,9 @@ size_t PackedInputStream::read(void* dst, size_t minBytes, size_t maxBytes) {
} }
} }
CAPNPROTO_ASSERT(false, "Can't get here."); FAIL_CHECK("Can't get here.");
return 0;
#undef REFRESH_BUFFER
} }
void PackedInputStream::skip(size_t bytes) { void PackedInputStream::skip(size_t bytes) {
...@@ -182,12 +189,17 @@ void PackedInputStream::skip(size_t bytes) { ...@@ -182,12 +189,17 @@ void PackedInputStream::skip(size_t bytes) {
return; return;
} }
CAPNPROTO_DEBUG_ASSERT(bytes % sizeof(word) == 0, DPRECOND(bytes % sizeof(word) == 0, "PackedInputStream reads must be word-aligned.");
"PackedInputStream reads must be word-aligned.");
ArrayPtr<const byte> buffer = inner.getReadBuffer(); ArrayPtr<const byte> buffer = inner.getReadBuffer();
const uint8_t* __restrict__ in = reinterpret_cast<const uint8_t*>(buffer.begin()); const uint8_t* __restrict__ in = reinterpret_cast<const uint8_t*>(buffer.begin());
#define REFRESH_BUFFER() \
inner.skip(buffer.size()); \
buffer = inner.getReadBuffer(); \
VALIDATE_INPUT(buffer.size() > 0, "Premature end of packed input.") return; \
in = reinterpret_cast<const uint8_t*>(buffer.begin())
for (;;) { for (;;) {
uint8_t tag; uint8_t tag;
...@@ -235,24 +247,26 @@ void PackedInputStream::skip(size_t bytes) { ...@@ -235,24 +247,26 @@ void PackedInputStream::skip(size_t bytes) {
} }
if (tag == 0) { if (tag == 0) {
CAPNPROTO_DEBUG_ASSERT(BUFFER_REMAINING > 0, DCHECK(BUFFER_REMAINING > 0, "Should always have non-empty buffer here.");
"Bug in this function: Should always have non-empty buffer here.");
uint runLength = *in++ * sizeof(word); uint runLength = *in++ * sizeof(word);
CAPNPROTO_ASSERT(runLength <= bytes, VALIDATE_INPUT(runLength <= bytes,
"Packed input did not end cleanly on a segment boundary."); "Packed input did not end cleanly on a segment boundary.") {
return;
}
bytes -= runLength; bytes -= runLength;
} else if (tag == 0xffu) { } else if (tag == 0xffu) {
CAPNPROTO_DEBUG_ASSERT(BUFFER_REMAINING > 0, DCHECK(BUFFER_REMAINING > 0, "Should always have non-empty buffer here.");
"Bug in this function: Should always have non-empty buffer here.");
uint runLength = *in++ * sizeof(word); uint runLength = *in++ * sizeof(word);
CAPNPROTO_ASSERT(runLength <= bytes, VALIDATE_INPUT(runLength <= bytes,
"Packed input did not end cleanly on a segment boundary."); "Packed input did not end cleanly on a segment boundary.") {
return;
}
bytes -= runLength; bytes -= runLength;
...@@ -283,7 +297,7 @@ void PackedInputStream::skip(size_t bytes) { ...@@ -283,7 +297,7 @@ void PackedInputStream::skip(size_t bytes) {
} }
} }
CAPNPROTO_ASSERT(false, "Can't get here."); FAIL_CHECK("Can't get here.");
} }
// ------------------------------------------------------------------- // -------------------------------------------------------------------
......
...@@ -21,8 +21,10 @@ ...@@ -21,8 +21,10 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "test.capnp.h" #define CAPNPROTO_PRIVATE
#include "serialize-snappy.h" #include "serialize-snappy.h"
#include "logging.h"
#include "test.capnp.h"
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <string> #include <string>
#include <stdlib.h> #include <stdlib.h>
...@@ -94,7 +96,7 @@ public: ...@@ -94,7 +96,7 @@ public:
} }
size_t read(void* buffer, size_t minBytes, size_t maxBytes) override { size_t read(void* buffer, size_t minBytes, size_t maxBytes) override {
CAPNPROTO_ASSERT(maxBytes <= data.size() - readPos, "Overran end of stream."); CHECK(maxBytes <= data.size() - readPos, "Overran end of stream.");
size_t amount = std::min(maxBytes, std::max(minBytes, preferredReadSize)); size_t amount = std::min(maxBytes, std::max(minBytes, preferredReadSize));
memcpy(buffer, data.data() + readPos, amount); memcpy(buffer, data.data() + readPos, amount);
readPos += amount; readPos += amount;
...@@ -102,7 +104,7 @@ public: ...@@ -102,7 +104,7 @@ public:
} }
void skip(size_t bytes) override { void skip(size_t bytes) override {
CAPNPROTO_ASSERT(bytes <= data.size() - readPos, "Overran end of stream."); CHECK(bytes <= data.size() - readPos, "Overran end of stream.");
readPos += bytes; readPos += bytes;
} }
......
...@@ -21,7 +21,9 @@ ...@@ -21,7 +21,9 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define CAPNPROTO_PRIVATE
#include "serialize-snappy.h" #include "serialize-snappy.h"
#include "logging.h"
#include "layout.h" #include "layout.h"
#include <snappy/snappy.h> #include <snappy/snappy.h>
#include <snappy/snappy-sinksource.h> #include <snappy/snappy-sinksource.h>
...@@ -38,7 +40,7 @@ public: ...@@ -38,7 +40,7 @@ public:
// implements snappy::Source --------------------------------------- // implements snappy::Source ---------------------------------------
size_t Available() const override { size_t Available() const override {
CAPNPROTO_ASSERT(false, "Snappy doesn't actually call this."); FAIL_CHECK("Snappy doesn't actually call this.");
return 0; return 0;
} }
...@@ -104,10 +106,12 @@ void SnappyInputStream::skip(size_t bytes) { ...@@ -104,10 +106,12 @@ void SnappyInputStream::skip(size_t bytes) {
void SnappyInputStream::refill() { void SnappyInputStream::refill() {
uint32_t length = 0; uint32_t length = 0;
InputStreamSnappySource snappySource(inner); InputStreamSnappySource snappySource(inner);
CAPNPROTO_ASSERT( VALIDATE_INPUT(
snappy::RawUncompress( snappy::RawUncompress(
&snappySource, reinterpret_cast<char*>(buffer.begin()), buffer.size(), &length), &snappySource, reinterpret_cast<char*>(buffer.begin()), buffer.size(), &length),
"Snappy decompression failed."); "Snappy decompression failed.") {
length = 1; // garbage
}
bufferAvailable = buffer.slice(0, length); bufferAvailable = buffer.slice(0, length);
} }
...@@ -117,8 +121,7 @@ void SnappyInputStream::refill() { ...@@ -117,8 +121,7 @@ void SnappyInputStream::refill() {
SnappyOutputStream::SnappyOutputStream( SnappyOutputStream::SnappyOutputStream(
OutputStream& inner, ArrayPtr<byte> buffer, ArrayPtr<byte> compressedBuffer) OutputStream& inner, ArrayPtr<byte> buffer, ArrayPtr<byte> compressedBuffer)
: inner(inner) { : inner(inner) {
CAPNPROTO_DEBUG_ASSERT( DCHECK(SNAPPY_COMPRESSED_BUFFER_SIZE >= snappy::MaxCompressedLength(snappy::kBlockSize),
SNAPPY_COMPRESSED_BUFFER_SIZE >= snappy::MaxCompressedLength(snappy::kBlockSize),
"snappy::MaxCompressedLength() changed?"); "snappy::MaxCompressedLength() changed?");
if (buffer.size() < SNAPPY_BUFFER_SIZE) { if (buffer.size() < SNAPPY_BUFFER_SIZE) {
...@@ -156,7 +159,7 @@ void SnappyOutputStream::flush() { ...@@ -156,7 +159,7 @@ void SnappyOutputStream::flush() {
snappy::UncheckedByteArraySink sink(reinterpret_cast<char*>(compressedBuffer.begin())); snappy::UncheckedByteArraySink sink(reinterpret_cast<char*>(compressedBuffer.begin()));
size_t n = snappy::Compress(&source, &sink); size_t n = snappy::Compress(&source, &sink);
CAPNPROTO_ASSERT(n <= compressedBuffer.size(), CHECK(n <= compressedBuffer.size(),
"Critical security bug: Snappy compression overran its output buffer."); "Critical security bug: Snappy compression overran its output buffer.");
inner.write(compressedBuffer.begin(), n); inner.write(compressedBuffer.begin(), n);
......
...@@ -21,8 +21,10 @@ ...@@ -21,8 +21,10 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "test.capnp.h" #define CAPNPROTO_PRIVATE
#include "serialize.h" #include "serialize.h"
#include "logging.h"
#include "test.capnp.h"
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <string> #include <string>
#include <stdlib.h> #include <stdlib.h>
...@@ -102,7 +104,7 @@ public: ...@@ -102,7 +104,7 @@ public:
~TestInputStream() {} ~TestInputStream() {}
size_t read(void* buffer, size_t minBytes, size_t maxBytes) override { size_t read(void* buffer, size_t minBytes, size_t maxBytes) override {
CAPNPROTO_ASSERT(maxBytes <= size_t(end - pos), "Overran end of stream."); CHECK(maxBytes <= size_t(end - pos), "Overran end of stream.");
size_t amount = lazy ? minBytes : maxBytes; size_t amount = lazy ? minBytes : maxBytes;
memcpy(buffer, pos, amount); memcpy(buffer, pos, amount);
pos += amount; pos += amount;
......
...@@ -21,8 +21,10 @@ ...@@ -21,8 +21,10 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define CAPNPROTO_PRIVATE
#include "serialize.h" #include "serialize.h"
#include "layout.h" #include "layout.h"
#include "logging.h"
namespace capnproto { namespace capnproto {
...@@ -87,7 +89,7 @@ ArrayPtr<const word> FlatArrayMessageReader::getSegment(uint id) { ...@@ -87,7 +89,7 @@ ArrayPtr<const word> FlatArrayMessageReader::getSegment(uint id) {
} }
Array<word> messageToFlatArray(ArrayPtr<const ArrayPtr<const word>> segments) { Array<word> messageToFlatArray(ArrayPtr<const ArrayPtr<const word>> segments) {
CAPNPROTO_ASSERT(segments.size() > 0, "Tried to serialize uninitialized message."); PRECOND(segments.size() > 0, "Tried to serialize uninitialized message.");
size_t totalSize = segments.size() / 2 + 1; size_t totalSize = segments.size() / 2 + 1;
...@@ -121,7 +123,7 @@ Array<word> messageToFlatArray(ArrayPtr<const ArrayPtr<const word>> segments) { ...@@ -121,7 +123,7 @@ Array<word> messageToFlatArray(ArrayPtr<const ArrayPtr<const word>> segments) {
dst += segment.size(); dst += segment.size();
} }
CAPNPROTO_DEBUG_ASSERT(dst == result.end(), "Buffer overrun/underrun bug in code above."); DCHECK(dst == result.end(), "Buffer overrun/underrun bug in code above.");
return move(result); return move(result);
} }
...@@ -141,7 +143,10 @@ InputStreamMessageReader::InputStreamMessageReader( ...@@ -141,7 +143,10 @@ InputStreamMessageReader::InputStreamMessageReader(
size_t totalWords = segment0Size; size_t totalWords = segment0Size;
// Reject messages with too many segments for security reasons. // Reject messages with too many segments for security reasons.
CAPNPROTO_ASSERT(segmentCount < 512, "Message has too many segments."); VALIDATE_INPUT(segmentCount < 512, "Message has too many segments.") {
segmentCount = 1;
segment0Size = 1;
}
// Read sizes for all segments except the first. Include padding if necessary. // Read sizes for all segments except the first. Include padding if necessary.
internal::WireValue<uint32_t> moreSizes[segmentCount & ~1]; internal::WireValue<uint32_t> moreSizes[segmentCount & ~1];
...@@ -155,9 +160,13 @@ InputStreamMessageReader::InputStreamMessageReader( ...@@ -155,9 +160,13 @@ InputStreamMessageReader::InputStreamMessageReader(
// Don't accept a message which the receiver couldn't possibly traverse without hitting the // Don't accept a message which the receiver couldn't possibly traverse without hitting the
// traversal limit. Without this check, a malicious client could transmit a very large segment // traversal limit. Without this check, a malicious client could transmit a very large segment
// size to make the receiver allocate excessive space and possibly crash. // size to make the receiver allocate excessive space and possibly crash.
CAPNPROTO_ASSERT(totalWords <= options.traversalLimitInWords, VALIDATE_INPUT(totalWords <= options.traversalLimitInWords,
"Message is too large. To increase the limit on the receiving end, see " "Message is too large. To increase the limit on the receiving end, see "
"capnproto::ReaderOptions."); "capnproto::ReaderOptions.") {
segmentCount = 1;
segment0Size = std::min<size_t>(segment0Size, options.traversalLimitInWords);
totalWords = segment0Size;
}
if (scratchSpace.size() < totalWords) { if (scratchSpace.size() < totalWords) {
// TODO: Consider allocating each segment as a separate chunk to reduce memory fragmentation. // TODO: Consider allocating each segment as a separate chunk to reduce memory fragmentation.
...@@ -228,7 +237,7 @@ ArrayPtr<const word> InputStreamMessageReader::getSegment(uint id) { ...@@ -228,7 +237,7 @@ ArrayPtr<const word> InputStreamMessageReader::getSegment(uint id) {
// ------------------------------------------------------------------- // -------------------------------------------------------------------
void writeMessage(OutputStream& output, ArrayPtr<const ArrayPtr<const word>> segments) { void writeMessage(OutputStream& output, ArrayPtr<const ArrayPtr<const word>> segments) {
CAPNPROTO_ASSERT(segments.size() > 0, "Tried to serialize uninitialized message."); PRECOND(segments.size() > 0, "Tried to serialize uninitialized message.");
internal::WireValue<uint32_t> table[(segments.size() + 2) & ~size_t(1)]; internal::WireValue<uint32_t> table[(segments.size() + 2) & ~size_t(1)];
......
...@@ -91,7 +91,7 @@ public: ...@@ -91,7 +91,7 @@ public:
inline std::size_t size() const { return size_; } inline std::size_t size() const { return size_; }
inline T& operator[](std::size_t index) const { inline T& operator[](std::size_t index) const {
CAPNPROTO_DEBUG_ASSERT(index < size_, "Out-of-bounds ArrayPtr access."); CAPNPROTO_INLINE_DPRECOND(index < size_, "Out-of-bounds ArrayPtr access.");
return ptr[index]; return ptr[index];
} }
...@@ -101,7 +101,7 @@ public: ...@@ -101,7 +101,7 @@ public:
inline T& back() const { return *(ptr + size_ - 1); } inline T& back() const { return *(ptr + size_ - 1); }
inline ArrayPtr slice(size_t start, size_t end) { inline ArrayPtr slice(size_t start, size_t end) {
CAPNPROTO_DEBUG_ASSERT(start <= end && end <= size_, "Out-of-bounds ArrayPtr::slice()."); CAPNPROTO_INLINE_DPRECOND(start <= end && end <= size_, "Out-of-bounds ArrayPtr::slice().");
return ArrayPtr(ptr + start, end - start); return ArrayPtr(ptr + start, end - start);
} }
...@@ -158,7 +158,7 @@ public: ...@@ -158,7 +158,7 @@ public:
inline std::size_t size() const { return size_; } inline std::size_t size() const { return size_; }
inline T& operator[](std::size_t index) const { inline T& operator[](std::size_t index) const {
CAPNPROTO_DEBUG_ASSERT(index < size_, "Out-of-bounds Array access."); CAPNPROTO_INLINE_DPRECOND(index < size_, "Out-of-bounds Array access.");
return ptr[index]; return ptr[index];
} }
...@@ -168,7 +168,7 @@ public: ...@@ -168,7 +168,7 @@ public:
inline T& back() const { return *(ptr + size_ - 1); } inline T& back() const { return *(ptr + size_ - 1); }
inline ArrayPtr<T> slice(size_t start, size_t end) { inline ArrayPtr<T> slice(size_t start, size_t end) {
CAPNPROTO_DEBUG_ASSERT(start <= end && end <= size_, "Out-of-bounds Array::slice()."); CAPNPROTO_INLINE_DPRECOND(start <= end && end <= size_, "Out-of-bounds Array::slice().");
return ArrayPtr<T>(ptr + start, end - start); return ArrayPtr<T>(ptr + start, end - start);
} }
...@@ -226,7 +226,7 @@ public: ...@@ -226,7 +226,7 @@ public:
template <typename... Params> template <typename... Params>
void add(Params&&... params) { void add(Params&&... params) {
CAPNPROTO_DEBUG_ASSERT(pos < endPtr, "Added too many elements to ArrayBuilder."); CAPNPROTO_INLINE_DPRECOND(pos < endPtr, "Added too many elements to ArrayBuilder.");
new(&pos->value) T(forward<Params>(params)...); new(&pos->value) T(forward<Params>(params)...);
++pos; ++pos;
} }
...@@ -245,7 +245,7 @@ public: ...@@ -245,7 +245,7 @@ public:
Array<T> finish() { Array<T> finish() {
// We could allow partial builds if Array<T> used a deleter callback, but that would make // We could allow partial builds if Array<T> used a deleter callback, but that would make
// Array<T> bigger for no benefit most of the time. // Array<T> bigger for no benefit most of the time.
CAPNPROTO_DEBUG_ASSERT(pos == endPtr, "ArrayBuilder::finish() called prematurely."); CAPNPROTO_INLINE_DPRECOND(pos == endPtr, "ArrayBuilder::finish() called prematurely.");
Array<T> result(reinterpret_cast<T*>(ptr), pos - ptr); Array<T> result(reinterpret_cast<T*>(ptr), pos - ptr);
ptr = nullptr; ptr = nullptr;
pos = nullptr; pos = nullptr;
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define CAPNPROTO_PRIVATE
#include "util.h" #include "util.h"
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <string> #include <string>
......
...@@ -21,7 +21,9 @@ ...@@ -21,7 +21,9 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define CAPNPROTO_PRIVATE
#include "util.h" #include "util.h"
#include "logging.h"
#include <stdio.h> #include <stdio.h>
#include <float.h> #include <float.h>
#include <limits> #include <limits>
...@@ -105,8 +107,6 @@ namespace { ...@@ -105,8 +107,6 @@ namespace {
// implementation. // implementation.
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
#define GOOGLE_DCHECK(cond) CAPNPROTO_DEBUG_ASSERT(cond, "Bug in code here.");
#ifdef _WIN32 #ifdef _WIN32
// MSVC has only _snprintf, not snprintf. // MSVC has only _snprintf, not snprintf.
// //
...@@ -200,7 +200,7 @@ char* DoubleToBuffer(double value, char* buffer) { ...@@ -200,7 +200,7 @@ char* DoubleToBuffer(double value, char* buffer) {
// The snprintf should never overflow because the buffer is significantly // The snprintf should never overflow because the buffer is significantly
// larger than the precision we asked for. // larger than the precision we asked for.
GOOGLE_DCHECK(snprintf_result > 0 && snprintf_result < kDoubleToBufferSize); DCHECK(snprintf_result > 0 && snprintf_result < kDoubleToBufferSize);
// We need to make parsed_value volatile in order to force the compiler to // We need to make parsed_value volatile in order to force the compiler to
// write it out to the stack. Otherwise, it may keep the value in a // write it out to the stack. Otherwise, it may keep the value in a
...@@ -214,7 +214,7 @@ char* DoubleToBuffer(double value, char* buffer) { ...@@ -214,7 +214,7 @@ char* DoubleToBuffer(double value, char* buffer) {
snprintf(buffer, kDoubleToBufferSize, "%.*g", DBL_DIG+2, value); snprintf(buffer, kDoubleToBufferSize, "%.*g", DBL_DIG+2, value);
// Should never overflow; see above. // Should never overflow; see above.
GOOGLE_DCHECK(snprintf_result > 0 && snprintf_result < kDoubleToBufferSize); DCHECK(snprintf_result > 0 && snprintf_result < kDoubleToBufferSize);
} }
DelocalizeRadix(buffer); DelocalizeRadix(buffer);
...@@ -256,7 +256,7 @@ char* FloatToBuffer(float value, char* buffer) { ...@@ -256,7 +256,7 @@ char* FloatToBuffer(float value, char* buffer) {
// The snprintf should never overflow because the buffer is significantly // The snprintf should never overflow because the buffer is significantly
// larger than the precision we asked for. // larger than the precision we asked for.
GOOGLE_DCHECK(snprintf_result > 0 && snprintf_result < kFloatToBufferSize); DCHECK(snprintf_result > 0 && snprintf_result < kFloatToBufferSize);
float parsed_value; float parsed_value;
if (!safe_strtof(buffer, &parsed_value) || parsed_value != value) { if (!safe_strtof(buffer, &parsed_value) || parsed_value != value) {
...@@ -264,7 +264,7 @@ char* FloatToBuffer(float value, char* buffer) { ...@@ -264,7 +264,7 @@ char* FloatToBuffer(float value, char* buffer) {
snprintf(buffer, kFloatToBufferSize, "%.*g", FLT_DIG+2, value); snprintf(buffer, kFloatToBufferSize, "%.*g", FLT_DIG+2, value);
// Should never overflow; see above. // Should never overflow; see above.
GOOGLE_DCHECK(snprintf_result > 0 && snprintf_result < kFloatToBufferSize); DCHECK(snprintf_result > 0 && snprintf_result < kFloatToBufferSize);
} }
DelocalizeRadix(buffer); DelocalizeRadix(buffer);
......
...@@ -24,6 +24,10 @@ ...@@ -24,6 +24,10 @@
#ifndef CAPNPROTO_UTIL_H_ #ifndef CAPNPROTO_UTIL_H_
#define CAPNPROTO_UTIL_H_ #define CAPNPROTO_UTIL_H_
#ifndef CAPNPROTO_PRIVATE
#error "This header is only meant to be included by Cap'n Proto's own source code."
#endif
#include <initializer_list> #include <initializer_list>
#include <utility> #include <utility>
#include <type_traits> #include <type_traits>
...@@ -39,6 +43,8 @@ namespace capnproto { ...@@ -39,6 +43,8 @@ namespace capnproto {
template <typename T, size_t fixedSize> template <typename T, size_t fixedSize>
class FixedArray { class FixedArray {
// A fixed-width array whose storage is allocated inline rather than on the heap.
public: public:
inline size_t size() const { return fixedSize; } inline size_t size() const { return fixedSize; }
inline T* begin() { return content; } inline T* begin() { return content; }
...@@ -62,6 +68,9 @@ private: ...@@ -62,6 +68,9 @@ private:
template <typename T, size_t fixedSize> template <typename T, size_t fixedSize>
class CappedArray { class CappedArray {
// Like `FixedArray` but can be dynamically resized as long as the size does not exceed the limit
// specified by the template parameter.
public: public:
inline constexpr CappedArray(): currentSize(fixedSize) {} inline constexpr CappedArray(): currentSize(fixedSize) {}
inline explicit constexpr CappedArray(size_t s): currentSize(s) {} inline explicit constexpr CappedArray(size_t s): currentSize(s) {}
...@@ -82,8 +91,10 @@ private: ...@@ -82,8 +91,10 @@ private:
T content[fixedSize]; T content[fixedSize];
}; };
template <typename T> template <typename T, typename Container>
Array<T> iterableToArray(T&& a) { Array<T> iterableToArray(Container&& a) {
// Converts an arbitrary iterable container into an array of the given element type.
Array<T> result = newArray<T>(a.size()); Array<T> result = newArray<T>(a.size());
auto i = a.iterator(); auto i = a.iterator();
auto end = a.end(); auto end = a.end();
...@@ -135,7 +146,15 @@ Element* fill(Element* __restrict__ target, First&& first, Rest&&... rest) { ...@@ -135,7 +146,15 @@ Element* fill(Element* __restrict__ target, First&& first, Rest&&... rest) {
template <typename Element, typename... Params> template <typename Element, typename... Params>
Array<Element> concat(Params&&... params) { Array<Element> concat(Params&&... params) {
// Concatenate a bunch of containers into a single Array. The containers can be anything that
// is iterable and whose elements can be converted to `Element`.
#ifdef __CDT_PARSER__
// Eclipse reports a bogus error on `size()`.
Array<Element> result;
#else
Array<Element> result = newArray<Element>(sum({params.size()...})); Array<Element> result = newArray<Element>(sum({params.size()...}));
#endif
fill(result.begin(), std::forward<Params>(params)...); fill(result.begin(), std::forward<Params>(params)...);
return result; return result;
} }
...@@ -153,9 +172,10 @@ struct Stringifier { ...@@ -153,9 +172,10 @@ struct Stringifier {
// //
// A more usual way to accomplish what we're doing here would be to require that you define // A more usual way to accomplish what we're doing here would be to require that you define
// a function like `toString(T)` and then rely on argument-dependent lookup. However, this has // a function like `toString(T)` and then rely on argument-dependent lookup. However, this has
// the problem that it pollutes other people's namespaces and even the global namespace. // the problem that it pollutes other people's namespaces and even the global namespace. For
// Declaring `operator*` with `Stringifier` as the left operand does not harm any other // example, some other project may already have functions called `toString` which do something
// namespaces. // different. Declaring `operator*` with `Stringifier` as the left operand cannot conflict with
// anything.
inline ArrayPtr<const char> operator*(ArrayPtr<const char> s) const { return s; } inline ArrayPtr<const char> operator*(ArrayPtr<const char> s) const { return s; }
inline ArrayPtr<const char> operator*(const char* s) const { return arrayPtr(s, strlen(s)); } inline ArrayPtr<const char> operator*(const char* s) const { return arrayPtr(s, strlen(s)); }
...@@ -181,6 +201,12 @@ static constexpr Stringifier STR; ...@@ -181,6 +201,12 @@ static constexpr Stringifier STR;
template <typename... Params> template <typename... Params>
Array<char> str(Params&&... params) { Array<char> str(Params&&... params) {
// Magic function which builds a string from a bunch of arbitrary values. Example:
// str(1, " / ", 2, " = ", 0.5)
// returns:
// "1 / 2 = 0.5"
// To teach `str` how to stringify a type, see `Stringifier`.
return concat<char>(STR * std::forward<Params>(params)...); return concat<char>(STR * std::forward<Params>(params)...);
} }
......
...@@ -168,8 +168,8 @@ inline {{structName}}::{{unionTitleCase}} {{structName}}::Reader::which{{unionTi ...@@ -168,8 +168,8 @@ inline {{structName}}::{{unionTitleCase}} {{structName}}::Reader::which{{unionTi
{{#fieldIsPrimitive}} {{#fieldIsPrimitive}}
inline {{fieldType}} {{structName}}::Reader::get{{fieldTitleCase}}() { inline {{fieldType}} {{structName}}::Reader::get{{fieldTitleCase}}() {
{{#fieldUnion}} {{#fieldUnion}}
CAPNPROTO_DEBUG_ASSERT(which{{unionTitleCase}}() == {{unionTitleCase}}::{{fieldUpperCase}}, CAPNPROTO_INLINE_DPRECOND(which{{unionTitleCase}}() == {{unionTitleCase}}::{{fieldUpperCase}},
"Must check which() before get()ing a union member."); "Must check which() before get()ing a union member.");
{{/fieldUnion}} {{/fieldUnion}}
return _reader.getDataField<{{fieldType}}>( return _reader.getDataField<{{fieldType}}>(
{{fieldOffset}} * ::capnproto::ELEMENTS{{fieldDefaultMask}}); {{fieldOffset}} * ::capnproto::ELEMENTS{{fieldDefaultMask}});
...@@ -178,8 +178,8 @@ inline {{fieldType}} {{structName}}::Reader::get{{fieldTitleCase}}() { ...@@ -178,8 +178,8 @@ inline {{fieldType}} {{structName}}::Reader::get{{fieldTitleCase}}() {
{{#fieldIsBlob}} {{#fieldIsBlob}}
inline {{fieldType}}::Reader {{structName}}::Reader::get{{fieldTitleCase}}() { inline {{fieldType}}::Reader {{structName}}::Reader::get{{fieldTitleCase}}() {
{{#fieldUnion}} {{#fieldUnion}}
CAPNPROTO_DEBUG_ASSERT(which{{unionTitleCase}}() == {{unionTitleCase}}::{{fieldUpperCase}}, CAPNPROTO_INLINE_DPRECOND(which{{unionTitleCase}}() == {{unionTitleCase}}::{{fieldUpperCase}},
"Must check which() before get()ing a union member."); "Must check which() before get()ing a union member.");
{{/fieldUnion}} {{/fieldUnion}}
return _reader.get{{fieldBlobType}}Field( return _reader.get{{fieldBlobType}}Field(
{{fieldOffset}} * ::capnproto::REFERENCES, {{fieldOffset}} * ::capnproto::REFERENCES,
...@@ -192,8 +192,8 @@ inline {{fieldType}}::Reader {{structName}}::Reader::get{{fieldTitleCase}}() { ...@@ -192,8 +192,8 @@ inline {{fieldType}}::Reader {{structName}}::Reader::get{{fieldTitleCase}}() {
{{#fieldIsStruct}} {{#fieldIsStruct}}
inline {{fieldType}}::Reader {{structName}}::Reader::get{{fieldTitleCase}}() { inline {{fieldType}}::Reader {{structName}}::Reader::get{{fieldTitleCase}}() {
{{#fieldUnion}} {{#fieldUnion}}
CAPNPROTO_DEBUG_ASSERT(which{{unionTitleCase}}() == {{unionTitleCase}}::{{fieldUpperCase}}, CAPNPROTO_INLINE_DPRECOND(which{{unionTitleCase}}() == {{unionTitleCase}}::{{fieldUpperCase}},
"Must check which() before get()ing a union member."); "Must check which() before get()ing a union member.");
{{/fieldUnion}} {{/fieldUnion}}
return {{fieldType}}::Reader(_reader.getStructField( return {{fieldType}}::Reader(_reader.getStructField(
{{fieldOffset}} * ::capnproto::REFERENCES, {{fieldOffset}} * ::capnproto::REFERENCES,
...@@ -204,8 +204,8 @@ inline {{fieldType}}::Reader {{structName}}::Reader::get{{fieldTitleCase}}() { ...@@ -204,8 +204,8 @@ inline {{fieldType}}::Reader {{structName}}::Reader::get{{fieldTitleCase}}() {
{{#fieldIsList}} {{#fieldIsList}}
inline {{fieldType}}::Reader {{structName}}::Reader::get{{fieldTitleCase}}() { inline {{fieldType}}::Reader {{structName}}::Reader::get{{fieldTitleCase}}() {
{{#fieldUnion}} {{#fieldUnion}}
CAPNPROTO_DEBUG_ASSERT(which{{unionTitleCase}}() == {{unionTitleCase}}::{{fieldUpperCase}}, CAPNPROTO_INLINE_DPRECOND(which{{unionTitleCase}}() == {{unionTitleCase}}::{{fieldUpperCase}},
"Must check which() before get()ing a union member."); "Must check which() before get()ing a union member.");
{{/fieldUnion}} {{/fieldUnion}}
return {{fieldType}}::Reader(_reader.getListField( return {{fieldType}}::Reader(_reader.getListField(
{{fieldOffset}} * ::capnproto::REFERENCES, {{fieldOffset}} * ::capnproto::REFERENCES,
...@@ -231,8 +231,8 @@ inline {{structName}}::{{unionTitleCase}} {{structName}}::Builder::which{{unionT ...@@ -231,8 +231,8 @@ inline {{structName}}::{{unionTitleCase}} {{structName}}::Builder::which{{unionT
{{#fieldIsPrimitive}} {{#fieldIsPrimitive}}
inline {{fieldType}} {{structName}}::Builder::get{{fieldTitleCase}}() { inline {{fieldType}} {{structName}}::Builder::get{{fieldTitleCase}}() {
{{#fieldUnion}} {{#fieldUnion}}
CAPNPROTO_DEBUG_ASSERT(which{{unionTitleCase}}() == {{unionTitleCase}}::{{fieldUpperCase}}, CAPNPROTO_INLINE_DPRECOND(which{{unionTitleCase}}() == {{unionTitleCase}}::{{fieldUpperCase}},
"Must check which() before get()ing a union member."); "Must check which() before get()ing a union member.");
{{/fieldUnion}} {{/fieldUnion}}
return _builder.getDataField<{{fieldType}}>( return _builder.getDataField<{{fieldType}}>(
{{fieldOffset}} * ::capnproto::ELEMENTS{{fieldDefaultMask}}); {{fieldOffset}} * ::capnproto::ELEMENTS{{fieldDefaultMask}});
...@@ -249,8 +249,8 @@ inline void {{structName}}::Builder::set{{fieldTitleCase}}({{fieldType}} value) ...@@ -249,8 +249,8 @@ inline void {{structName}}::Builder::set{{fieldTitleCase}}({{fieldType}} value)
{{#fieldIsBlob}} {{#fieldIsBlob}}
inline {{fieldType}}::Builder {{structName}}::Builder::get{{fieldTitleCase}}() { inline {{fieldType}}::Builder {{structName}}::Builder::get{{fieldTitleCase}}() {
{{#fieldUnion}} {{#fieldUnion}}
CAPNPROTO_DEBUG_ASSERT(which{{unionTitleCase}}() == {{unionTitleCase}}::{{fieldUpperCase}}, CAPNPROTO_INLINE_DPRECOND(which{{unionTitleCase}}() == {{unionTitleCase}}::{{fieldUpperCase}},
"Must check which() before get()ing a union member."); "Must check which() before get()ing a union member.");
{{/fieldUnion}} {{/fieldUnion}}
return _builder.get{{fieldBlobType}}Field({{fieldOffset}} * ::capnproto::REFERENCES, return _builder.get{{fieldBlobType}}Field({{fieldOffset}} * ::capnproto::REFERENCES,
{{#fieldDefaultBytes}} {{#fieldDefaultBytes}}
...@@ -285,8 +285,8 @@ inline {{fieldType}}::Builder {{structName}}::Builder::init{{fieldTitleCase}}() ...@@ -285,8 +285,8 @@ inline {{fieldType}}::Builder {{structName}}::Builder::init{{fieldTitleCase}}()
} }
inline {{fieldType}}::Builder {{structName}}::Builder::get{{fieldTitleCase}}() { inline {{fieldType}}::Builder {{structName}}::Builder::get{{fieldTitleCase}}() {
{{#fieldUnion}} {{#fieldUnion}}
CAPNPROTO_DEBUG_ASSERT(which{{unionTitleCase}}() == {{unionTitleCase}}::{{fieldUpperCase}}, CAPNPROTO_INLINE_DPRECOND(which{{unionTitleCase}}() == {{unionTitleCase}}::{{fieldUpperCase}},
"Must check which() before get()ing a union member."); "Must check which() before get()ing a union member.");
{{/fieldUnion}} {{/fieldUnion}}
return {{fieldType}}::Builder(_builder.getStructField( return {{fieldType}}::Builder(_builder.getStructField(
{{fieldOffset}} * ::capnproto::REFERENCES, {{fieldType}}::STRUCT_SIZE, {{fieldOffset}} * ::capnproto::REFERENCES, {{fieldType}}::STRUCT_SIZE,
...@@ -307,8 +307,8 @@ inline {{fieldType}}::Builder {{structName}}::Builder::init{{fieldTitleCase}}(un ...@@ -307,8 +307,8 @@ inline {{fieldType}}::Builder {{structName}}::Builder::init{{fieldTitleCase}}(un
} }
inline {{fieldType}}::Builder {{structName}}::Builder::get{{fieldTitleCase}}() { inline {{fieldType}}::Builder {{structName}}::Builder::get{{fieldTitleCase}}() {
{{#fieldUnion}} {{#fieldUnion}}
CAPNPROTO_DEBUG_ASSERT(which{{unionTitleCase}}() == {{unionTitleCase}}::{{fieldUpperCase}}, CAPNPROTO_INLINE_DPRECOND(which{{unionTitleCase}}() == {{unionTitleCase}}::{{fieldUpperCase}},
"Must check which() before get()ing a union member."); "Must check which() before get()ing a union member.");
{{/fieldUnion}} {{/fieldUnion}}
return {{fieldType}}::Builder(_builder.getListField( return {{fieldType}}::Builder(_builder.getListField(
{{fieldOffset}} * ::capnproto::REFERENCES, {{fieldOffset}} * ::capnproto::REFERENCES,
...@@ -356,8 +356,8 @@ inline {{fieldType}}::Builder {{structName}}::Builder::init{{fieldTitleCase}}(un ...@@ -356,8 +356,8 @@ inline {{fieldType}}::Builder {{structName}}::Builder::init{{fieldTitleCase}}(un
} }
inline {{fieldType}}::Builder {{structName}}::Builder::get{{fieldTitleCase}}() { inline {{fieldType}}::Builder {{structName}}::Builder::get{{fieldTitleCase}}() {
{{#fieldUnion}} {{#fieldUnion}}
CAPNPROTO_DEBUG_ASSERT(which{{unionTitleCase}}() == {{unionTitleCase}}::{{fieldUpperCase}}, CAPNPROTO_INLINE_DPRECOND(which{{unionTitleCase}}() == {{unionTitleCase}}::{{fieldUpperCase}},
"Must check which() before get()ing a union member."); "Must check which() before get()ing a union member.");
{{/fieldUnion}} {{/fieldUnion}}
return {{fieldType}}::Builder(_builder.getListField( return {{fieldType}}::Builder(_builder.getListField(
{{fieldOffset}} * ::capnproto::REFERENCES, {{fieldOffset}} * ::capnproto::REFERENCES,
......
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