Commit dd46a758 authored by Kenton Varda's avatar Kenton Varda

Type-safe quantities catch bugs.

parent 92a64de2
// Copyright (c) 2013, Kenton Varda <temporal@gmail.com>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "descriptor.h"
#include <gtest/gtest.h>
namespace capnproto {
namespace internal {
namespace {
const int READONLY_SEGMENT_START = 123;
const FieldDescriptor TEST_FIELDS[2] = {
{ WordCount8(1 * WORDS), 0, 0, FieldSize::FOUR_BYTES, 1, 0, 0, 0 },
{ WordCount8(1 * WORDS), 1, 0, FieldSize::REFERENCE, 1, 0, 0, 0 }
};
extern const StructDescriptor TEST_STRUCT;
extern const Descriptor* const TEST_STRUCT_DEFAULT_REFS[1] = {
&TEST_STRUCT.base
};
const AlignedData<1> TEST_STRUCT_DEFAULT_DATA = {
{ 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0 }
};
const StructDescriptor TEST_STRUCT = {
{ Descriptor::Kind::STRUCT },
2,
WordCount8(1 * WORDS),
1,
TEST_FIELDS,
TEST_STRUCT_DEFAULT_DATA.bytes,
TEST_STRUCT_DEFAULT_REFS
};
const int READONLY_SEGMENT_END = 321;
TEST(Descriptors, InReadOnlySegment) {
// It's extremely important that statically-initialized descriptors end up in the read-only
// segment, proving that they will not require any dynamic initialization at startup. We hackily
// assume that variables will be placed in each segment in the order that they appear, therefore
// if our test declarations above do in fact land in the read-only segment, they should appear
// between READONLY_SEGMENT_START and READONLY_SEGMENT_END.
EXPECT_LE((const void*)&READONLY_SEGMENT_START, (const void*)&TEST_FIELDS);
EXPECT_GE((const void*)&READONLY_SEGMENT_END , (const void*)&TEST_FIELDS);
EXPECT_LE((const void*)&READONLY_SEGMENT_START, (const void*)&TEST_STRUCT_DEFAULT_DATA);
EXPECT_GE((const void*)&READONLY_SEGMENT_END , (const void*)&TEST_STRUCT_DEFAULT_DATA);
EXPECT_LE((const void*)&READONLY_SEGMENT_START, (const void*)&TEST_STRUCT_DEFAULT_REFS);
EXPECT_GE((const void*)&READONLY_SEGMENT_END , (const void*)&TEST_STRUCT_DEFAULT_REFS);
EXPECT_LE((const void*)&READONLY_SEGMENT_START, (const void*)&TEST_STRUCT);
EXPECT_GE((const void*)&READONLY_SEGMENT_END , (const void*)&TEST_STRUCT);
}
} // namespace
} // namespace internal
} // namespace capnproto
......@@ -46,6 +46,15 @@ struct ListDescriptor;
struct StructDescriptor;
struct FieldDescriptor;
template <int wordCount>
union AlignedData {
// Useful for declaring static constant data blobs as an array of bytes, but forcing those
// bytes to be word-aligned.
uint8_t bytes[wordCount * sizeof(word)];
word words[wordCount];
};
struct Descriptor {
// This is the "base type" for descriptors that describe the target of a reference.
// StructDescriptor and ListDescriptor should be treated as if they subclass this type. However,
......@@ -79,17 +88,19 @@ enum class FieldSize: uint8_t {
// fields, since a struct cannot embed another struct inline.
};
inline int sizeInBits(FieldSize s) {
static const int table[] = {1, 8, 16, 32, 64, 64, 128, -1};
inline BitCount sizeInBits(FieldSize s) {
static constexpr BitCount table[] = {1*BITS, 8*BITS, 16*BITS, 32*BITS, 64*BITS,
64*BITS, 128*BITS, 0*BITS};
return table[static_cast<int>(s)];
}
inline int byteOffsetForFieldZero(FieldSize s) {
inline ByteCount byteOffsetForFieldZero(FieldSize s) {
// For the given field size, get the offset, in bytes, between a struct pointer and the location
// of the struct's first field, if the struct's first field is of the given type. We use this
// to adjust pointers when non-struct lists are converted to struct lists or vice versa.
static const int table[] = {1, 1, 2, 4, 8, 0, 8, 0};
static constexpr ByteCount table[] = {1*BYTES, 1*BYTES, 2*BYTES, 4*BYTES, 8*BYTES,
0*BYTES, 8*BYTES, 0*BYTES};
return table[static_cast<int>(s)];
}
......@@ -131,8 +142,9 @@ struct StructDescriptor {
uint8_t fieldCount;
// Number of fields in this type -- that we were aware of at compile time, of course.
uint8_t dataSize;
WordCount8 dataSize;
// Size of the data segment, in 64-bit words.
// TODO: Can we use WordCount here and still get static init?
uint8_t referenceCount;
// Number of references in the reference segment.
......@@ -146,9 +158,10 @@ struct StructDescriptor {
const Descriptor* const* defaultReferences;
// Array of descriptors describing the references.
inline uint32_t wordSize() const {
inline WordCount wordSize() const {
// Size of the struct in words.
return static_cast<uint32_t>(fieldCount) + static_cast<uint32_t>(referenceCount);
// TODO: Somehow use WORDS_PER_REFERENCE here.
return WordCount(dataSize) + referenceCount * WORDS;
}
};
static_assert(__builtin_offsetof(StructDescriptor, base) == 0,
......@@ -158,12 +171,12 @@ static_assert(__builtin_offsetof(StructDescriptor, base) == 0,
struct FieldDescriptor {
// Describes one field of a struct.
uint8_t requiredDataSize;
WordCount8 requiredDataSize;
// The minimum size of the data segment of any object which includes this field. This is always
// offset * size / 64 bits, rounded up. This value is useful for validating object references
// received on the wire -- if dataSize is insufficient to support fieldCount, don't trust it!
uint8_t requiredReferenceSize;
uint8_t requiredReferenceCount;
// The minimum size of the reference segment of any object which includes this field. Same deal
// as with requiredDataSize.
......
......@@ -24,7 +24,174 @@
#ifndef CAPNPROTO_MACROS_H_
#define CAPNPROTO_MACROS_H_
#include <inttypes.h>
#include "unit.h"
namespace capnproto {
// TODO: Rename file "common.h" since it's no longer just macros.
#define CAPNPROTO_DISALLOW_COPY(classname) \
classname(const classname&) = delete; \
classname& operator=(const classname&) = delete
typedef unsigned int uint;
class byte { uint8_t content; CAPNPROTO_DISALLOW_COPY(byte); };
class word { uint64_t content; CAPNPROTO_DISALLOW_COPY(word); };
// byte and word are opaque types with sizes of 8 and 64 bits, respectively. These types are useful
// only to make pointer arithmetic clearer. Since the contents are private, the only way to access
// them is to first reinterpret_cast to some other pointer type.
//
// Coping is disallowed because you should always use memcpy(). Otherwise, you may run afoul of
// aliasing rules (particularly when copying words).
//
// A pointer of type word* should always be word-aligned even if won't actually be dereferenced as
// that type.
static_assert(sizeof(byte) == 1, "uint8_t is not one byte?");
static_assert(sizeof(word) == 8, "uint64_t is not 8 bytes?");
namespace internal { class bit; }
#ifdef __CDT_PARSER__
// Eclipse gets confused by decltypes, so we'll feed it these simplified-yet-compatible definitions.
//
// We could also consider using these definitions in opt builds. Trouble is, the mangled symbol
// names of any functions that take these types as inputs would be affected, so it would be
// important to compile the Cap'n Proto library and the client app with the same flags.
typedef uint BitCount;
typedef uint8_t BitCount8;
typedef uint16_t BitCount16;
typedef uint32_t BitCount32;
typedef uint64_t BitCount64;
typedef uint ByteCount;
typedef uint8_t ByteCount8;
typedef uint16_t ByteCount16;
typedef uint32_t ByteCount32;
typedef uint64_t ByteCount64;
typedef uint WordCount;
typedef uint8_t WordCount8;
typedef uint16_t WordCount16;
typedef uint32_t WordCount32;
typedef uint64_t WordCount64;
constexpr BitCount BITS = 1;
constexpr ByteCount BYTES = 1;
constexpr WordCount WORDS = 1;
constexpr uint BITS_PER_BYTE = 8;
constexpr uint BITS_PER_WORD = 64;
constexpr uint BYTES_PER_WORD = 8;
#else
typedef UnitMeasure<uint, internal::bit> BitCount;
typedef UnitMeasure<uint8_t, internal::bit> BitCount8;
typedef UnitMeasure<uint16_t, internal::bit> BitCount16;
typedef UnitMeasure<uint32_t, internal::bit> BitCount32;
typedef UnitMeasure<uint64_t, internal::bit> BitCount64;
typedef UnitMeasure<uint, byte> ByteCount;
typedef UnitMeasure<uint8_t, byte> ByteCount8;
typedef UnitMeasure<uint16_t, byte> ByteCount16;
typedef UnitMeasure<uint32_t, byte> ByteCount32;
typedef UnitMeasure<uint64_t, byte> ByteCount64;
typedef UnitMeasure<uint, word> WordCount;
typedef UnitMeasure<uint8_t, word> WordCount8;
typedef UnitMeasure<uint16_t, word> WordCount16;
typedef UnitMeasure<uint32_t, word> WordCount32;
typedef UnitMeasure<uint64_t, word> WordCount64;
constexpr BitCount BITS = BitCount::ONE;
constexpr ByteCount BYTES = ByteCount::ONE;
constexpr WordCount WORDS = WordCount::ONE;
constexpr UnitRatio<uint, internal::bit, byte> BITS_PER_BYTE(8);
constexpr UnitRatio<uint, internal::bit, word> BITS_PER_WORD(64);
constexpr UnitRatio<uint, byte, word> BYTES_PER_WORD(8);
template <typename T>
inline constexpr byte* operator+(byte* ptr, UnitMeasure<T, byte> offset) {
return ptr + offset / BYTES;
}
template <typename T>
inline constexpr const byte* operator+(const byte* ptr, UnitMeasure<T, byte> offset) {
return ptr + offset / BYTES;
}
template <typename T>
inline constexpr byte* operator+=(byte*& ptr, UnitMeasure<T, byte> offset) {
return ptr = ptr + offset / BYTES;
}
template <typename T>
inline constexpr const byte* operator+=(const byte* ptr, UnitMeasure<T, byte> offset) {
return ptr = ptr + offset / BYTES;
}
template <typename T>
inline constexpr word* operator+(word* ptr, UnitMeasure<T, word> offset) {
return ptr + offset / WORDS;
}
template <typename T>
inline constexpr const word* operator+(const word* ptr, UnitMeasure<T, word> offset) {
return ptr + offset / WORDS;
}
template <typename T>
inline constexpr word* operator+=(word*& ptr, UnitMeasure<T, word> offset) {
return ptr = ptr + offset / WORDS;
}
template <typename T>
inline constexpr const word* operator+=(const word*& ptr, UnitMeasure<T, word> offset) {
return ptr = ptr + offset / WORDS;
}
template <typename T>
inline constexpr byte* operator-(byte* ptr, UnitMeasure<T, byte> offset) {
return ptr - offset / BYTES;
}
template <typename T>
inline constexpr const byte* operator-(const byte* ptr, UnitMeasure<T, byte> offset) {
return ptr - offset / BYTES;
}
template <typename T>
inline constexpr byte* operator-=(byte*& ptr, UnitMeasure<T, byte> offset) {
return ptr = ptr - offset / BYTES;
}
template <typename T>
inline constexpr const byte* operator-=(const byte* ptr, UnitMeasure<T, byte> offset) {
return ptr = ptr - offset / BYTES;
}
template <typename T>
inline constexpr word* operator-(word* ptr, UnitMeasure<T, word> offset) {
return ptr - offset / WORDS;
}
template <typename T>
inline constexpr const word* operator-(const word* ptr, UnitMeasure<T, word> offset) {
return ptr - offset / WORDS;
}
template <typename T>
inline constexpr word* operator-=(word*& ptr, UnitMeasure<T, word> offset) {
return ptr = ptr - offset / WORDS;
}
template <typename T>
inline constexpr const word* operator-=(const word*& ptr, UnitMeasure<T, word> offset) {
return ptr = ptr - offset / WORDS;
}
#endif
inline constexpr ByteCount intervalLength(const byte* a, const byte* b) {
return uint(b - a) * BYTES;
}
inline constexpr WordCount intervalLength(const word* a, const word* b) {
return uint(b - a) * WORDS;
}
namespace internal {
#define CAPNPROTO_EXPECT_TRUE(condition) __builtin_expect(condition, true)
......
......@@ -21,7 +21,6 @@
// (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 <inttypes.h>
#include <cstddef>
#include "macros.h"
......@@ -36,6 +35,22 @@ class MessageReader;
class MessageBuilder;
class ReadLimiter;
struct SegmentId {
// TODO: Generalize this. class Id<Base, Type>.
uint32_t number;
inline constexpr SegmentId(): number(0) {}
inline constexpr explicit SegmentId(int number): number(number) {}
inline constexpr bool operator==(const SegmentId& other) { return number == other.number; }
inline constexpr bool operator!=(const SegmentId& other) { return number != other.number; }
inline constexpr bool operator<=(const SegmentId& other) { return number <= other.number; }
inline constexpr bool operator>=(const SegmentId& other) { return number >= other.number; }
inline constexpr bool operator< (const SegmentId& other) { return number < other.number; }
inline constexpr bool operator> (const SegmentId& other) { return number > other.number; }
};
class MessageReader {
// Abstract interface encapsulating a readable message. By implementing this interface, you can
// control how memory is allocated for the message. Or use MallocMessage to make things easy.
......@@ -43,7 +58,7 @@ class MessageReader {
public:
virtual ~MessageReader();
virtual SegmentReader* tryGetSegment(uint32_t index) = 0;
virtual SegmentReader* tryGetSegment(SegmentId id) = 0;
// Gets the segment with the given ID, or return nullptr if no such segment exists.
virtual void reportInvalidData(const char* description) = 0;
......@@ -78,10 +93,10 @@ class MessageBuilder: public MessageReader {
public:
virtual ~MessageBuilder();
virtual SegmentBuilder* getSegment(uint32_t id) = 0;
virtual SegmentBuilder* getSegment(SegmentId id) = 0;
// Get the segment with the given id. Crashes or throws an exception if no such segment exists.
virtual SegmentBuilder* getSegmentWithAvailable(uint32_t minimumSize) = 0;
virtual SegmentBuilder* getSegmentWithAvailable(WordCount minimumAvailable) = 0;
// Get a segment which has at least the given amount of space available, allocating it if
// necessary. Crashes or throws an exception if there is not enough memory.
......@@ -95,7 +110,7 @@ class ReadLimiter {
// and overlapping are not permitted by the Cap'n Proto format because in many cases they could
// be used to craft a deceptively small message which could consume excessive server resources to
// process, perhaps even sending it into an infinite loop. Actually detecting overlaps would be
// time-consuming, so instead we just keep track of how many bytes worth of data structures the
// time-consuming, so instead we just keep track of how many words worth of data structures the
// receiver has actually dereferenced and error out if this gets too high.
//
// This counting takes place as you call getters (for non-primitive values) on the message
......@@ -104,67 +119,55 @@ class ReadLimiter {
// only trigger in unreasonable cases.
public:
inline explicit ReadLimiter(); // No limit.
inline explicit ReadLimiter(int64_t limit); // Limit to the given number of bytes.
inline explicit ReadLimiter(); // No limit.
inline explicit ReadLimiter(WordCount64 limit); // Limit to the given number of words.
inline bool canRead(uint32_t amount);
inline bool canRead(WordCount amount);
private:
int64_t counter;
WordCount64 limit;
};
class SegmentReader {
public:
inline SegmentReader(MessageReader* message, uint32_t id, const void* ptr, uint32_t size,
inline SegmentReader(MessageReader* message, SegmentId id, const word ptr[], WordCount size,
ReadLimiter* readLimiter);
CAPNPROTO_ALWAYS_INLINE(const void* getPtrChecked(
uint32_t offset, uint32_t bytesBefore, uint32_t bytesAfter));
CAPNPROTO_ALWAYS_INLINE(const word* getPtrChecked(
WordCount offset, WordCount before, WordCount after));
inline MessageReader* getMessage();
inline uint32_t getSegmentId();
inline SegmentId getSegmentId();
inline const void* getStartPtr();
inline uint32_t getSize();
inline const word* getStartPtr();
inline WordCount getOffsetTo(const word* ptr);
inline WordCount getSize();
private:
MessageReader* message;
uint32_t id;
uint32_t size;
const void* start;
SegmentId id;
WordCount size;
const word* start;
ReadLimiter* readLimiter;
SegmentReader(const SegmentReader& other) = delete;
SegmentReader& operator=(const SegmentReader& other) = delete;
void readLimitReached();
friend class SegmentBuilder;
};
class SegmentBuilder: public SegmentReader {
public:
inline SegmentBuilder(MessageBuilder* message, uint32_t id, void* ptr, uint32_t available);
inline SegmentBuilder(MessageBuilder* message, SegmentId id, word ptr[], WordCount available);
struct Allocation {
void* ptr;
uint32_t offset;
inline Allocation(): ptr(nullptr), offset(0) {}
inline Allocation(std::nullptr_t): ptr(nullptr), offset(0) {}
inline Allocation(void* ptr, uint32_t offset): ptr(ptr), offset(offset) {}
inline bool operator==(std::nullptr_t) const { return ptr == nullptr; }
};
CAPNPROTO_ALWAYS_INLINE(Allocation allocate(uint32_t amount));
inline void* getPtrUnchecked(uint32_t offset);
CAPNPROTO_ALWAYS_INLINE(word* allocate(WordCount amount));
inline word* getPtrUnchecked(WordCount offset);
inline MessageBuilder* getMessage();
private:
char* pos;
char* end;
word* pos;
word* end;
ReadLimiter dummyLimiter;
SegmentBuilder(const SegmentBuilder& other) = delete;
......@@ -177,64 +180,74 @@ private:
inline ReadLimiter::ReadLimiter()
// I didn't want to #include <limits> just for this one lousy constant.
: counter(0x7fffffffffffffffll) {}
: limit(uint64_t(0x7fffffffffffffffll) * WORDS) {}
inline ReadLimiter::ReadLimiter(int64_t limit): counter(limit) {}
inline ReadLimiter::ReadLimiter(WordCount64 limit): limit(limit) {}
inline bool ReadLimiter::canRead(uint32_t amount) {
return (counter -= amount) >= 0;
inline bool ReadLimiter::canRead(WordCount amount) {
if (amount > limit) {
return false;
} else {
limit -= amount;
return true;
}
}
// -------------------------------------------------------------------
inline SegmentReader::SegmentReader(
MessageReader* message, uint32_t id, const void* ptr, uint32_t size, ReadLimiter* readLimiter)
inline SegmentReader::SegmentReader(MessageReader* message, SegmentId id, const word ptr[],
WordCount size, ReadLimiter* readLimiter)
: message(message), id(id), size(size), start(ptr), readLimiter(readLimiter) {}
inline const void* SegmentReader::getPtrChecked(uint32_t offset, uint32_t bytesBefore,
uint32_t bytesAfter) {
inline const word* SegmentReader::getPtrChecked(
WordCount offset, WordCount before, WordCount after) {
// Check bounds. Watch out for overflow and underflow here.
if (offset > size || bytesBefore > offset || bytesAfter > size - offset) {
if (offset > size ||
before > offset ||
after > size - offset) {
return nullptr;
} else {
// Enforce the read limit. Synchronization is not necessary because readLimit is just a rough
// counter to prevent infinite loops leading to DoS attacks.
if (CAPNPROTO_EXPECT_FALSE(!readLimiter->canRead(bytesBefore + bytesAfter))) {
if (CAPNPROTO_EXPECT_FALSE(!readLimiter->canRead(before + after))) {
message->reportReadLimitReached();
}
return reinterpret_cast<const char*>(start) + offset;
return start + offset;
}
}
inline MessageReader* SegmentReader::getMessage() { return message; }
inline uint32_t SegmentReader::getSegmentId() { return id; }
inline const void* SegmentReader::getStartPtr() { return start; }
inline uint32_t SegmentReader::getSize() { return size; }
inline SegmentId SegmentReader::getSegmentId() { return id; }
inline const word* SegmentReader::getStartPtr() { return start; }
inline WordCount SegmentReader::getOffsetTo(const word* ptr) {
return intervalLength(start, ptr);
}
inline WordCount SegmentReader::getSize() { return size; }
// -------------------------------------------------------------------
inline SegmentBuilder::SegmentBuilder(
MessageBuilder* message, uint32_t id, void* ptr, uint32_t available)
: SegmentReader(message, id, ptr, 0, &dummyLimiter),
pos(reinterpret_cast<char*>(ptr)),
MessageBuilder* message, SegmentId id, word ptr[], WordCount available)
: SegmentReader(message, id, ptr, 0 * WORDS, &dummyLimiter),
pos(ptr),
end(pos + available) {}
inline SegmentBuilder::Allocation SegmentBuilder::allocate(uint32_t amount) {
if (amount > end - pos) {
inline word* SegmentBuilder::allocate(WordCount amount) {
if (amount > intervalLength(pos, end)) {
return nullptr;
} else {
char* result = pos;
word* result = pos;
pos += amount;
size += amount;
return Allocation(result, result - reinterpret_cast<const char*>(start));
return result;
}
}
inline void* SegmentBuilder::getPtrUnchecked(uint32_t offset) {
inline word* SegmentBuilder::getPtrUnchecked(WordCount offset) {
// const_cast OK because SegmentBuilder's constructor always initializes its SegmentReader base
// class with a pointer that was originally non-const.
return const_cast<char*>(reinterpret_cast<const char*>(start) + offset);
return const_cast<word*>(start + offset);
}
inline MessageBuilder* SegmentBuilder::getMessage() {
......
// Copyright (c) 2013, Kenton Varda <temporal@gmail.com>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "unit.h"
#include <gtest/gtest.h>
#include <iostream>
namespace capnproto {
namespace {
class Bytes;
class KiB;
class MiB;
typedef UnitMeasure<int, Bytes> ByteCount;
typedef UnitMeasure<int, KiB> KiBCount;
typedef UnitMeasure<int, MiB> MiBCount;
constexpr ByteCount BYTE = ByteCount::ONE;
constexpr KiBCount KIB = KiBCount::ONE;
constexpr MiBCount MIB = MiBCount::ONE;
constexpr UnitRatio<int, Bytes, KiB> BYTES_PER_KIB(1024);
constexpr UnitRatio<int, Bytes, MiB> BYTES_PER_MIB(1024*1024);
constexpr UnitRatio<int, KiB, MiB> KIB_PER_MIB(1024);
template <typename T, typename U>
std::ostream& operator<<(std::ostream& os, UnitMeasure<T, U> value) {
return os << (value / UnitMeasure<T, U>::ONE);
}
TEST(UnitMeasure, Basics) {
KiBCount k = 15 * KIB;
EXPECT_EQ(15, k / KIB);
EXPECT_EQ(16 * KIB, k + KIB);
k += KIB;
k *= 2048;
EXPECT_EQ(32 * MIB, k / KIB_PER_MIB);
EXPECT_TRUE(2 * KIB < 4 * KIB);
EXPECT_FALSE(8 * KIB < 4 * KIB);
}
} // namespace
} // namespace capnproto
// Copyright (c) 2013, Kenton Varda <temporal@gmail.com>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "unit.h"
namespace capnproto {
} // namespace capnproto
// Copyright (c) 2013, Kenton Varda <temporal@gmail.com>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef CAPNPROTO_UNIT_H_
#define CAPNPROTO_UNIT_H_
namespace capnproto {
template <typename Number, typename Unit1, typename Unit2>
class UnitRatio {
public:
constexpr UnitRatio(Number unit1PerUnit2): unit1PerUnit2(unit1PerUnit2) {}
Number unit1PerUnit2;
};
template <typename T>
T any();
template <typename Number, typename Unit>
class UnitMeasure {
public:
inline constexpr UnitMeasure() {}
template <typename OtherNumber>
inline constexpr UnitMeasure(const UnitMeasure<OtherNumber, Unit>& other)
: value(other.value) {}
static constexpr UnitMeasure ONE = UnitMeasure(1);
template <typename OtherNumber>
inline constexpr auto operator+(const UnitMeasure<OtherNumber, Unit>& other) const
-> UnitMeasure<decltype(any<Number>() + any<OtherNumber>()), Unit> {
return UnitMeasure<decltype(any<Number>() + any<OtherNumber>()), Unit>(value + other.value);
}
template <typename OtherNumber>
inline constexpr auto operator-(const UnitMeasure<OtherNumber, Unit>& other) const
-> UnitMeasure<decltype(any<Number>() - any<OtherNumber>()), Unit> {
return UnitMeasure<decltype(any<Number>() - any<OtherNumber>()), Unit>(value - other.value);
}
template <typename OtherNumber>
inline constexpr auto operator*(OtherNumber other) const
-> UnitMeasure<decltype(any<Number>() * other), Unit> {
return UnitMeasure<decltype(any<Number>() * other), Unit>(value * other);
}
template <typename OtherNumber>
inline constexpr auto operator/(OtherNumber other) const
-> UnitMeasure<decltype(any<Number>() / other), Unit> {
return UnitMeasure<decltype(any<Number>() / other), Unit>(value / other);
}
template <typename OtherNumber>
inline constexpr auto operator/(const UnitMeasure<OtherNumber, Unit>& other) const
-> decltype(any<Number>() / any<OtherNumber>()) {
return value / other.value;
}
template <typename OtherNumber>
inline constexpr auto operator%(const UnitMeasure<OtherNumber, Unit>& other) const
-> decltype(any<Number>() % any<OtherNumber>()) {
return value % other.value;
}
template <typename OtherNumber, typename OtherUnit>
inline constexpr auto operator*(const UnitRatio<OtherNumber, OtherUnit, Unit>& ratio) const
-> UnitMeasure<decltype(any<Number>() * any<OtherNumber>()), OtherUnit> {
return UnitMeasure<decltype(any<Number>() * any<OtherNumber>()), OtherUnit>(
value * ratio.unit1PerUnit2);
}
template <typename OtherNumber, typename OtherUnit>
inline constexpr auto operator/(const UnitRatio<OtherNumber, Unit, OtherUnit>& ratio) const
-> UnitMeasure<decltype(any<Number>() / any<OtherNumber>()), OtherUnit> {
return UnitMeasure<decltype(any<Number>() / any<OtherNumber>()), OtherUnit>(
value / ratio.unit1PerUnit2);
}
template <typename OtherNumber, typename OtherUnit>
inline constexpr auto operator%(const UnitRatio<OtherNumber, Unit, OtherUnit>& ratio) const
-> UnitMeasure<decltype(any<Number>() % any<OtherNumber>()), Unit> {
return UnitMeasure<decltype(any<Number>() % any<OtherNumber>()), Unit>(
value % ratio.unit1PerUnit2);
}
template <typename OtherNumber>
inline constexpr bool operator==(const UnitMeasure<OtherNumber, Unit>& other) const {
return value == other.value;
}
template <typename OtherNumber>
inline constexpr bool operator!=(const UnitMeasure<OtherNumber, Unit>& other) const {
return value != other.value;
}
template <typename OtherNumber>
inline constexpr bool operator<=(const UnitMeasure<OtherNumber, Unit>& other) const {
return value <= other.value;
}
template <typename OtherNumber>
inline constexpr bool operator>=(const UnitMeasure<OtherNumber, Unit>& other) const {
return value >= other.value;
}
template <typename OtherNumber>
inline constexpr bool operator<(const UnitMeasure<OtherNumber, Unit>& other) const {
return value < other.value;
}
template <typename OtherNumber>
inline constexpr bool operator>(const UnitMeasure<OtherNumber, Unit>& other) const {
return value > other.value;
}
template <typename OtherNumber>
inline UnitMeasure& operator+=(const UnitMeasure<OtherNumber, Unit>& other) {
value += other.value;
return *this;
}
template <typename OtherNumber>
inline UnitMeasure& operator-=(const UnitMeasure<OtherNumber, Unit>& other) {
value -= other.value;
return *this;
}
template <typename OtherNumber>
inline UnitMeasure& operator*=(OtherNumber other) {
value *= other;
return *this;
}
template <typename OtherNumber>
inline UnitMeasure& operator/=(OtherNumber other) {
value /= other.value;
return *this;
}
private:
inline explicit constexpr UnitMeasure(Number value): value(value) {}
Number value;
template <typename OtherNumber, typename OtherUnit>
friend class UnitMeasure;
template <typename Number1, typename Number2, typename Unit2>
friend inline constexpr auto operator*(Number1 a, UnitMeasure<Number2, Unit2> b)
-> UnitMeasure<decltype(any<Number1>() * any<Number2>()), Unit2>;
};
template <typename Number, typename Unit>
constexpr UnitMeasure<Number, Unit> UnitMeasure<Number, Unit>::ONE;
template <typename Number1, typename Number2, typename Unit>
inline constexpr auto operator*(Number1 a, UnitMeasure<Number2, Unit> b)
-> UnitMeasure<decltype(any<Number1>() * any<Number2>()), Unit> {
return UnitMeasure<decltype(any<Number1>() * any<Number2>()), Unit>(a * b.value);
}
template <typename Number1, typename Number2, typename Unit, typename Unit2>
inline constexpr auto operator*(UnitRatio<Number1, Unit2, Unit> ratio,
UnitMeasure<Number2, Unit> measure)
-> decltype(measure * ratio) {
return measure * ratio;
}
} // namespace capnproto
#endif // CAPNPROTO_UNIT_H_
This diff is collapsed.
This diff is collapsed.
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