Commit ebce4aa6 authored by Kenton Varda's avatar Kenton Varda

MSVC: All lite-mode tests pass.

The project file still only compiles a test binary, but it should be easy to separate out a library project from here.

Thanks again to Bryan Boreham <bjboreham@gmail.com> for much help getting this working.
parent ead1a3b3
......@@ -44,7 +44,7 @@
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PreprocessorDefinitions>WIN32;CAPNP_LITE;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\src;..\gtest\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>.;..\src;..\gtest\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
......@@ -60,7 +60,7 @@
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PreprocessorDefinitions>WIN32;CAPNP_LITE;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\src;..\gtest\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>.;..\src;..\gtest\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
......@@ -75,14 +75,35 @@
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="..\src\capnp\any.h" />
<ClInclude Include="..\src\capnp\arena.h" />
<ClInclude Include="..\src\capnp\blob.h" />
<ClInclude Include="..\src\capnp\common.h" />
<ClInclude Include="..\src\capnp\generated-header-support.h" />
<ClInclude Include="..\src\capnp\layout.h" />
<ClInclude Include="..\src\capnp\list.h" />
<ClInclude Include="..\src\capnp\message.h" />
<ClInclude Include="..\src\capnp\orphan.h" />
<ClInclude Include="..\src\capnp\pointer-helpers.h" />
<ClInclude Include="..\src\capnp\schema-lite.h" />
<ClInclude Include="..\src\capnp\serialize-packed.h" />
<ClInclude Include="..\src\capnp\serialize.h" />
<ClInclude Include="..\src\capnp\test-util.h" />
<ClInclude Include="..\src\kj\array.h" />
<ClInclude Include="..\src\kj\common.h" />
<ClInclude Include="..\src\kj\debug.h" />
<ClInclude Include="..\src\kj\exception.h" />
<ClInclude Include="..\src\kj\io.h" />
<ClInclude Include="..\src\kj\memory.h" />
<ClInclude Include="..\src\kj\mutex.h" />
<ClInclude Include="..\src\kj\string.h" />
<ClInclude Include="..\src\kj\miniposix.h" />
<ClInclude Include="..\src\kj\thread.h" />
<ClInclude Include="..\src\kj\threadlocal.h" />
<ClInclude Include="..\src\kj\units.h" />
<ClInclude Include="capnp\test-import.capnp.h" />
<ClInclude Include="capnp\test-import2.capnp.h" />
<ClInclude Include="capnp\test.capnp.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\src\kj\array-test.c++">
......@@ -136,6 +157,105 @@
<Project>{3af54c8a-10bf-4332-9147-f68ed9862032}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\src\capnp\any-test.c++">
<FileType>Document</FileType>
</ClCompile>
<ClCompile Include="..\src\capnp\any.c++">
<FileType>Document</FileType>
</ClCompile>
<ClCompile Include="..\src\capnp\arena.c++">
<FileType>Document</FileType>
</ClCompile>
<ClCompile Include="..\src\capnp\blob-test.c++">
<FileType>Document</FileType>
</ClCompile>
<ClCompile Include="..\src\capnp\blob.c++">
<FileType>Document</FileType>
</ClCompile>
<ClCompile Include="..\src\capnp\c++.capnp.c++">
<FileType>Document</FileType>
</ClCompile>
<ClCompile Include="..\src\capnp\common-test.c++">
<FileType>Document</FileType>
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(IntDir)-capnp</ObjectFileName>
<ProgramDataBaseFileName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(IntDir)-capnpvc$(PlatformToolsetVersion).pdb</ProgramDataBaseFileName>
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(IntDir)-capnp</ObjectFileName>
<ProgramDataBaseFileName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(IntDir)-capnpvc$(PlatformToolsetVersion).pdb</ProgramDataBaseFileName>
</ClCompile>
<ClCompile Include="..\src\capnp\encoding-test.c++">
<FileType>Document</FileType>
</ClCompile>
<ClCompile Include="..\src\capnp\endian-fallback-test.c++">
<FileType>Document</FileType>
</ClCompile>
<ClCompile Include="..\src\capnp\endian-test.c++">
<FileType>Document</FileType>
</ClCompile>
<ClCompile Include="..\src\capnp\layout-test.c++">
<FileType>Document</FileType>
</ClCompile>
<ClCompile Include="..\src\capnp\layout.c++">
<FileType>Document</FileType>
</ClCompile>
<ClCompile Include="..\src\capnp\list.c++">
<FileType>Document</FileType>
</ClCompile>
<ClCompile Include="..\src\capnp\message-test.c++">
<FileType>Document</FileType>
</ClCompile>
<ClCompile Include="..\src\capnp\message.c++">
<FileType>Document</FileType>
</ClCompile>
<ClCompile Include="..\src\capnp\orphan-test.c++">
<FileType>Document</FileType>
</ClCompile>
<ClCompile Include="..\src\capnp\schema.capnp.c++">
<FileType>Document</FileType>
</ClCompile>
<ClCompile Include="..\src\capnp\serialize-packed-test.c++">
<FileType>Document</FileType>
</ClCompile>
<ClCompile Include="..\src\capnp\serialize-packed.c++">
<FileType>Document</FileType>
</ClCompile>
<ClCompile Include="..\src\capnp\serialize-test.c++">
<FileType>Document</FileType>
</ClCompile>
<ClCompile Include="..\src\capnp\serialize.c++">
<FileType>Document</FileType>
</ClCompile>
<ClCompile Include="..\src\capnp\test-util.c++">
<FileType>Document</FileType>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClCompile Include="capnp\test-import.capnp.c++">
<FileType>Document</FileType>
</ClCompile>
<ClCompile Include="capnp\test-import2.capnp.c++">
<FileType>Document</FileType>
</ClCompile>
<ClCompile Include="capnp\test.capnp.c++">
<FileType>Document</FileType>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\src\kj\mutex-test.c++">
<FileType>Document</FileType>
</ClCompile>
<ClCompile Include="..\src\kj\mutex.c++">
<FileType>Document</FileType>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\src\kj\thread.c++">
<FileType>Document</FileType>
</ClCompile>
<ClCompile Include="..\src\kj\threadlocal-test.c++">
<FileType>Document</FileType>
</ClCompile>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
......
@echo off
rem You'll need to build capnp.exe and capnpc-c++.exe using MinGW.
capnp compile -oc++ -I../src --src-prefix=../src ../src/capnp/test.capnp ../src/capnp/test-import.capnp ../src/capnp/test-import2.capnp
......@@ -8,3 +8,12 @@ because Kenton doesn't understand cmake and wanted to get some work done.
The solution file refers to gtest, which must be downloaded to "c++/gtest".
The "setup-autotools.sh" script accomplishes this, although it requires bash
to run.
The solution also refers to generated code for test schemas which should be
under msvc/capnp (i.e. a directory called "capnp" within *this* directory).
To generate these files, do the following:
1. Build capnp.exe and capnpc-c++.exe with MinGW. (Or, download the
precompiled binaries if they are available.)
2. Copy those files to this directory, or somewhere in PATH.
3. Run gen-test-code.bat.
......@@ -124,9 +124,11 @@ TEST(Any, AnyStruct) {
EXPECT_EQ(48, b.getDataSection().size());
EXPECT_EQ(20, b.getPointerSection().size());
#if !_MSC_VER // TODO(msvc): ICE on the necessary constructor; see any.h.
b = root.getAnyPointerField().getAs<TestAllTypes>();
EXPECT_EQ(48, b.getDataSection().size());
EXPECT_EQ(20, b.getPointerSection().size());
#endif
auto r = toAny(root.getAnyPointerField().getAs<TestAllTypes>().asReader());
EXPECT_EQ(48, r.getDataSection().size());
......@@ -136,9 +138,11 @@ TEST(Any, AnyStruct) {
EXPECT_EQ(48, r.getDataSection().size());
EXPECT_EQ(20, r.getPointerSection().size());
#if !_MSC_VER // TODO(msvc): ICE on the necessary constructor; see any.h.
r = root.getAnyPointerField().getAs<TestAllTypes>().asReader();
EXPECT_EQ(48, r.getDataSection().size());
EXPECT_EQ(20, r.getPointerSection().size());
#endif
{
MallocMessageBuilder b2;
......@@ -177,10 +181,12 @@ TEST(Any, AnyList) {
EXPECT_EQ(48, alb.as<List<AnyStruct>>()[0].getDataSection().size());
EXPECT_EQ(20, alb.as<List<AnyStruct>>()[0].getPointerSection().size());
#if !_MSC_VER // TODO(msvc): ICE on the necessary constructor; see any.h.
alb = root.getAnyPointerField().getAs<List<TestAllTypes>>();
EXPECT_EQ(2, alb.size());
EXPECT_EQ(48, alb.as<List<AnyStruct>>()[0].getDataSection().size());
EXPECT_EQ(20, alb.as<List<AnyStruct>>()[0].getPointerSection().size());
#endif
auto alr = toAny(root.getAnyPointerField().getAs<List<TestAllTypes>>().asReader());
EXPECT_EQ(2, alr.size());
......@@ -192,10 +198,12 @@ TEST(Any, AnyList) {
EXPECT_EQ(48, alr.as<List<AnyStruct>>()[0].getDataSection().size());
EXPECT_EQ(20, alr.as<List<AnyStruct>>()[0].getPointerSection().size());
#if !_MSC_VER // TODO(msvc): ICE on the necessary constructor; see any.h.
alr = root.getAnyPointerField().getAs<List<TestAllTypes>>().asReader();
EXPECT_EQ(2, alr.size());
EXPECT_EQ(48, alr.as<List<AnyStruct>>()[0].getDataSection().size());
EXPECT_EQ(20, alr.as<List<AnyStruct>>()[0].getPointerSection().size());
#endif
}
} // namespace
......
......@@ -366,9 +366,9 @@ struct List<AnyPointer, Kind::OTHER> {
inline explicit Reader(_::ListReader reader): reader(reader) {}
inline uint size() const { return reader.size() / ELEMENTS; }
inline typename AnyPointer::Reader operator[](uint index) const {
inline AnyPointer::Reader operator[](uint index) const {
KJ_IREQUIRE(index < size());
return typename AnyPointer::Reader(reader.getPointerElement(index * ELEMENTS));
return AnyPointer::Reader(reader.getPointerElement(index * ELEMENTS));
}
typedef _::IndexingIterator<const Reader, typename AnyPointer::Reader> Iterator;
......@@ -398,9 +398,9 @@ struct List<AnyPointer, Kind::OTHER> {
inline Reader asReader() { return Reader(builder.asReader()); }
inline uint size() const { return builder.size() / ELEMENTS; }
inline typename AnyPointer::Builder operator[](uint index) {
inline AnyPointer::Builder operator[](uint index) {
KJ_IREQUIRE(index < size());
return typename AnyPointer::Builder(builder.getPointerElement(index * ELEMENTS));
return AnyPointer::Builder(builder.getPointerElement(index * ELEMENTS));
}
typedef _::IndexingIterator<Builder, typename AnyPointer::Builder> Iterator;
......@@ -422,9 +422,11 @@ public:
Reader() = default;
inline Reader(_::StructReader reader): _reader(reader) {}
#if !_MSC_VER // TODO(msvc): MSVC ICEs on this. Try restoring when compiler improves.
template <typename T, typename = kj::EnableIf<CAPNP_KIND(FromReader<T>) == Kind::STRUCT>>
inline Reader(T&& value)
: _reader(_::PointerHelpers<FromReader<T>>::getInternalReader(kj::fwd<T>(value))) {}
#endif
Data::Reader getDataSection() {
return _reader.getDataSectionAsBlob();
......@@ -448,9 +450,11 @@ public:
inline Builder(decltype(nullptr)) {}
inline Builder(_::StructBuilder builder): _builder(builder) {}
#if !_MSC_VER // TODO(msvc): MSVC ICEs on this. Try restoring when compiler improves.
template <typename T, typename = kj::EnableIf<CAPNP_KIND(FromBuilder<T>) == Kind::STRUCT>>
inline Builder(T&& value)
: _builder(_::PointerHelpers<FromBuilder<T>>::getInternalBuilder(kj::fwd<T>(value))) {}
#endif
inline Data::Builder getDataSection() {
return _builder.getDataSectionAsBlob();
......@@ -495,9 +499,9 @@ public:
inline explicit Reader(_::ListReader reader): reader(reader) {}
inline uint size() const { return reader.size() / ELEMENTS; }
inline typename AnyStruct::Reader operator[](uint index) const {
inline AnyStruct::Reader operator[](uint index) const {
KJ_IREQUIRE(index < size());
return typename AnyStruct::Reader(reader.getStructElement(index * ELEMENTS));
return AnyStruct::Reader(reader.getStructElement(index * ELEMENTS));
}
typedef _::IndexingIterator<const Reader, typename AnyStruct::Reader> Iterator;
......@@ -528,9 +532,9 @@ public:
inline Reader asReader() { return Reader(builder.asReader()); }
inline uint size() const { return builder.size() / ELEMENTS; }
inline typename AnyStruct::Builder operator[](uint index) {
inline AnyStruct::Builder operator[](uint index) {
KJ_IREQUIRE(index < size());
return typename AnyStruct::Builder(builder.getStructElement(index * ELEMENTS));
return AnyStruct::Builder(builder.getStructElement(index * ELEMENTS));
}
typedef _::IndexingIterator<Builder, typename AnyStruct::Builder> Iterator;
......@@ -551,9 +555,11 @@ public:
Reader() = default;
inline Reader(_::ListReader reader): _reader(reader) {}
#if !_MSC_VER // TODO(msvc): MSVC ICEs on this. Try restoring when compiler improves.
template <typename T, typename = kj::EnableIf<CAPNP_KIND(FromReader<T>) == Kind::LIST>>
inline Reader(T&& value)
: _reader(_::PointerHelpers<FromReader<T>>::getInternalReader(kj::fwd<T>(value))) {}
#endif
inline ElementSize getElementSize() { return _reader.getElementSize(); }
inline uint size() { return _reader.size() / ELEMENTS; }
......@@ -574,9 +580,11 @@ public:
inline Builder(decltype(nullptr)) {}
inline Builder(_::ListBuilder builder): _builder(builder) {}
#if !_MSC_VER // TODO(msvc): MSVC ICEs on this. Try restoring when compiler improves.
template <typename T, typename = kj::EnableIf<CAPNP_KIND(FromBuilder<T>) == Kind::LIST>>
inline Builder(T&& value)
: _builder(_::PointerHelpers<FromBuilder<T>>::getInternalBuilder(kj::fwd<T>(value))) {}
#endif
inline ElementSize getElementSize() { return _builder.getElementSize(); }
inline uint size() { return _builder.size() / ELEMENTS; }
......@@ -818,11 +826,11 @@ struct PointerHelpers<AnyPointer, Kind::OTHER> {
template <>
struct PointerHelpers<AnyStruct, Kind::OTHER> {
static inline typename AnyStruct::Reader get(
static inline AnyStruct::Reader get(
PointerReader reader, const word* defaultValue = nullptr) {
return AnyStruct::Reader(reader.getStruct(defaultValue));
}
static inline typename AnyStruct::Builder get(
static inline AnyStruct::Builder get(
PointerBuilder builder, const word* defaultValue = nullptr) {
// TODO(someday): Allow specifying the size somehow?
return AnyStruct::Builder(builder.getStruct(
......@@ -831,31 +839,31 @@ struct PointerHelpers<AnyStruct, Kind::OTHER> {
static inline void set(PointerBuilder builder, AnyStruct::Reader value) {
builder.setStruct(value._reader);
}
static inline typename AnyStruct::Builder init(
static inline AnyStruct::Builder init(
PointerBuilder builder, uint dataWordCount, uint pointerCount) {
return typename AnyStruct::Builder(builder.initStruct(
return AnyStruct::Builder(builder.initStruct(
StructSize(dataWordCount * WORDS, pointerCount * POINTERS)));
}
};
template <>
struct PointerHelpers<AnyList, Kind::OTHER> {
static inline typename AnyList::Reader get(
static inline AnyList::Reader get(
PointerReader reader, const word* defaultValue = nullptr) {
return AnyList::Reader(reader.getListAnySize(defaultValue));
}
static inline typename AnyList::Builder get(
static inline AnyList::Builder get(
PointerBuilder builder, const word* defaultValue = nullptr) {
return AnyList::Builder(builder.getListAnySize(defaultValue));
}
static inline void set(PointerBuilder builder, AnyList::Reader value) {
builder.setList(value._reader);
}
static inline typename AnyList::Builder init(
static inline AnyList::Builder init(
PointerBuilder builder, ElementSize elementSize, uint elementCount) {
return AnyList::Builder(builder.initList(elementSize, elementCount * ELEMENTS));
}
static inline typename AnyList::Builder init(
static inline AnyList::Builder init(
PointerBuilder builder, uint dataWordCount, uint pointerCount, uint elementCount) {
return AnyList::Builder(builder.initStructList(
elementCount * ELEMENTS, StructSize(dataWordCount * WORDS, pointerCount * POINTERS)));
......
......@@ -103,6 +103,7 @@ public:
inline Builder(ArrayPtr<byte>& value): ArrayPtr<byte>(value) {}
inline Data::Reader asReader() const { return Data::Reader(*this); }
inline operator Reader() const { return asReader(); }
};
class Text::Builder: public kj::DisallowConstCopy {
......@@ -117,6 +118,7 @@ public:
}
inline Reader asReader() const { return Reader(content.begin(), content.size() - 1); }
inline operator Reader() const { return asReader(); }
inline operator kj::ArrayPtr<char>();
inline kj::ArrayPtr<char> asArray();
......
......@@ -61,7 +61,7 @@ struct Void {
inline constexpr bool operator!=(Void other) const { return false; }
};
static constexpr Void VOID = Void();
static KJ_CONSTEXPR(const) Void VOID = Void();
// Constant value for `Void`, which is an empty struct.
template <typename T>
......@@ -113,7 +113,13 @@ template <typename T, typename = typename T::_capnpPrivate::IsInterface> uint16_
template <typename T, typename = typename schemas::EnumInfo<T>::IsEnum> uint32_t kindSfinae(int);
template <typename T> uint64_t kindSfinae(...);
template <typename T, size_t s = sizeof(kindSfinae<T>(0))> struct Kind_;
template <typename T>
struct MsvcWorkaround {
// TODO(msvc): Remove this once MSVC supports expression SFINAE.
enum { value = sizeof(kindSfinae<T>(0)) };
};
template <typename T, size_t s = MsvcWorkaround<T>::value> struct Kind_;
template <> struct Kind_<Void> { static constexpr Kind kind = Kind::PRIMITIVE; };
template <> struct Kind_<bool> { static constexpr Kind kind = Kind::PRIMITIVE; };
......@@ -158,6 +164,17 @@ inline constexpr Kind kind() {
template <typename T, Kind k = CAPNP_KIND(T)>
struct List;
#if _MSC_VER
template <typename T, Kind k>
struct List {};
// For some reason, without this declaration, MSVC will error out on some uses of List
// claiming that "T" -- as used in the default initializer for the second template param, "k" --
// is not defined. I do not understand this error, but adding this empty default declaration fixes
// it.
#endif
template <typename T> struct ListElementType_;
template <typename T> struct ListElementType_<List<T>> { typedef T Type; };
template <typename T> using ListElementType = typename ListElementType_<T>::Type;
......@@ -213,8 +230,21 @@ using FromServer = typename kj::Decay<T>::Serves;
// FromBuilder<MyType::Server> = MyType (for any Cap'n Proto interface type).
namespace _ { // private
template <typename T, Kind k = CAPNP_KIND(T)>
struct PointerHelpers;
#if _MSC_VER
template <typename T, Kind k>
struct PointerHelpers {};
// For some reason, without this declaration, MSVC will error out on some uses of PointerHelpers
// claiming that "T" -- as used in the default initializer for the second template param, "k" --
// is not defined. I do not understand this error, but adding this empty default declaration fixes
// it.
#endif
} // namespace _ (private)
struct MessageSize {
......@@ -370,12 +400,12 @@ constexpr auto WORDS_PER_POINTER KJ_UNUSED = 1 * WORDS / POINTERS;
constexpr WordCount POINTER_SIZE_IN_WORDS = 1 * POINTERS * WORDS_PER_POINTER;
template <typename T>
inline constexpr decltype(BYTES / ELEMENTS) bytesPerElement() {
inline KJ_CONSTEXPR() decltype(BYTES / ELEMENTS) bytesPerElement() {
return sizeof(T) * BYTES / ELEMENTS;
}
template <typename T>
inline constexpr decltype(BITS / ELEMENTS) bitsPerElement() {
inline KJ_CONSTEXPR() decltype(BITS / ELEMENTS) bitsPerElement() {
return sizeof(T) * 8 * BITS / ELEMENTS;
}
......
......@@ -31,30 +31,6 @@ namespace capnp {
namespace _ { // private
namespace {
template <typename T, typename U>
void checkList(T reader, std::initializer_list<U> expected) {
ASSERT_EQ(expected.size(), reader.size());
for (uint i = 0; i < expected.size(); i++) {
EXPECT_EQ(expected.begin()[i], reader[i]);
}
}
template <typename T>
void checkList(T reader, std::initializer_list<float> expected) {
ASSERT_EQ(expected.size(), reader.size());
for (uint i = 0; i < expected.size(); i++) {
EXPECT_FLOAT_EQ(expected.begin()[i], reader[i]);
}
}
template <typename T>
void checkList(T reader, std::initializer_list<double> expected) {
ASSERT_EQ(expected.size(), reader.size());
for (uint i = 0; i < expected.size(); i++) {
EXPECT_DOUBLE_EQ(expected.begin()[i], reader[i]);
}
}
TEST(Encoding, AllTypes) {
MallocMessageBuilder builder;
......@@ -541,7 +517,7 @@ TEST(Encoding, SmallStructLists) {
// Should match...
EXPECT_EQ(defaultSegment.size(), segment.size());
for (size_t i = 0; i < std::min(segment.size(), defaultSegment.size()); i++) {
for (size_t i = 0; i < kj::min(segment.size(), defaultSegment.size()); i++) {
EXPECT_EQ(reinterpret_cast<const uint64_t*>(defaultSegment.begin())[i],
reinterpret_cast<const uint64_t*>(segment.begin())[i]);
}
......@@ -973,16 +949,6 @@ TEST(Encoding, UpgradeStructInBuilderDoubleFarPointers) {
EXPECT_EQ(2u, builder.getSegmentsForOutput()[2].size());
}
void checkList(List<test::TestOldVersion>::Reader reader,
std::initializer_list<int64_t> expectedData,
std::initializer_list<Text::Reader> expectedPointers) {
ASSERT_EQ(expectedData.size(), reader.size());
for (uint i = 0; i < expectedData.size(); i++) {
EXPECT_EQ(expectedData.begin()[i], reader[i].getOld1());
EXPECT_EQ(expectedPointers.begin()[i], reader[i].getOld2());
}
}
void checkUpgradedList(test::TestAnyPointer::Builder root,
std::initializer_list<int64_t> expectedData,
std::initializer_list<Text::Reader> expectedPointers) {
......
......@@ -43,6 +43,24 @@ namespace _ { // private
// Cap'n Proto is special because it is essentially doing compiler-like things, fussing over
// allocation and layout of memory, in order to squeeze out every last drop of performance.
#if _MSC_VER
// Assume Windows is little-endian.
//
// TODO(msvc): This is ugly. Maybe refactor later checks to be based on CAPNP_BYTE_ORDER or
// CAPNP_SWAP_BYTES or something, and define that in turn based on _MSC_VER or the GCC
// intrinsics.
#ifndef __ORDER_BIG_ENDIAN__
#define __ORDER_BIG_ENDIAN__ 4321
#endif
#ifndef __ORDER_LITTLE_ENDIAN__
#define __ORDER_LITTLE_ENDIAN__ 1234
#endif
#ifndef __BYTE_ORDER__
#define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__
#endif
#endif
#if CAPNP_REVERSE_ENDIAN
#define CAPNP_WIRE_BYTE_ORDER __ORDER_BIG_ENDIAN__
#define CAPNP_OPPOSITE_OF_WIRE_BYTE_ORDER __ORDER_LITTLE_ENDIAN__
......
......@@ -25,12 +25,14 @@
#include "arena.h"
#include <gtest/gtest.h>
namespace capnp {
#if CAPNP_DEBUG_TYPES
namespace kj {
template <typename T, typename U>
std::ostream& operator<<(std::ostream& os, kj::Quantity<T, U> value) {
return os << (value / kj::unit<kj::Quantity<T, U>>());
}
}
#endif
namespace capnp {
namespace _ { // private
......
......@@ -960,9 +960,8 @@ struct WireHelpers {
// run with it and do bounds checks at access time, because how would we handle writes?
// Instead, we have to copy the struct to a new space now.
WordCount newDataSize = std::max<WordCount>(oldDataSize, size.data);
WirePointerCount newPointerCount =
std::max<WirePointerCount>(oldPointerCount, size.pointers);
WordCount newDataSize = kj::max(oldDataSize, size.data);
WirePointerCount newPointerCount = kj::max(oldPointerCount, size.pointers);
WordCount totalSize = newDataSize + newPointerCount * WORDS_PER_POINTER;
// Don't let allocate() zero out the object just yet.
......@@ -1282,9 +1281,8 @@ struct WireHelpers {
// The structs in this list are smaller than expected, probably written using an older
// version of the protocol. We need to make a copy and expand them.
WordCount newDataSize = std::max<WordCount>(oldDataSize, elementSize.data);
WirePointerCount newPointerCount =
std::max<WirePointerCount>(oldPointerCount, elementSize.pointers);
WordCount newDataSize = kj::max(oldDataSize, elementSize.data);
WirePointerCount newPointerCount = kj::max(oldPointerCount, elementSize.pointers);
auto newStep = (newDataSize + newPointerCount * WORDS_PER_POINTER) / ELEMENTS;
WordCount totalSize = newStep * elementCount;
......@@ -1309,8 +1307,8 @@ struct WireHelpers {
// Copy pointer section.
WirePointer* newPointerSection = reinterpret_cast<WirePointer*>(dst + newDataSize);
WirePointer* oldPointerSection = reinterpret_cast<WirePointer*>(src + oldDataSize);
for (uint i = 0; i < oldPointerCount / POINTERS; i++) {
transferPointer(origSegment, newPointerSection + i, oldSegment, oldPointerSection + i);
for (uint j = 0; j < oldPointerCount / POINTERS; j++) {
transferPointer(origSegment, newPointerSection + j, oldSegment, oldPointerSection + j);
}
dst += newStep * (1 * ELEMENTS);
......@@ -1346,10 +1344,10 @@ struct WireHelpers {
WirePointerCount newPointerCount = elementSize.pointers;
if (oldSize == ElementSize::POINTER) {
newPointerCount = std::max(newPointerCount, 1 * POINTERS);
newPointerCount = kj::max(newPointerCount, 1 * POINTERS);
} else {
// Old list contains data elements, so we need at least 1 word of data.
newDataSize = std::max(newDataSize, 1 * WORDS);
newDataSize = kj::max(newDataSize, 1 * WORDS);
}
auto newStep = (newDataSize + newPointerCount * WORDS_PER_POINTER) / ELEMENTS;
......@@ -1894,7 +1892,12 @@ struct WireHelpers {
ElementSize elementSize = ref->listRef.elementSize();
if (elementSize == ElementSize::INLINE_COMPOSITE) {
#if _MSC_VER
// TODO(msvc): MSVC thinks decltype(WORDS/ELEMENTS) is a const type. /eyeroll
uint wordsPerElement;
#else
decltype(WORDS/ELEMENTS) wordsPerElement;
#endif
ElementCount size;
WordCount wordCount = ref->listRef.inlineCompositeWordCount();
......@@ -2312,7 +2315,7 @@ bool PointerReader::isNull() const {
}
bool PointerReader::isStruct() const {
word* refTarget;
word* refTarget = nullptr;
const WirePointer* ptr = pointer;
SegmentReader* sgmt = segment;
WireHelpers::followFars(ptr, refTarget, sgmt);
......@@ -2320,7 +2323,7 @@ bool PointerReader::isStruct() const {
}
bool PointerReader::isList() const {
word* refTarget;
word* refTarget = nullptr;
const WirePointer* ptr = pointer;
SegmentReader* sgmt = segment;
WireHelpers::followFars(ptr, refTarget, sgmt);
......
......@@ -96,7 +96,7 @@ static constexpr BitsPerElement BITS_PER_ELEMENT_TABLE[8] = {
0 * BITS / ELEMENTS
};
inline constexpr BitsPerElement dataBitsPerElement(ElementSize size) {
inline KJ_CONSTEXPR() BitsPerElement dataBitsPerElement(ElementSize size) {
return _::BITS_PER_ELEMENT_TABLE[static_cast<int>(size)];
}
......@@ -549,12 +549,26 @@ private:
// -------------------------------------------------------------------
#if _MSC_VER
// TODO(msvc): MSVC insists List{Reader,Builder}::operator= are deleted unless we
// define them explicitly. Don't know why, especially for the readers.
#define MSVC_DEFAULT_ASSIGNMENT_WORKAROUND(const_, type) \
inline type& operator=(const_ type& other) { \
memcpy(this, &other, sizeof(*this)); \
return *this; \
}
#else
#define MSVC_DEFAULT_ASSIGNMENT_WORKAROUND(const_, type)
#endif
class ListBuilder: public kj::DisallowConstCopy {
public:
inline ListBuilder()
: segment(nullptr), ptr(nullptr), elementCount(0 * ELEMENTS),
step(0 * BITS / ELEMENTS) {}
MSVC_DEFAULT_ASSIGNMENT_WORKAROUND(, ListBuilder);
inline word* getLocation() {
// Get the object's location.
......@@ -631,6 +645,8 @@ public:
: segment(nullptr), ptr(nullptr), elementCount(0), step(0 * BITS / ELEMENTS),
structDataSize(0), structPointerCount(0), nestingLimit(0x7fffffff) {}
MSVC_DEFAULT_ASSIGNMENT_WORKAROUND(const, ListReader);
inline ElementCount size() const;
// The number of elements in the list.
......
......@@ -28,7 +28,6 @@
#include <exception>
#include <string>
#include <vector>
#include <unistd.h>
#include <errno.h>
namespace capnp {
......@@ -193,7 +192,7 @@ kj::ArrayPtr<word> MallocMessageBuilder::allocateSegment(uint minimumSize) {
ownFirstSegment = true;
}
uint size = std::max(minimumSize, nextSize);
uint size = kj::max(minimumSize, nextSize);
void* result = calloc(size, sizeof(word));
if (result == nullptr) {
......
......@@ -59,7 +59,7 @@ public:
size_t tryRead(void* buffer, size_t minBytes, size_t maxBytes) override {
KJ_ASSERT(maxBytes <= data.size() - readPos, "Overran end of stream.");
size_t amount = std::min(maxBytes, std::max(minBytes, preferredReadSize));
size_t amount = kj::min(maxBytes, kj::max(minBytes, preferredReadSize));
memcpy(buffer, data.data() + readPos, amount);
readPos += amount;
return amount;
......@@ -71,7 +71,7 @@ public:
}
kj::ArrayPtr<const byte> tryGetReadBuffer() override {
size_t amount = std::min(data.size() - readPos, preferredReadSize);
size_t amount = kj::min(data.size() - readPos, preferredReadSize);
return kj::arrayPtr(reinterpret_cast<const byte*>(data.data() + readPos), amount);
}
......@@ -86,6 +86,8 @@ struct DisplayByteArray {
: data(reinterpret_cast<const uint8_t*>(str.data())), size(str.size()) {}
DisplayByteArray(const std::initializer_list<uint8_t>& list)
: data(list.begin()), size(list.size()) {}
DisplayByteArray(kj::ArrayPtr<const byte> data)
: data(data.begin()), size(data.size()) {}
const uint8_t* data;
size_t size;
......@@ -128,16 +130,15 @@ void expectPacksTo(std::initializer_list<uint8_t> unpacked,
// -----------------------------------------------------------------
// read
std::string roundTrip;
roundTrip.resize(unpacked.size());
kj::Array<byte> roundTrip = kj::heapArray<byte>(unpacked.size());
{
PackedInputStream packedIn(pipe);
packedIn.InputStream::read(&*roundTrip.begin(), roundTrip.size());
packedIn.InputStream::read(roundTrip.begin(), roundTrip.size());
EXPECT_TRUE(pipe.allRead());
}
if (roundTrip != std::string(reinterpret_cast<const char*>(unpacked.begin()), unpacked.size())) {
if (memcmp(roundTrip.begin(), unpacked.begin(), unpacked.size()) != 0) {
ADD_FAILURE()
<< "Tried to unpack: " << DisplayByteArray(packed) << "\n"
<< "Expected: " << DisplayByteArray(unpacked) << "\n"
......@@ -150,12 +151,11 @@ void expectPacksTo(std::initializer_list<uint8_t> unpacked,
{
PackedInputStream packedIn(pipe);
packedIn.InputStream::read(&*roundTrip.begin(), roundTrip.size());
packedIn.InputStream::read(roundTrip.begin(), roundTrip.size());
EXPECT_TRUE(pipe.allRead());
}
if (roundTrip !=
std::string(reinterpret_cast<const char*>(unpacked.begin()), unpacked.size())) {
if (memcmp(roundTrip.begin(), unpacked.begin(), unpacked.size()) != 0) {
ADD_FAILURE()
<< "Tried to unpack: " << DisplayByteArray(packed) << "\n"
<< " Block size: " << blockSize << "\n"
......@@ -202,8 +202,7 @@ void expectPacksTo(std::initializer_list<uint8_t> unpacked,
PackedInputStream packedIn(pipe);
packedIn.InputStream::read(&*roundTrip.begin(), roundTrip.size());
if (roundTrip !=
std::string(reinterpret_cast<const char*>(unpacked.begin()), unpacked.size())) {
if (memcmp(roundTrip.begin(), unpacked.begin(), unpacked.size()) != 0) {
ADD_FAILURE()
<< "Tried to unpack: " << DisplayByteArray(packed) << "\n"
<< " Index: " << i << "\n"
......
......@@ -304,7 +304,7 @@ int mkstemp(char *tpl) {
int error = errno;
if (error != EEXIST && error != EINTR) {
KJ_FAIL_SYSCALL("open(mktemp())", error);
KJ_FAIL_SYSCALL("open(mktemp())", error, tpl);
}
memset(end, 'X', strlen(end));
......@@ -313,7 +313,12 @@ int mkstemp(char *tpl) {
#endif
TEST(Serialize, FileDescriptors) {
#if _WIN32
// TODO(cleanup): Find the Windows temp directory? Seems overly difficult.
char filename[] = "capnproto-serialize-test-XXXXXX";
#else
char filename[] = "/tmp/capnproto-serialize-test-XXXXXX";
#endif
kj::AutoCloseFd tmpfile(mkstemp(filename));
ASSERT_GE(tmpfile.get(), 0);
......
......@@ -49,15 +49,17 @@ FlatArrayMessageReader::FlatArrayMessageReader(
return;
}
uint segmentSize = table[1].get();
{
uint segmentSize = table[1].get();
KJ_REQUIRE(array.size() >= offset + segmentSize,
"Message ends prematurely in first segment.") {
return;
}
KJ_REQUIRE(array.size() >= offset + segmentSize,
"Message ends prematurely in first segment.") {
return;
}
segment0 = array.slice(offset, offset + segmentSize);
offset += segmentSize;
segment0 = array.slice(offset, offset + segmentSize);
offset += segmentSize;
}
if (segmentCount > 1) {
moreSegments = kj::heapArray<kj::ArrayPtr<const word>>(segmentCount - 1);
......@@ -154,9 +156,9 @@ InputStreamMessageReader::InputStreamMessageReader(
}
// Read sizes for all segments except the first. Include padding if necessary.
_::WireValue<uint32_t> moreSizes[segmentCount & ~1];
KJ_STACK_ARRAY(_::WireValue<uint32_t>, moreSizes, segmentCount & ~1, 16, 64);
if (segmentCount > 1) {
inputStream.read(moreSizes, sizeof(moreSizes));
inputStream.read(moreSizes.begin(), moreSizes.size() * sizeof(moreSizes[0]));
for (uint i = 0; i < segmentCount - 1; i++) {
totalWords += moreSizes[i].get();
}
......@@ -239,7 +241,7 @@ kj::ArrayPtr<const word> InputStreamMessageReader::getSegment(uint id) {
void writeMessage(kj::OutputStream& output, kj::ArrayPtr<const kj::ArrayPtr<const word>> segments) {
KJ_REQUIRE(segments.size() > 0, "Tried to serialize uninitialized message.");
_::WireValue<uint32_t> table[(segments.size() + 2) & ~size_t(1)];
KJ_STACK_ARRAY(_::WireValue<uint32_t>, table, (segments.size() + 2) & ~size_t(1), 16, 64);
// We write the segment count - 1 because this makes the first word zero for single-segment
// messages, improving compression. We don't bother doing this with segment sizes because
......@@ -254,7 +256,7 @@ void writeMessage(kj::OutputStream& output, kj::ArrayPtr<const kj::ArrayPtr<cons
}
KJ_STACK_ARRAY(kj::ArrayPtr<const byte>, pieces, segments.size() + 1, 4, 32);
pieces[0] = kj::arrayPtr(reinterpret_cast<byte*>(table), sizeof(table));
pieces[0] = kj::arrayPtr(reinterpret_cast<byte*>(table.begin()), table.size() * sizeof(table[0]));
for (uint i = 0; i < segments.size(); i++) {
pieces[i + 1] = kj::arrayPtr(reinterpret_cast<const byte*>(segments[i].begin()),
......
......@@ -56,7 +56,7 @@ void genericInitTestMessage(Builder builder) {
subBuilder.setUInt16Field(1234u);
subBuilder.setUInt32Field(56789012u);
subBuilder.setUInt64Field(345678901234567890ull);
subBuilder.setFloat32Field(-1.25e-10);
subBuilder.setFloat32Field(-1.25e-10f);
subBuilder.setFloat64Field(345);
subBuilder.setTextField("baz");
subBuilder.setDataField(data("qux"));
......@@ -78,7 +78,7 @@ void genericInitTestMessage(Builder builder) {
subBuilder.setUInt16List({1234u, 5678u, 0u, 0xffffu});
subBuilder.setUInt32List({12345678u, 90123456u, 0u, 0xffffffffu});
subBuilder.setUInt64List({123456789012345ull, 678901234567890ull, 0ull, 0xffffffffffffffffull});
subBuilder.setFloat32List({0, 1234567, 1e37, -1e37, 1e-37, -1e-37});
subBuilder.setFloat32List({0, 1234567, 1e37f, -1e37f, 1e-37f, -1e-37f});
subBuilder.setFloat64List({0, 123456789012345, 1e306, -1e306, 1e-306, -1e-306});
subBuilder.setTextList({"quux", "corge", "grault"});
subBuilder.setDataList({data("garply"), data("waldo"), data("fred")});
......
......@@ -112,27 +112,44 @@ void checkDynamicTestMessageAllZero(DynamicStruct::Builder builder);
void checkDynamicTestMessageAllZero(DynamicStruct::Reader reader);
#endif // !CAPNP_LITE
template <typename T, typename U>
void checkList(T reader, std::initializer_list<U> expected) {
template <typename T>
inline void checkElement(T a, T b) {
EXPECT_EQ(a, b);
}
template <>
inline void checkElement<float>(float a, float b) {
EXPECT_FLOAT_EQ(a, b);
}
template <>
inline void checkElement<double>(double a, double b) {
EXPECT_DOUBLE_EQ(a, b);
}
template <typename T, typename L = T::Reads>
void checkList(T reader, std::initializer_list<decltype(reader[0])> expected) {
ASSERT_EQ(expected.size(), reader.size());
for (uint i = 0; i < expected.size(); i++) {
EXPECT_EQ(expected.begin()[i], reader[i]);
checkElement<decltype(reader[0])>(expected.begin()[i], reader[i]);
}
}
template <typename T>
void checkList(T reader, std::initializer_list<float> expected) {
template <typename T, typename L = T::Builds, bool = false>
void checkList(T reader, std::initializer_list<decltype(L::Reader()[0])> expected) {
ASSERT_EQ(expected.size(), reader.size());
for (uint i = 0; i < expected.size(); i++) {
EXPECT_FLOAT_EQ(expected.begin()[i], reader[i]);
checkElement<decltype(L::Reader()[0])>(expected.begin()[i], reader[i]);
}
}
template <typename T>
void checkList(T reader, std::initializer_list<double> expected) {
ASSERT_EQ(expected.size(), reader.size());
for (uint i = 0; i < expected.size(); i++) {
EXPECT_DOUBLE_EQ(expected.begin()[i], reader[i]);
inline void checkList(List<test::TestOldVersion>::Reader reader,
std::initializer_list<int64_t> expectedData,
std::initializer_list<Text::Reader> expectedPointers) {
ASSERT_EQ(expectedData.size(), reader.size());
for (uint i = 0; i < expectedData.size(); i++) {
EXPECT_EQ(expectedData.begin()[i], reader[i].getOld1());
EXPECT_EQ(expectedPointers.begin()[i], reader[i].getOld2());
}
}
......
......@@ -22,6 +22,9 @@
#include "common.h"
#include "debug.h"
#include <stdlib.h>
#ifdef _MSC_VER
#include <limits>
#endif
namespace kj {
namespace _ { // private
......@@ -56,4 +59,11 @@ void unreachable() {
}
} // namespace _ (private)
#if _MSC_VER
float nan() { return std::numeric_limits<float>::quiet_NaN(); }
#endif
} // namespace kj
......@@ -461,9 +461,17 @@ template<typename T> constexpr T cp(T& t) noexcept { return t; }
template<typename T> constexpr T cp(const T& t) noexcept { return t; }
// Useful to force a copy, particularly to pass into a function that expects T&&.
template <typename T, typename U, bool takeT> struct MinType_;
template <typename T, typename U> struct MinType_<T, U, true> { typedef T Type; };
template <typename T, typename U> struct MinType_<T, U, false> { typedef U Type; };
template <typename T, typename U>
using MinType = typename MinType_<T, U, sizeof(T) <= sizeof(U)>::Type;
// Resolves to the smaller of the two input types.
template <typename T, typename U>
inline KJ_CONSTEXPR() auto min(T&& a, U&& b) -> Decay<decltype(a < b ? a : b)> {
return a < b ? a : b;
inline KJ_CONSTEXPR() auto min(T&& a, U&& b) -> MinType<Decay<T>, Decay<U>> {
return MinType<Decay<T>, Decay<U>>(a < b ? a : b);
}
template <typename T, typename U>
......@@ -555,13 +563,23 @@ static KJ_CONSTEXPR(const) MinValue_ minValue = MinValue_();
#if __GNUC__
inline constexpr float inf() { return __builtin_huge_valf(); }
inline constexpr float nan() { return __builtin_nanf(""); }
#elif _MSC_VER
// Do what MSVC math.h does
#pragma warning(push)
#pragma warning(disable: 4756) // "overflow in constant arithmetic"
inline constexpr float inf() { return (float)(1e300 * 1e300); }
#pragma warning(pop)
inline constexpr float nan() { return inf() * 0.0F; }
// Do what MSVC math.h does
#pragma warning(push)
#pragma warning(disable: 4756) // "overflow in constant arithmetic"
inline constexpr float inf() { return (float)(1e300 * 1e300); }
#pragma warning(pop)
float nan();
// Unfortunatley, inf() * 0.0f produces a NaN with the sign bit set, whereas our preferred
// canonical NaN should not have the sign bit set. std::numeric_limits<float>::quiet_NaN()
// returns the correct NaN, but we don't want to #include that here. So, we give up and make
// this out-of-line on MSVC.
//
// TODO(msvc): Can we do better?
#else
#error "Not sure how to support your compiler."
#endif
......
......@@ -22,19 +22,23 @@
#include "mutex.h"
#include "debug.h"
#include "thread.h"
#include <unistd.h>
#include <gtest/gtest.h>
#if _WIN32
#include <windows.h>
#else
#include <pthread.h>
#include <unistd.h>
#endif
namespace kj {
namespace {
#if _WIN32
inline void delay() { Sleep(10); }
#else
inline void delay() { usleep(10000); }
#endif
#if KJ_NO_EXCEPTIONS
#undef EXPECT_ANY_THROW
......@@ -128,18 +132,18 @@ TEST(Mutex, MutexGuarded) {
TEST(Mutex, Lazy) {
Lazy<uint> lazy;
bool initStarted = false;
volatile bool initStarted = false;
Thread thread([&]() {
EXPECT_EQ(123u, lazy.get([&](SpaceFor<uint>& space) -> Own<uint> {
__atomic_store_n(&initStarted, true, __ATOMIC_RELAXED);
initStarted = true;
delay();
return space.construct(123);
}));
});
// Spin until the initializer has been entered in the thread.
while (!__atomic_load_n(&initStarted, __ATOMIC_RELAXED)) {
while (!initStarted) {
#if _WIN32
Sleep(0);
#else
......
......@@ -243,7 +243,7 @@ void Mutex::assertLockedByCaller(Exclusivity exclusivity) {
// held for debug purposes anyway, we just don't bother.
}
static BOOL nullInitializer(PINIT_ONCE initOnce, PVOID parameter, PVOID* context) {
static BOOL WINAPI nullInitializer(PINIT_ONCE initOnce, PVOID parameter, PVOID* context) {
return true;
}
......
......@@ -61,7 +61,7 @@ private:
bool detached = false;
#if _WIN32
static unsigned long runThread(void* ptr);
static unsigned long __stdcall runThread(void* ptr);
#else
static void* runThread(void* ptr);
#endif
......
......@@ -67,6 +67,10 @@ struct Id {
// =======================================================================================
// Quantity and UnitRatio -- implement unit analysis via the type system
#if !_MSC_VER
// TODO(msvc): MSVC has trouble with this intense templating. Luckily Cap'n Proto can deal with
// using regular integers in place of Quantity, so we can just skip all this.
template <typename T> constexpr bool isIntegral() { return false; }
template <> constexpr bool isIntegral<char>() { return true; }
template <> constexpr bool isIntegral<signed char>() { return true; }
......@@ -350,11 +354,15 @@ private:
friend inline constexpr T unit();
};
#endif // !_MSC_VER
template <typename T>
inline constexpr T unit() { return T(1); }
// unit<Quantity<T, U>>() returns a Quantity of value 1. It also, intentionally, works on basic
// numeric types.
#if !_MSC_VER
template <typename Number1, typename Number2, typename Unit>
inline constexpr auto operator*(Number1 a, Quantity<Number2, Unit> b)
-> Quantity<decltype(Number1(1) * Number2(1)), Unit> {
......@@ -425,6 +433,8 @@ inline constexpr T origin() { return T(0 * unit<UnitOf<T>>()); }
// origin<Absolute<T, U>>() returns an Absolute of value 0. It also, intentionally, works on basic
// numeric types.
#endif // !_MSC_VER
} // namespace kj
#endif // KJ_UNITS_H_
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