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 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define CAPNPROTO_PRIVATE
#include "arena.h"
#include "message.h"
#include "logging.h"
#include <vector>
#include <string.h>
#include <stdio.h>
......@@ -170,9 +172,9 @@ ArrayPtr<const ArrayPtr<const word>> BuilderArena::getSegmentsForOutput() {
return arrayPtr(&segment0ForOutput, 1);
}
} else {
CAPNPROTO_DEBUG_ASSERT(moreSegments->forOutput.size() == moreSegments->builders.size() + 1,
"Bug in capnproto::internal::BuilderArena: moreSegments->forOutput wasn't resized "
"correctly when the last builder was added.");
DCHECK(moreSegments->forOutput.size() == moreSegments->builders.size() + 1,
"moreSegments->forOutput wasn't resized correctly when the last builder was added.",
moreSegments->forOutput.size(), moreSegments->builders.size());
ArrayPtr<ArrayPtr<const word>> result(
&moreSegments->forOutput[0], moreSegments->forOutput.size());
......
......@@ -24,9 +24,11 @@
#ifndef CAPNPROTO_BENCHMARK_CAPNPROTO_COMMON_H_
#define CAPNPROTO_BENCHMARK_CAPNPROTO_COMMON_H_
#define CAPNPROTO_PRIVATE
#include "common.h"
#include <capnproto/serialize.h>
#include <capnproto/serialize-packed.h>
#include <capnproto/logging.h>
#if HAVE_SNAPPY
#include <capnproto/serialize-snappy.h>
#endif // HAVE_SNAPPY
......@@ -179,7 +181,7 @@ struct UseScratch {
word* words;
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;
}
~ScratchSpace() {
......
......@@ -72,7 +72,7 @@ public:
inline const char operator[](uint index) const { return bytes[index]; }
inline Reader slice(uint start, uint end) const {
CAPNPROTO_DEBUG_ASSERT(start <= end && end <= size_, "Out-of-bounds slice.");
CAPNPROTO_INLINE_DPRECOND(start <= end && end <= size_, "Out-of-bounds slice.");
return Reader(bytes + start, end - start);
}
......@@ -110,20 +110,19 @@ class Text::Reader: public Data::Reader {
public:
inline Reader(): Data::Reader("", 0) {}
inline Reader(const char* text): Data::Reader(text, strlen(text)) {
CAPNPROTO_DEBUG_ASSERT(text[size()] == '\0', "Text must be NUL-terminated.");
CAPNPROTO_INLINE_DPRECOND(text[size()] == '\0', "Text must be NUL-terminated.");
}
inline Reader(char* text): Data::Reader(text, strlen(text)) {
CAPNPROTO_DEBUG_ASSERT(text[size()] == '\0', "Text must be NUL-terminated.");
CAPNPROTO_INLINE_DPRECOND(text[size()] == '\0', "Text must be NUL-terminated.");
}
inline Reader(const char* text, uint size): Data::Reader(text, size) {
CAPNPROTO_DEBUG_ASSERT(text[size] == '\0', "Text must be NUL-terminated.");
CAPNPROTO_INLINE_DPRECOND(text[size] == '\0', "Text must be NUL-terminated.");
}
template <typename T>
inline Reader(const T& other): Data::Reader(other.c_str(), other.size()) {
// Primarily intended for converting from std::string.
CAPNPROTO_DEBUG_ASSERT(data()[size()] == '\0',
"Text must be NUL-terminated.");
CAPNPROTO_INLINE_DPRECOND(data()[size()] == '\0', "Text must be NUL-terminated.");
}
inline const char* c_str() const { return data(); }
......@@ -153,7 +152,7 @@ public:
inline char& operator[](uint index) const { return bytes[index]; }
inline Builder slice(uint start, uint end) const {
CAPNPROTO_DEBUG_ASSERT(start <= end && end <= size_, "Out-of-bounds slice.");
CAPNPROTO_INLINE_DPRECOND(start <= end && end <= size_, "Out-of-bounds slice.");
return Builder(bytes + start, end - start);
}
......@@ -172,7 +171,7 @@ public:
template <typename T>
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());
}
inline void copyFrom(const void* other) const {
......
......@@ -21,8 +21,10 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define CAPNPROTO_PRIVATE
#include "test.capnp.h"
#include "message.h"
#include "logging.h"
#include <gtest/gtest.h>
#include "test-util.h"
......@@ -177,7 +179,7 @@ UnionState initUnion(void (TestUnion::Builder::*setter)(T value)) {
(builder.getRoot<TestUnion>().*setter)(one<T>());
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.
int offset = 0;
......
......@@ -34,6 +34,7 @@ ArrayPtr<const char> operator*(const Stringifier&, Exception::Nature nature) {
"precondition not met",
"bug in code",
"invalid input data",
"error from OS",
"network failure",
"error"
};
......@@ -96,15 +97,19 @@ ExceptionCallback::~ExceptionCallback() {
void ExceptionCallback::onRecoverableException(Exception&& exception) {
#if __GNUC__ && !__EXCEPTIONS
Log::writeRaw(str(exception.what(), '\n'));
logMessage(str(exception.what(), '\n'));
#else
throw std::move(exception);
if (std::uncaught_exception()) {
logMessage(str("unwind: ", exception.what(), '\n'));
} else {
throw std::move(exception);
}
#endif
}
void ExceptionCallback::onFatalException(Exception&& exception) {
#if __GNUC__ && !__EXCEPTIONS
Log::writeRaw(str(exception.what(), '\n'));
logMessage(str(exception.what(), '\n'));
#else
throw std::move(exception);
#endif
......
......@@ -32,6 +32,12 @@ namespace capnproto {
class Exception: public std::exception {
// 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:
enum class Nature {
// What kind of failure? This is informational, not intended for programmatic use.
......@@ -42,6 +48,7 @@ public:
PRECONDITION,
LOCAL_BUG,
INPUT,
OS_ERROR,
NETWORK_FAILURE,
OTHER
......
......@@ -21,25 +21,15 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define CAPNPROTO_PRIVATE
#include "io.h"
#include <errno.h>
#include "logging.h"
#include <unistd.h>
#include <sys/uio.h>
#include <string>
#include <string.h>
namespace capnproto {
class PrematureEofException: public std::exception {
public:
PrematureEofException() {}
~PrematureEofException() noexcept {}
const char* what() const noexcept override {
return "Stream ended prematurely.";
}
};
InputStream::~InputStream() {}
OutputStream::~OutputStream() {}
BufferedInputStream::~BufferedInputStream() {}
......@@ -198,17 +188,18 @@ ArrayPtr<const byte> ArrayInputStream::getReadBuffer() {
size_t ArrayInputStream::read(void* dst, size_t minBytes, size_t maxBytes) {
size_t n = std::min(maxBytes, array.size());
if (n < minBytes) {
throw PrematureEofException();
size_t result = n;
VALIDATE_INPUT(n >= minBytes, "ArrayInputStream ended prematurely.") {
result = minBytes; // garbage
}
memcpy(dst, array.begin(), n);
array = array.slice(n, array.size());
return n;
return result;
}
void ArrayInputStream::skip(size_t bytes) {
if (array.size() < bytes) {
throw PrematureEofException();
VALIDATE_INPUT(array.size() >= bytes, "ArrayInputStream ended prematurely.") {
bytes = array.size();
}
array = array.slice(bytes, array.size());
}
......@@ -227,8 +218,8 @@ void ArrayOutputStream::write(const void* src, size_t size) {
// Oh goody, the caller wrote directly into our buffer.
fillPos += size;
} else {
CAPNPROTO_ASSERT(size <= (size_t)(array.end() - fillPos),
"ArrayOutputStream's array is not big enough.");
PRECOND(size <= (size_t)(array.end() - fillPos),
"ArrayOutputStream's backing array was not large enough for the data written.");
memcpy(fillPos, src, size);
fillPos += 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() {
if (fd >= 0 && close(fd) < 0) {
if (std::uncaught_exception()) {
// TODO: Devise some way to report secondary errors during unwind.
} else {
throw OsException("close", errno);
}
FAIL_RECOVERABLE_SYSCALL("close", errno, fd);
}
}
......@@ -272,21 +241,10 @@ size_t FdInputStream::read(void* buffer, size_t minBytes, size_t maxBytes) {
byte* max = pos + maxBytes;
while (pos < min) {
ssize_t n = ::read(fd, pos, max - pos);
if (n <= 0) {
if (n < 0) {
int error = errno;
if (error == EINTR) {
continue;
} else {
throw OsException("read", error);
}
} else if (n == 0) {
throw PrematureEofException();
}
return false;
ssize_t n = SYSCALL(::read(fd, pos, max - pos), fd);
VALIDATE_INPUT(n > 0, "Premature EOF") {
return minBytes;
}
pos += n;
}
......@@ -299,16 +257,8 @@ void FdOutputStream::write(const void* buffer, size_t size) {
const char* pos = reinterpret_cast<const char*>(buffer);
while (size > 0) {
ssize_t n = ::write(fd, pos, size);
if (n <= 0) {
CAPNPROTO_ASSERT(n < 0, "write() returned zero.");
int error = errno;
if (error == EINTR) {
continue;
} else {
throw OsException("write", error);
}
}
ssize_t n = SYSCALL(::write(fd, pos, size), fd);
CHECK(n > 0, "write() returned zero.");
pos += n;
size -= n;
}
......@@ -331,14 +281,8 @@ void FdOutputStream::write(ArrayPtr<const ArrayPtr<const byte>> pieces) {
}
while (current < iov.end()) {
ssize_t n = ::writev(fd, current, iov.end() - current);
if (n <= 0) {
if (n <= 0) {
CAPNPROTO_ASSERT(n < 0, "write() returned zero.");
throw OsException("writev", errno);
}
}
ssize_t n = SYSCALL(::writev(fd, current, iov.end() - current), fd);
CHECK(n > 0, "writev() returned zero.");
while (static_cast<size_t>(n) >= current->iov_len) {
n -= current->iov_len;
......
......@@ -21,7 +21,9 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define CAPNPROTO_PRIVATE
#include "layout.h"
#include "logging.h"
#include "arena.h"
#include <string.h>
#include <limits>
......@@ -96,12 +98,12 @@ struct WireReference {
}
CAPNPROTO_ALWAYS_INLINE(WordCount farPositionInSegment() const) {
CAPNPROTO_DEBUG_ASSERT(kind() == FAR,
DPRECOND(kind() == FAR,
"positionInSegment() should only be called on FAR references.");
return (offsetAndKind.get() >> 3) * WORDS;
}
CAPNPROTO_ALWAYS_INLINE(bool isDoubleFar() const) {
CAPNPROTO_DEBUG_ASSERT(kind() == FAR,
DPRECOND(kind() == FAR,
"isDoubleFar() should only be called on FAR references.");
return (offsetAndKind.get() >> 2) & 1;
}
......@@ -149,14 +151,12 @@ struct WireReference {
}
CAPNPROTO_ALWAYS_INLINE(void set(FieldSize es, ElementCount ec)) {
CAPNPROTO_DEBUG_ASSERT(ec < (1 << 29) * ELEMENTS,
"Lists are limited to 2**29 elements.");
DPRECOND(ec < (1 << 29) * ELEMENTS, "Lists are limited to 2**29 elements.");
elementSizeAndCount.set(((ec / ELEMENTS) << 3) | static_cast<int>(es));
}
CAPNPROTO_ALWAYS_INLINE(void setInlineComposite(WordCount wc)) {
CAPNPROTO_DEBUG_ASSERT(wc < (1 << 29) * WORDS,
"Inline composite lists are limited to 2**29 words.");
DPRECOND(wc < (1 << 29) * WORDS, "Inline composite lists are limited to 2**29 words.");
elementSizeAndCount.set(((wc / WORDS) << 3) |
static_cast<int>(FieldSize::INLINE_COMPOSITE));
}
......@@ -379,7 +379,7 @@ struct WireHelpers {
const word* srcElement = srcPtr + 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.");
uint n = srcTag->inlineCompositeListElementCount() / ELEMENTS;
......@@ -395,7 +395,7 @@ struct WireHelpers {
break;
}
default:
CAPNPROTO_ASSERT(false, "Copy source message contained unexpected kind.");
FAIL_PRECOND("Copy source message contained unexpected kind.");
break;
}
......@@ -430,12 +430,12 @@ struct WireHelpers {
} else {
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.");
CAPNPROTO_DEBUG_ASSERT(
DPRECOND(
ref->structRef.dataSize.get() == size.data,
"Trying to update struct with incorrect data size.");
CAPNPROTO_DEBUG_ASSERT(
DPRECOND(
ref->structRef.refCount.get() == size.pointers,
"Trying to update struct with incorrect reference count.");
}
......@@ -446,7 +446,7 @@ struct WireHelpers {
static CAPNPROTO_ALWAYS_INLINE(ListBuilder initListReference(
WireReference* ref, SegmentBuilder* segment, ElementCount elementCount,
FieldSize elementSize)) {
CAPNPROTO_DEBUG_ASSERT(elementSize != FieldSize::INLINE_COMPOSITE,
DPRECOND(elementSize != FieldSize::INLINE_COMPOSITE,
"Should have called initStructListReference() instead.");
// Calculate size of the list.
......@@ -499,14 +499,14 @@ struct WireHelpers {
} else {
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.");
}
if (ref->listRef.elementSize() == FieldSize::INLINE_COMPOSITE) {
// Read the tag to get the actual element count.
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.");
ElementCount elementCount = tag->inlineCompositeListElementCount();
......@@ -547,9 +547,9 @@ struct WireHelpers {
} else {
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.");
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.");
// Subtract 1 from the size for the NUL terminator.
......@@ -584,9 +584,9 @@ struct WireHelpers {
} else {
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.");
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.");
return Data::Builder(reinterpret_cast<char*>(ptr), ref->listRef.elementCount() / ELEMENTS);
......@@ -810,14 +810,14 @@ struct WireHelpers {
break;
case FieldSize::INLINE_COMPOSITE:
CAPNPROTO_ASSERT(false, "can't get here");
FAIL_CHECK();
break;
}
return ListReader(segment, ptr, ref->listRef.elementCount(), step,
dataSize, referenceCount, nestingLimit - 1);
} 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.");
goto useDefault;
}
......@@ -1102,7 +1102,7 @@ Data::Builder ListBuilder::getDataElement(WireReferenceCount index) const {
ListReader ListBuilder::asReader(FieldSize elementSize) const {
// 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.");
return ListReader(segment, ptr, elementCount, bitsPerElement(elementSize),
std::numeric_limits<int>::max());
......
......@@ -193,10 +193,12 @@ struct List<T, true> {
for (; i != end && pos < size(); ++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) {
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++) {
set(i, other.begin()[i]);
}
......@@ -398,10 +400,12 @@ struct List<Data, false> {
for (; i != end && pos < size(); ++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) {
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++) {
set(i, other.begin()[i]);
}
......@@ -460,10 +464,12 @@ struct List<Text, false> {
for (; i != end && pos < size(); ++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) {
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++) {
set(i, other.begin()[i]);
}
......
......@@ -27,6 +27,9 @@
#include <gtest/gtest.h>
#include <string>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
namespace capnproto {
namespace internal {
......@@ -111,6 +114,35 @@ TEST(Logging, Log) {
EXPECT_EQ("fatal exception: " + fileLine(__FILE__, line) + ": precondition not met: expected "
"1 == 2; i = 123; hi; str = foo\n", mockCallback.text);
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
......
......@@ -25,10 +25,12 @@
#include "logging.h"
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <errno.h>
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) {
static const char* SEVERITY_STRINGS[] = {
......@@ -42,8 +44,16 @@ ArrayPtr<const char> operator*(const Stringifier&, Log::Severity severity) {
return arrayPtr(s, strlen(s));
}
static Array<char> makeDescription(const char* condition, const char* macroArgs,
ArrayPtr<Array<char>> argValues) {
namespace {
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()];
if (argValues.size() > 0) {
......@@ -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> conditionArray = condition == nullptr ? nullptr : arrayPtr(condition);
ArrayPtr<const char> codeArray = style == LOG ? nullptr : arrayPtr(code);
ArrayPtr<const char> sep = 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;
if (condition != nullptr) {
totalSize += expected.size() + conditionArray.size();
switch (style) {
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++) {
if (i > 0 || condition != nullptr) totalSize += delim.size();
if (i > 0 || style != LOG) {
totalSize += delim.size();
}
if (argNames[i].size() > 0 && argNames[i][0] != '\"') {
totalSize += argNames[i].size() + sep.size();
}
......@@ -109,12 +159,23 @@ static Array<char> makeDescription(const char* condition, const char* macroArgs,
ArrayBuilder<char> result(totalSize);
if (condition != nullptr) {
result.addAll(expected);
result.addAll(conditionArray);
switch (style) {
case LOG:
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++) {
if (i > 0 || condition != nullptr) result.addAll(delim);
if (i > 0 || style != LOG) {
result.addAll(delim);
}
if (argNames[i].size() > 0 && argNames[i][0] != '\"') {
result.addAll(argNames[i]);
result.addAll(sep);
......@@ -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,
ArrayPtr<Array<char>> argValues) {
getExceptionCallback()->logMessage(
str(severity, ": ", file, ":", line, ": ",
makeDescription(nullptr, macroArgs, argValues), '\n'));
makeDescription(LOG, nullptr, 0, macroArgs, argValues), '\n'));
}
void Log::recoverableFaultInternal(
......@@ -138,7 +201,7 @@ void Log::recoverableFaultInternal(
const char* condition, const char* macroArgs, ArrayPtr<Array<char>> argValues) {
getExceptionCallback()->onRecoverableException(
Exception(nature, Exception::Durability::PERMANENT, file, line,
makeDescription(condition, macroArgs, argValues)));
makeDescription(ASSERTION, condition, 0, macroArgs, argValues)));
}
void Log::fatalFaultInternal(
......@@ -146,8 +209,30 @@ void Log::fatalFaultInternal(
const char* condition, const char* macroArgs, ArrayPtr<Array<char>> argValues) {
getExceptionCallback()->onFatalException(
Exception(nature, Exception::Durability::PERMANENT, file, line,
makeDescription(condition, macroArgs, argValues)));
makeDescription(ASSERTION, condition, 0, macroArgs, argValues)));
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
This diff is collapsed.
......@@ -23,18 +23,23 @@
#define CAPNPROTO_PRIVATE
#include "macros.h"
#include "exception.h"
#include <unistd.h>
#include <stdio.h>
#include "exception.h"
#include "util.h"
#include "logging.h"
#include <stdlib.h>
namespace capnproto {
namespace internal {
void assertionFailure(const char* file, int line, const char* expectation, const char* message) {
throw Exception(Exception::Nature::LOCAL_BUG, Exception::Durability::PERMANENT,
file, line, str(expectation));
void inlinePreconditionFailure(const char* file, int line, const char* expectation,
const char* macroArgs, const char* message) {
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
......
......@@ -57,19 +57,22 @@ namespace internal {
#define CAPNPROTO_NORETURN __attribute__((noreturn));
void assertionFailure(const char* file, int line, const char* expectation, const char* message)
CAPNPROTO_NORETURN;
void inlinePreconditionFailure(
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
#define CAPNPROTO_DEBUG_ASSERT(condition, message)
#define CAPNPROTO_INLINE_DPRECOND(...)
#else
#define CAPNPROTO_DEBUG_ASSERT(condition, message) CAPNPROTO_ASSERT(condition, message)
#define CAPNPROTO_INLINE_DPRECOND CAPNPROTO_INLINE_PRECOND
#endif
// Allocate an array, preferably on the stack, unless it is too big. On GCC this will use
......
......@@ -21,7 +21,9 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define CAPNPROTO_PRIVATE
#include "message.h"
#include "logging.h"
#include "arena.h"
#include "stdlib.h"
#include <exception>
......@@ -78,10 +80,10 @@ internal::SegmentBuilder* MessageBuilder::getRootSegment() {
WordCount refSize = 1 * REFERENCES * WORDS_PER_REFERENCE;
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.");
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.");
return segment;
}
......@@ -111,28 +113,12 @@ ArrayPtr<const ArrayPtr<const word>> MessageBuilder::getSegmentsForOutput() {
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 {
public:
virtual ~ThrowingErrorReporter() {}
void reportError(const char* description) override {
std::string message("Cap'n Proto message was invalid: ");
message += description;
throw ParseException(std::move(message));
FAIL_VALIDATE_INPUT("Invalid Cap'n Proto message", description);
}
};
......@@ -201,11 +187,11 @@ MallocMessageBuilder::MallocMessageBuilder(
ArrayPtr<word> firstSegment, AllocationStrategy allocationStrategy)
: nextSize(firstSegment.size()), allocationStrategy(allocationStrategy),
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.
CAPNPROTO_ASSERT(*reinterpret_cast<uint64_t*>(firstSegment.begin()) == 0,
"First segment must be zeroed.");
PRECOND(*reinterpret_cast<uint64_t*>(firstSegment.begin()) == 0,
"First segment must be zeroed.");
}
MallocMessageBuilder::~MallocMessageBuilder() {
......@@ -216,7 +202,7 @@ MallocMessageBuilder::~MallocMessageBuilder() {
// Must zero first segment.
ArrayPtr<const ArrayPtr<const word>> segments = getSegmentsForOutput();
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?");
memset(firstSegment, 0, segments[0].size() * sizeof(word));
}
......@@ -247,7 +233,7 @@ ArrayPtr<word> MallocMessageBuilder::allocateSegment(uint minimumSize) {
void* result = calloc(size, sizeof(word));
if (result == nullptr) {
throw std::bad_alloc();
FAIL_SYSCALL("calloc(size, sizeof(word))", ENOMEM, size);
}
if (!returnedFirstSegment) {
......
......@@ -21,8 +21,10 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "test.capnp.h"
#define CAPNPROTO_PRIVATE
#include "serialize-packed.h"
#include "test.capnp.h"
#include "logging.h"
#include <gtest/gtest.h>
#include <string>
#include <stdlib.h>
......@@ -60,7 +62,7 @@ public:
}
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));
memcpy(buffer, data.data() + readPos, amount);
readPos += amount;
......@@ -68,7 +70,7 @@ public:
}
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;
}
......
......@@ -21,7 +21,9 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define CAPNPROTO_PRIVATE
#include "serialize-packed.h"
#include "logging.h"
#include "layout.h"
#include <vector>
......@@ -37,23 +39,25 @@ size_t PackedInputStream::read(void* dst, size_t minBytes, size_t maxBytes) {
return 0;
}
CAPNPROTO_DEBUG_ASSERT(minBytes % sizeof(word) == 0,
"PackedInputStream reads must be word-aligned.");
CAPNPROTO_DEBUG_ASSERT(maxBytes % sizeof(word) == 0,
"PackedInputStream reads must be word-aligned.");
DPRECOND(minBytes % sizeof(word) == 0, "PackedInputStream reads must be word-aligned.");
DPRECOND(maxBytes % sizeof(word) == 0, "PackedInputStream reads must be word-aligned.");
uint8_t* __restrict__ out = reinterpret_cast<uint8_t*>(dst);
uint8_t* const outEnd = reinterpret_cast<uint8_t*>(dst) + maxBytes;
uint8_t* const outMin = reinterpret_cast<uint8_t*>(dst) + minBytes;
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());
#define REFRESH_BUFFER() \
inner.skip(buffer.size()); \
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())
#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) {
for (;;) {
uint8_t tag;
CAPNPROTO_DEBUG_ASSERT((out - reinterpret_cast<uint8_t*>(dst)) % sizeof(word) == 0,
"Output pointer should always be aligned here.");
DCHECK((out - reinterpret_cast<uint8_t*>(dst)) % sizeof(word) == 0,
"Output pointer should always be aligned here.");
if (BUFFER_REMAINING < 10) {
if (out >= outMin) {
......@@ -118,24 +122,26 @@ size_t PackedInputStream::read(void* dst, size_t minBytes, size_t maxBytes) {
}
if (tag == 0) {
CAPNPROTO_DEBUG_ASSERT(BUFFER_REMAINING > 0,
"Bug in this function: Should always have non-empty buffer here.");
DCHECK(BUFFER_REMAINING > 0, "Should always have non-empty buffer here.");
uint runLength = *in++ * sizeof(word);
CAPNPROTO_ASSERT(runLength <= outEnd - out,
"Packed input did not end cleanly on a segment boundary.");
VALIDATE_INPUT(runLength <= outEnd - out,
"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);
out += runLength;
} else if (tag == 0xffu) {
CAPNPROTO_DEBUG_ASSERT(BUFFER_REMAINING > 0,
"Bug in this function: Should always have non-empty buffer here.");
DCHECK(BUFFER_REMAINING > 0, "Should always have non-empty buffer here.");
uint runLength = *in++ * sizeof(word);
CAPNPROTO_ASSERT(runLength <= outEnd - out,
"Packed input did not end cleanly on a segment boundary.");
VALIDATE_INPUT(runLength <= outEnd - out,
"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;
if (inRemaining >= runLength) {
......@@ -171,8 +177,9 @@ size_t PackedInputStream::read(void* dst, size_t minBytes, size_t maxBytes) {
}
}
CAPNPROTO_ASSERT(false, "Can't get here.");
return 0;
FAIL_CHECK("Can't get here.");
#undef REFRESH_BUFFER
}
void PackedInputStream::skip(size_t bytes) {
......@@ -182,12 +189,17 @@ void PackedInputStream::skip(size_t bytes) {
return;
}
CAPNPROTO_DEBUG_ASSERT(bytes % sizeof(word) == 0,
"PackedInputStream reads must be word-aligned.");
DPRECOND(bytes % sizeof(word) == 0, "PackedInputStream reads must be word-aligned.");
ArrayPtr<const byte> buffer = inner.getReadBuffer();
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 (;;) {
uint8_t tag;
......@@ -235,24 +247,26 @@ void PackedInputStream::skip(size_t bytes) {
}
if (tag == 0) {
CAPNPROTO_DEBUG_ASSERT(BUFFER_REMAINING > 0,
"Bug in this function: Should always have non-empty buffer here.");
DCHECK(BUFFER_REMAINING > 0, "Should always have non-empty buffer here.");
uint runLength = *in++ * sizeof(word);
CAPNPROTO_ASSERT(runLength <= bytes,
"Packed input did not end cleanly on a segment boundary.");
VALIDATE_INPUT(runLength <= bytes,
"Packed input did not end cleanly on a segment boundary.") {
return;
}
bytes -= runLength;
} else if (tag == 0xffu) {
CAPNPROTO_DEBUG_ASSERT(BUFFER_REMAINING > 0,
"Bug in this function: Should always have non-empty buffer here.");
DCHECK(BUFFER_REMAINING > 0, "Should always have non-empty buffer here.");
uint runLength = *in++ * sizeof(word);
CAPNPROTO_ASSERT(runLength <= bytes,
"Packed input did not end cleanly on a segment boundary.");
VALIDATE_INPUT(runLength <= bytes,
"Packed input did not end cleanly on a segment boundary.") {
return;
}
bytes -= runLength;
......@@ -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 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "test.capnp.h"
#define CAPNPROTO_PRIVATE
#include "serialize-snappy.h"
#include "logging.h"
#include "test.capnp.h"
#include <gtest/gtest.h>
#include <string>
#include <stdlib.h>
......@@ -94,7 +96,7 @@ public:
}
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));
memcpy(buffer, data.data() + readPos, amount);
readPos += amount;
......@@ -102,7 +104,7 @@ public:
}
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;
}
......
......@@ -21,7 +21,9 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define CAPNPROTO_PRIVATE
#include "serialize-snappy.h"
#include "logging.h"
#include "layout.h"
#include <snappy/snappy.h>
#include <snappy/snappy-sinksource.h>
......@@ -38,7 +40,7 @@ public:
// implements snappy::Source ---------------------------------------
size_t Available() const override {
CAPNPROTO_ASSERT(false, "Snappy doesn't actually call this.");
FAIL_CHECK("Snappy doesn't actually call this.");
return 0;
}
......@@ -104,10 +106,12 @@ void SnappyInputStream::skip(size_t bytes) {
void SnappyInputStream::refill() {
uint32_t length = 0;
InputStreamSnappySource snappySource(inner);
CAPNPROTO_ASSERT(
VALIDATE_INPUT(
snappy::RawUncompress(
&snappySource, reinterpret_cast<char*>(buffer.begin()), buffer.size(), &length),
"Snappy decompression failed.");
"Snappy decompression failed.") {
length = 1; // garbage
}
bufferAvailable = buffer.slice(0, length);
}
......@@ -117,8 +121,7 @@ void SnappyInputStream::refill() {
SnappyOutputStream::SnappyOutputStream(
OutputStream& inner, ArrayPtr<byte> buffer, ArrayPtr<byte> compressedBuffer)
: inner(inner) {
CAPNPROTO_DEBUG_ASSERT(
SNAPPY_COMPRESSED_BUFFER_SIZE >= snappy::MaxCompressedLength(snappy::kBlockSize),
DCHECK(SNAPPY_COMPRESSED_BUFFER_SIZE >= snappy::MaxCompressedLength(snappy::kBlockSize),
"snappy::MaxCompressedLength() changed?");
if (buffer.size() < SNAPPY_BUFFER_SIZE) {
......@@ -156,7 +159,7 @@ void SnappyOutputStream::flush() {
snappy::UncheckedByteArraySink sink(reinterpret_cast<char*>(compressedBuffer.begin()));
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.");
inner.write(compressedBuffer.begin(), n);
......
......@@ -21,8 +21,10 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "test.capnp.h"
#define CAPNPROTO_PRIVATE
#include "serialize.h"
#include "logging.h"
#include "test.capnp.h"
#include <gtest/gtest.h>
#include <string>
#include <stdlib.h>
......@@ -102,7 +104,7 @@ public:
~TestInputStream() {}
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;
memcpy(buffer, pos, amount);
pos += amount;
......
......@@ -21,8 +21,10 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define CAPNPROTO_PRIVATE
#include "serialize.h"
#include "layout.h"
#include "logging.h"
namespace capnproto {
......@@ -87,7 +89,7 @@ ArrayPtr<const word> FlatArrayMessageReader::getSegment(uint id) {
}
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;
......@@ -121,7 +123,7 @@ Array<word> messageToFlatArray(ArrayPtr<const ArrayPtr<const word>> segments) {
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);
}
......@@ -141,7 +143,10 @@ InputStreamMessageReader::InputStreamMessageReader(
size_t totalWords = segment0Size;
// 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.
internal::WireValue<uint32_t> moreSizes[segmentCount & ~1];
......@@ -155,9 +160,13 @@ InputStreamMessageReader::InputStreamMessageReader(
// 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
// size to make the receiver allocate excessive space and possibly crash.
CAPNPROTO_ASSERT(totalWords <= options.traversalLimitInWords,
"Message is too large. To increase the limit on the receiving end, see "
"capnproto::ReaderOptions.");
VALIDATE_INPUT(totalWords <= options.traversalLimitInWords,
"Message is too large. To increase the limit on the receiving end, see "
"capnproto::ReaderOptions.") {
segmentCount = 1;
segment0Size = std::min<size_t>(segment0Size, options.traversalLimitInWords);
totalWords = segment0Size;
}
if (scratchSpace.size() < totalWords) {
// TODO: Consider allocating each segment as a separate chunk to reduce memory fragmentation.
......@@ -228,7 +237,7 @@ ArrayPtr<const word> InputStreamMessageReader::getSegment(uint id) {
// -------------------------------------------------------------------
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)];
......
......@@ -91,7 +91,7 @@ public:
inline std::size_t size() const { return size_; }
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];
}
......@@ -101,7 +101,7 @@ public:
inline T& back() const { return *(ptr + size_ - 1); }
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);
}
......@@ -158,7 +158,7 @@ public:
inline std::size_t size() const { return size_; }
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];
}
......@@ -168,7 +168,7 @@ public:
inline T& back() const { return *(ptr + size_ - 1); }
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);
}
......@@ -226,7 +226,7 @@ public:
template <typename... 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)...);
++pos;
}
......@@ -245,7 +245,7 @@ public:
Array<T> finish() {
// 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.
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);
ptr = nullptr;
pos = nullptr;
......
......@@ -21,6 +21,7 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define CAPNPROTO_PRIVATE
#include "util.h"
#include <gtest/gtest.h>
#include <string>
......
......@@ -21,7 +21,9 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define CAPNPROTO_PRIVATE
#include "util.h"
#include "logging.h"
#include <stdio.h>
#include <float.h>
#include <limits>
......@@ -105,8 +107,6 @@ namespace {
// implementation.
// ----------------------------------------------------------------------
#define GOOGLE_DCHECK(cond) CAPNPROTO_DEBUG_ASSERT(cond, "Bug in code here.");
#ifdef _WIN32
// MSVC has only _snprintf, not snprintf.
//
......@@ -200,7 +200,7 @@ char* DoubleToBuffer(double value, char* buffer) {
// The snprintf should never overflow because the buffer is significantly
// 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
// write it out to the stack. Otherwise, it may keep the value in a
......@@ -214,7 +214,7 @@ char* DoubleToBuffer(double value, char* buffer) {
snprintf(buffer, kDoubleToBufferSize, "%.*g", DBL_DIG+2, value);
// Should never overflow; see above.
GOOGLE_DCHECK(snprintf_result > 0 && snprintf_result < kDoubleToBufferSize);
DCHECK(snprintf_result > 0 && snprintf_result < kDoubleToBufferSize);
}
DelocalizeRadix(buffer);
......@@ -256,7 +256,7 @@ char* FloatToBuffer(float value, char* buffer) {
// The snprintf should never overflow because the buffer is significantly
// 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;
if (!safe_strtof(buffer, &parsed_value) || parsed_value != value) {
......@@ -264,7 +264,7 @@ char* FloatToBuffer(float value, char* buffer) {
snprintf(buffer, kFloatToBufferSize, "%.*g", FLT_DIG+2, value);
// Should never overflow; see above.
GOOGLE_DCHECK(snprintf_result > 0 && snprintf_result < kFloatToBufferSize);
DCHECK(snprintf_result > 0 && snprintf_result < kFloatToBufferSize);
}
DelocalizeRadix(buffer);
......
......@@ -24,6 +24,10 @@
#ifndef 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 <utility>
#include <type_traits>
......@@ -39,6 +43,8 @@ namespace capnproto {
template <typename T, size_t fixedSize>
class FixedArray {
// A fixed-width array whose storage is allocated inline rather than on the heap.
public:
inline size_t size() const { return fixedSize; }
inline T* begin() { return content; }
......@@ -62,6 +68,9 @@ private:
template <typename T, size_t fixedSize>
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:
inline constexpr CappedArray(): currentSize(fixedSize) {}
inline explicit constexpr CappedArray(size_t s): currentSize(s) {}
......@@ -82,8 +91,10 @@ private:
T content[fixedSize];
};
template <typename T>
Array<T> iterableToArray(T&& a) {
template <typename T, typename Container>
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());
auto i = a.iterator();
auto end = a.end();
......@@ -135,7 +146,15 @@ Element* fill(Element* __restrict__ target, First&& first, Rest&&... rest) {
template <typename Element, typename... 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()...}));
#endif
fill(result.begin(), std::forward<Params>(params)...);
return result;
}
......@@ -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 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.
// Declaring `operator*` with `Stringifier` as the left operand does not harm any other
// namespaces.
// the problem that it pollutes other people's namespaces and even the global namespace. For
// example, some other project may already have functions called `toString` which do something
// 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*(const char* s) const { return arrayPtr(s, strlen(s)); }
......@@ -181,6 +201,12 @@ static constexpr Stringifier STR;
template <typename... 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)...);
}
......
......@@ -168,8 +168,8 @@ inline {{structName}}::{{unionTitleCase}} {{structName}}::Reader::which{{unionTi
{{#fieldIsPrimitive}}
inline {{fieldType}} {{structName}}::Reader::get{{fieldTitleCase}}() {
{{#fieldUnion}}
CAPNPROTO_DEBUG_ASSERT(which{{unionTitleCase}}() == {{unionTitleCase}}::{{fieldUpperCase}},
"Must check which() before get()ing a union member.");
CAPNPROTO_INLINE_DPRECOND(which{{unionTitleCase}}() == {{unionTitleCase}}::{{fieldUpperCase}},
"Must check which() before get()ing a union member.");
{{/fieldUnion}}
return _reader.getDataField<{{fieldType}}>(
{{fieldOffset}} * ::capnproto::ELEMENTS{{fieldDefaultMask}});
......@@ -178,8 +178,8 @@ inline {{fieldType}} {{structName}}::Reader::get{{fieldTitleCase}}() {
{{#fieldIsBlob}}
inline {{fieldType}}::Reader {{structName}}::Reader::get{{fieldTitleCase}}() {
{{#fieldUnion}}
CAPNPROTO_DEBUG_ASSERT(which{{unionTitleCase}}() == {{unionTitleCase}}::{{fieldUpperCase}},
"Must check which() before get()ing a union member.");
CAPNPROTO_INLINE_DPRECOND(which{{unionTitleCase}}() == {{unionTitleCase}}::{{fieldUpperCase}},
"Must check which() before get()ing a union member.");
{{/fieldUnion}}
return _reader.get{{fieldBlobType}}Field(
{{fieldOffset}} * ::capnproto::REFERENCES,
......@@ -192,8 +192,8 @@ inline {{fieldType}}::Reader {{structName}}::Reader::get{{fieldTitleCase}}() {
{{#fieldIsStruct}}
inline {{fieldType}}::Reader {{structName}}::Reader::get{{fieldTitleCase}}() {
{{#fieldUnion}}
CAPNPROTO_DEBUG_ASSERT(which{{unionTitleCase}}() == {{unionTitleCase}}::{{fieldUpperCase}},
"Must check which() before get()ing a union member.");
CAPNPROTO_INLINE_DPRECOND(which{{unionTitleCase}}() == {{unionTitleCase}}::{{fieldUpperCase}},
"Must check which() before get()ing a union member.");
{{/fieldUnion}}
return {{fieldType}}::Reader(_reader.getStructField(
{{fieldOffset}} * ::capnproto::REFERENCES,
......@@ -204,8 +204,8 @@ inline {{fieldType}}::Reader {{structName}}::Reader::get{{fieldTitleCase}}() {
{{#fieldIsList}}
inline {{fieldType}}::Reader {{structName}}::Reader::get{{fieldTitleCase}}() {
{{#fieldUnion}}
CAPNPROTO_DEBUG_ASSERT(which{{unionTitleCase}}() == {{unionTitleCase}}::{{fieldUpperCase}},
"Must check which() before get()ing a union member.");
CAPNPROTO_INLINE_DPRECOND(which{{unionTitleCase}}() == {{unionTitleCase}}::{{fieldUpperCase}},
"Must check which() before get()ing a union member.");
{{/fieldUnion}}
return {{fieldType}}::Reader(_reader.getListField(
{{fieldOffset}} * ::capnproto::REFERENCES,
......@@ -231,8 +231,8 @@ inline {{structName}}::{{unionTitleCase}} {{structName}}::Builder::which{{unionT
{{#fieldIsPrimitive}}
inline {{fieldType}} {{structName}}::Builder::get{{fieldTitleCase}}() {
{{#fieldUnion}}
CAPNPROTO_DEBUG_ASSERT(which{{unionTitleCase}}() == {{unionTitleCase}}::{{fieldUpperCase}},
"Must check which() before get()ing a union member.");
CAPNPROTO_INLINE_DPRECOND(which{{unionTitleCase}}() == {{unionTitleCase}}::{{fieldUpperCase}},
"Must check which() before get()ing a union member.");
{{/fieldUnion}}
return _builder.getDataField<{{fieldType}}>(
{{fieldOffset}} * ::capnproto::ELEMENTS{{fieldDefaultMask}});
......@@ -249,8 +249,8 @@ inline void {{structName}}::Builder::set{{fieldTitleCase}}({{fieldType}} value)
{{#fieldIsBlob}}
inline {{fieldType}}::Builder {{structName}}::Builder::get{{fieldTitleCase}}() {
{{#fieldUnion}}
CAPNPROTO_DEBUG_ASSERT(which{{unionTitleCase}}() == {{unionTitleCase}}::{{fieldUpperCase}},
"Must check which() before get()ing a union member.");
CAPNPROTO_INLINE_DPRECOND(which{{unionTitleCase}}() == {{unionTitleCase}}::{{fieldUpperCase}},
"Must check which() before get()ing a union member.");
{{/fieldUnion}}
return _builder.get{{fieldBlobType}}Field({{fieldOffset}} * ::capnproto::REFERENCES,
{{#fieldDefaultBytes}}
......@@ -285,8 +285,8 @@ inline {{fieldType}}::Builder {{structName}}::Builder::init{{fieldTitleCase}}()
}
inline {{fieldType}}::Builder {{structName}}::Builder::get{{fieldTitleCase}}() {
{{#fieldUnion}}
CAPNPROTO_DEBUG_ASSERT(which{{unionTitleCase}}() == {{unionTitleCase}}::{{fieldUpperCase}},
"Must check which() before get()ing a union member.");
CAPNPROTO_INLINE_DPRECOND(which{{unionTitleCase}}() == {{unionTitleCase}}::{{fieldUpperCase}},
"Must check which() before get()ing a union member.");
{{/fieldUnion}}
return {{fieldType}}::Builder(_builder.getStructField(
{{fieldOffset}} * ::capnproto::REFERENCES, {{fieldType}}::STRUCT_SIZE,
......@@ -307,8 +307,8 @@ inline {{fieldType}}::Builder {{structName}}::Builder::init{{fieldTitleCase}}(un
}
inline {{fieldType}}::Builder {{structName}}::Builder::get{{fieldTitleCase}}() {
{{#fieldUnion}}
CAPNPROTO_DEBUG_ASSERT(which{{unionTitleCase}}() == {{unionTitleCase}}::{{fieldUpperCase}},
"Must check which() before get()ing a union member.");
CAPNPROTO_INLINE_DPRECOND(which{{unionTitleCase}}() == {{unionTitleCase}}::{{fieldUpperCase}},
"Must check which() before get()ing a union member.");
{{/fieldUnion}}
return {{fieldType}}::Builder(_builder.getListField(
{{fieldOffset}} * ::capnproto::REFERENCES,
......@@ -356,8 +356,8 @@ inline {{fieldType}}::Builder {{structName}}::Builder::init{{fieldTitleCase}}(un
}
inline {{fieldType}}::Builder {{structName}}::Builder::get{{fieldTitleCase}}() {
{{#fieldUnion}}
CAPNPROTO_DEBUG_ASSERT(which{{unionTitleCase}}() == {{unionTitleCase}}::{{fieldUpperCase}},
"Must check which() before get()ing a union member.");
CAPNPROTO_INLINE_DPRECOND(which{{unionTitleCase}}() == {{unionTitleCase}}::{{fieldUpperCase}},
"Must check which() before get()ing a union member.");
{{/fieldUnion}}
return {{fieldType}}::Builder(_builder.getListField(
{{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