mirror of
https://github.com/microsoft/HybridRow.git
synced 2026-01-22 10:53:07 +00:00
Release 1.1.0-preview 3 (#6)
Release roll-up snapshot of C#/C++ codebase at version 1.1.0-preview. This release matches the current shipping nugets.
This commit is contained in:
75
src/Core/Core.Native.Tests.Unit/Base64UnitTests.cpp
Normal file
75
src/Core/Core.Native.Tests.Unit/Base64UnitTests.cpp
Normal file
@@ -0,0 +1,75 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#include "pch.h"
|
||||
#include <array>
|
||||
#include <random>
|
||||
#include "CppUnitTest.h"
|
||||
|
||||
namespace cdb_core_test
|
||||
{
|
||||
using namespace std::literals;
|
||||
using Assert = Microsoft::VisualStudio::CppUnitTestFramework::Assert;
|
||||
using Logger = Microsoft::VisualStudio::CppUnitTestFramework::Logger;
|
||||
|
||||
TEST_CLASS(Base64UnitTests)
|
||||
{
|
||||
public:
|
||||
|
||||
template<typename TChar>
|
||||
void Roundtrip()
|
||||
{
|
||||
std::array<byte, 200> bytes{};
|
||||
std::array<byte, 200> bytes2{};
|
||||
std::array<TChar, cdb_core::Base64::GetEncodeRequiredLength(static_cast<uint32_t>(bytes.size()))> chars{};
|
||||
|
||||
std::random_device seedGenerator{};
|
||||
std::mt19937 rand{seedGenerator()};
|
||||
const std::uniform_int_distribution<int> distribution{};
|
||||
|
||||
cdb_core::Span<int> bb = cdb_core::MemoryMarshal::Cast<byte, int>(cdb_core::Span{bytes});
|
||||
for (auto& b : bb)
|
||||
{
|
||||
b = distribution(rand);
|
||||
}
|
||||
std::fill(chars.begin(), chars.end(), '\0');
|
||||
|
||||
cdb_core::Span<TChar> encoded = cdb_core::Base64::Encode(bytes, cdb_core::Span{chars});
|
||||
Logger::WriteMessage(&encoded[0]);
|
||||
cdb_core::Span<byte> decoded = cdb_core::Base64::Decode(cdb_core::ReadOnlySpan{encoded}, cdb_core::Span{bytes2});
|
||||
|
||||
Assert::AreEqual(static_cast<uint32_t>(bytes.size()), decoded.Length());
|
||||
for (uint32_t i = 0; i < decoded.Length(); i++)
|
||||
{
|
||||
Assert::AreEqual(bytes[i], decoded[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether a byte string properly round-trips as Base64 text.
|
||||
/// </summary>
|
||||
TEST_METHOD(RoundtripA)
|
||||
{
|
||||
Roundtrip<char>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether a byte string properly round-trips as Base64 wide text.
|
||||
/// </summary>
|
||||
TEST_METHOD(RoundtripW)
|
||||
{
|
||||
Roundtrip<wchar_t>();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Additional string function for test diagnostics.
|
||||
namespace Microsoft::VisualStudio::CppUnitTestFramework
|
||||
{
|
||||
template<>
|
||||
inline std::wstring ToString<std::byte>(const std::byte& q)
|
||||
{
|
||||
return cdb_core::make_string(L"{%u}", q);
|
||||
}
|
||||
}
|
||||
45
src/Core/Core.Native.Tests.Unit/ContractUnitTests.cpp
Normal file
45
src/Core/Core.Native.Tests.Unit/ContractUnitTests.cpp
Normal file
@@ -0,0 +1,45 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#include "pch.h"
|
||||
#include "CppUnitTest.h"
|
||||
|
||||
namespace cdb_core_test
|
||||
{
|
||||
using namespace std::literals;
|
||||
using Assert = Microsoft::VisualStudio::CppUnitTestFramework::Assert;
|
||||
|
||||
TEST_CLASS(ContractUnitTests)
|
||||
{
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the string formatting in Contract works correctly for both
|
||||
/// empty and non-empty strings and views.
|
||||
/// </summary>
|
||||
TEST_METHOD(ContractFormatting)
|
||||
{
|
||||
std::wstring error = cdb_core::Contract::MakeError("Assert", "");
|
||||
Assert::AreEqual(0ull, error.find(L"Assert"s));
|
||||
error = cdb_core::Contract::MakeError("Assert", std::string_view());
|
||||
Assert::AreEqual(0ull, error.find(L"Assert"s));
|
||||
error = cdb_core::Contract::MakeError("Assert", "Some error message.");
|
||||
Assert::AreEqual(0ull, error.find(L"Assert"s));
|
||||
Assert::AreNotEqual(std::string::npos, error.find(L"Some error message."s));
|
||||
|
||||
// ReSharper disable once StringLiteralTypo
|
||||
error = cdb_core::Contract::MakeError("Assert", "Some error message \0 with a null ASDFGHJKL.");
|
||||
Assert::AreEqual(0ull, error.find(L"Assert"s));
|
||||
Assert::AreNotEqual(std::string::npos, error.find(L"Some error message "s));
|
||||
Assert::AreEqual(std::string::npos, error.find(L"ASDFGHJKL"s));
|
||||
|
||||
// ReSharper disable once StringLiteralTypo
|
||||
const std::string_view truncated = "some very long message ASDFGHJKL";
|
||||
error = cdb_core::Contract::MakeError("Assert", truncated.substr(0, 4));
|
||||
Assert::AreEqual(0ull, error.find(L"Assert"s));
|
||||
Assert::AreNotEqual(std::string::npos, error.find(L"some"s));
|
||||
Assert::AreEqual(std::string::npos, error.find(L"ASDFGHJKL"s));
|
||||
}
|
||||
};
|
||||
}
|
||||
162
src/Core/Core.Native.Tests.Unit/Crc32UnitTests.cpp
Normal file
162
src/Core/Core.Native.Tests.Unit/Crc32UnitTests.cpp
Normal file
@@ -0,0 +1,162 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#include "pch.h"
|
||||
#include <array>
|
||||
#include "CppUnitTest.h"
|
||||
|
||||
namespace cdb_core_test
|
||||
{
|
||||
using namespace std::literals;
|
||||
using Assert = Microsoft::VisualStudio::CppUnitTestFramework::Assert;
|
||||
using Logger = Microsoft::VisualStudio::CppUnitTestFramework::Logger;
|
||||
|
||||
TEST_CLASS(Crc32UnitTests)
|
||||
{
|
||||
constexpr static std::array<byte, 7> Sample1
|
||||
{
|
||||
{byte{0x45}, byte{0xB1}, byte{0xD6}, byte{0xC7}, byte{0x81}, byte{0xE1}, byte{0x3F}}
|
||||
};
|
||||
constexpr static std::array<byte, 23> Sample2
|
||||
{
|
||||
{
|
||||
byte{0xE5},
|
||||
byte{0x78},
|
||||
byte{0xBA},
|
||||
byte{0xD5},
|
||||
byte{0x00},
|
||||
byte{0xA2},
|
||||
byte{0x98},
|
||||
byte{0xFE},
|
||||
byte{0xF1},
|
||||
byte{0xEF},
|
||||
byte{0x2A},
|
||||
byte{0x90},
|
||||
byte{0x6B},
|
||||
byte{0xC9},
|
||||
byte{0x85},
|
||||
byte{0x22},
|
||||
byte{0x00},
|
||||
byte{0xA5},
|
||||
byte{0xEC},
|
||||
byte{0x20},
|
||||
byte{0x23},
|
||||
byte{0xF6},
|
||||
byte{0xB2}
|
||||
}
|
||||
};
|
||||
constexpr static std::array<byte, 23> Sample3
|
||||
{
|
||||
{
|
||||
byte{0xC3},
|
||||
byte{0x91},
|
||||
byte{0x0B},
|
||||
byte{0x50},
|
||||
byte{0xAF},
|
||||
byte{0x59},
|
||||
byte{0x5B},
|
||||
byte{0x30},
|
||||
byte{0x24},
|
||||
byte{0xDA},
|
||||
byte{0x22},
|
||||
byte{0x3C},
|
||||
byte{0x30},
|
||||
byte{0xBA},
|
||||
byte{0xDB},
|
||||
byte{0x1C},
|
||||
byte{0x18},
|
||||
byte{0x6F},
|
||||
byte{0xBB},
|
||||
byte{0xE6},
|
||||
byte{0x0B},
|
||||
byte{0x70},
|
||||
byte{0x0E}
|
||||
}
|
||||
};
|
||||
constexpr static std::array<byte, 0> Sample4{};
|
||||
constexpr static std::array<byte, 22> Sample5
|
||||
{
|
||||
byte{0xB4},
|
||||
byte{0xC7},
|
||||
byte{0xDB},
|
||||
byte{0xF4},
|
||||
byte{0x1F},
|
||||
byte{0x18},
|
||||
byte{0xEE},
|
||||
byte{0xC5},
|
||||
byte{0x67},
|
||||
byte{0x12},
|
||||
byte{0x6E},
|
||||
byte{0x96},
|
||||
byte{0x47},
|
||||
byte{0x4E},
|
||||
byte{0x98},
|
||||
byte{0x94},
|
||||
byte{0xFA},
|
||||
byte{0x6B},
|
||||
byte{0x90},
|
||||
byte{0xA6},
|
||||
byte{0x48},
|
||||
byte{0xF2}
|
||||
};
|
||||
|
||||
constexpr static std::array<cdb_core::ReadOnlySpan<byte>, 5> s_samples
|
||||
{
|
||||
{
|
||||
cdb_core::ReadOnlySpan<byte>{Sample1},
|
||||
cdb_core::ReadOnlySpan<byte>{Sample2},
|
||||
cdb_core::ReadOnlySpan<byte>{Sample3},
|
||||
cdb_core::ReadOnlySpan<byte>{Sample4},
|
||||
cdb_core::ReadOnlySpan<byte>{Sample5},
|
||||
}
|
||||
};
|
||||
|
||||
constexpr static uint32_t Expected[]
|
||||
{
|
||||
2786232081u,
|
||||
1744821187u,
|
||||
2853437495u,
|
||||
0u,
|
||||
2029626740u,
|
||||
};
|
||||
|
||||
template<size_t N>
|
||||
static int64_t MeasureLoop(std::array<cdb_core::ReadOnlySpan<byte>, N> samples)
|
||||
{
|
||||
const int outerLoopCount = 10000;
|
||||
cdb_core::Stopwatch watch{};
|
||||
|
||||
watch.Start();
|
||||
for (int j = 0; j < outerLoopCount; j++)
|
||||
{
|
||||
for (auto sample : samples)
|
||||
{
|
||||
[[maybe_unused]] int32_t crc = cdb_core::Crc32::Update(0, sample);
|
||||
}
|
||||
}
|
||||
|
||||
watch.Stop();
|
||||
return watch.ElapsedRaw();
|
||||
}
|
||||
|
||||
BEGIN_TEST_METHOD_ATTRIBUTE(Crc32Check)
|
||||
TEST_OWNER(L"jthunter")
|
||||
END_TEST_METHOD_ATTRIBUTE()
|
||||
|
||||
TEST_METHOD(Crc32Check)
|
||||
{
|
||||
// Warm up the loop and verify correctness.
|
||||
for (size_t i = 0; i < s_samples.size(); i++)
|
||||
{
|
||||
cdb_core::ReadOnlySpan<byte> sample = s_samples[i];
|
||||
uint32_t c1 = cdb_core::Crc32::Update(0, sample);
|
||||
Assert::AreEqual(Expected[i], c1);
|
||||
}
|
||||
|
||||
// Measure performance.
|
||||
int64_t ticks = MeasureLoop(s_samples);
|
||||
Logger::WriteMessage(cdb_core::make_string<std::string>("Crc32: %lld", ticks).c_str());
|
||||
}
|
||||
};
|
||||
}
|
||||
62
src/Core/Core.Native.Tests.Unit/DeepCompareUnitTests.cpp
Normal file
62
src/Core/Core.Native.Tests.Unit/DeepCompareUnitTests.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#include "pch.h"
|
||||
#include "CppUnitTest.h"
|
||||
|
||||
namespace cdb_core_test
|
||||
{
|
||||
using namespace std::literals;
|
||||
using Assert = Microsoft::VisualStudio::CppUnitTestFramework::Assert;
|
||||
|
||||
TEST_CLASS(DeepCompareUnitTests)
|
||||
{
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether deep compare works correctly on object trees.
|
||||
/// </summary>
|
||||
TEST_METHOD(ObjectTrees)
|
||||
{
|
||||
std::vector<int> v1{0, 1, 2, 3};
|
||||
Assert::IsTrue(cdb_core::DeepCompare(v1, v1));
|
||||
Assert::IsTrue(cdb_core::DeepCompare(v1, std::vector<int>{0, 1, 2, 3}));
|
||||
Assert::IsFalse(cdb_core::DeepCompare(v1, std::vector<int>{0, 1, 2, 3, 4}));
|
||||
Assert::IsFalse(cdb_core::DeepCompare(v1, std::vector<int>{0, 1, 2, 4}));
|
||||
Assert::IsFalse(cdb_core::DeepCompare(v1, std::vector<int>{9, 1, 2, 3}));
|
||||
|
||||
std::unique_ptr<int> u1 = std::make_unique<int>(42);
|
||||
Assert::IsTrue(cdb_core::DeepCompare(std::unique_ptr<int>{}, std::unique_ptr<int>{}));
|
||||
Assert::IsTrue(cdb_core::DeepCompare(u1, u1));
|
||||
Assert::IsTrue(cdb_core::DeepCompare(u1, std::make_unique<int>(42)));
|
||||
Assert::IsFalse(cdb_core::DeepCompare(u1, std::make_unique<int>(43)));
|
||||
|
||||
ObjectTree o1{std::move(v1), std::move(u1)};
|
||||
Assert::IsTrue(cdb_core::DeepCompare(o1, o1));
|
||||
Assert::IsTrue(cdb_core::DeepCompare(o1, ObjectTree{{0, 1, 2, 3}, std::make_unique<int>(42)}));
|
||||
Assert::IsFalse(cdb_core::DeepCompare(o1, ObjectTree{{0, 1, 2, 4}, std::make_unique<int>(42)}));
|
||||
Assert::IsFalse(cdb_core::DeepCompare(o1, ObjectTree{{0, 1, 2, 3}, std::make_unique<int>(43)}));
|
||||
}
|
||||
|
||||
struct ObjectTree final
|
||||
{
|
||||
std::vector<int> V;
|
||||
std::unique_ptr<int> U;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
namespace cdb_core
|
||||
{
|
||||
template<>
|
||||
struct DeepComparer<cdb_core_test::DeepCompareUnitTests::ObjectTree>
|
||||
{
|
||||
using value_type = cdb_core_test::DeepCompareUnitTests::ObjectTree;
|
||||
|
||||
bool operator()(const value_type& x, const value_type& y) const noexcept
|
||||
{
|
||||
return cdb_core::DeepCompare(x.V, y.V) && cdb_core::DeepCompare(x.U, y.U);
|
||||
}
|
||||
};
|
||||
}
|
||||
209
src/Core/Core.Native.Tests.Unit/HashCodeUnitTests.cpp
Normal file
209
src/Core/Core.Native.Tests.Unit/HashCodeUnitTests.cpp
Normal file
@@ -0,0 +1,209 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#include "pch.h"
|
||||
#include "CppUnitTest.h"
|
||||
|
||||
namespace cdb_core_test
|
||||
{
|
||||
using namespace std::literals;
|
||||
using Assert = Microsoft::VisualStudio::CppUnitTestFramework::Assert;
|
||||
|
||||
struct ConstHashCodeType {};
|
||||
|
||||
struct ConstComparer
|
||||
{
|
||||
constexpr static size_t ConstantValue = 1234;
|
||||
|
||||
std::size_t operator()(cdb_core_test::ConstHashCodeType const&) const noexcept
|
||||
{
|
||||
return ConstantValue;
|
||||
}
|
||||
};
|
||||
|
||||
TEST_CLASS(HashCodeUnitTests)
|
||||
{
|
||||
public:
|
||||
|
||||
TEST_METHOD(AddHashCode)
|
||||
{
|
||||
cdb_core::HashCode hc1{};
|
||||
hc1.Add("Hello"sv);
|
||||
|
||||
cdb_core::HashCode hc2{};
|
||||
hc2.AddHash(std::hash<std::string_view>{}.operator()("Hello"sv));
|
||||
|
||||
Assert::AreEqual(hc1.ToHashCode(), hc2.ToHashCode());
|
||||
}
|
||||
|
||||
TEST_METHOD(AddGeneric)
|
||||
{
|
||||
cdb_core::HashCode hc{};
|
||||
hc.Add(1);
|
||||
hc.Add(ConstHashCodeType{});
|
||||
|
||||
cdb_core::HashCode expected{};
|
||||
expected.Add(1);
|
||||
expected.AddHash(ConstComparer::ConstantValue);
|
||||
|
||||
Assert::AreEqual(expected.ToHashCode(), hc.ToHashCode());
|
||||
}
|
||||
|
||||
TEST_METHOD(AddNull)
|
||||
{
|
||||
cdb_core::HashCode hc{};
|
||||
hc.Add(static_cast<char*>(nullptr));
|
||||
|
||||
cdb_core::HashCode expected{};
|
||||
expected.AddHash(std::hash<char*>{}.operator()(nullptr));
|
||||
|
||||
Assert::AreEqual(expected.ToHashCode(), hc.ToHashCode());
|
||||
}
|
||||
|
||||
TEST_METHOD(AddGenericEqualityComparer)
|
||||
{
|
||||
cdb_core::HashCode hc{};
|
||||
hc.Add(1);
|
||||
hc.Add<ConstHashCodeType, ConstComparer>(ConstHashCodeType{});
|
||||
|
||||
cdb_core::HashCode expected{};
|
||||
expected.Add(1);
|
||||
expected.AddHash(ConstComparer::ConstantValue);
|
||||
|
||||
Assert::AreEqual(expected.ToHashCode(), hc.ToHashCode());
|
||||
}
|
||||
|
||||
TEST_METHOD(Combine)
|
||||
{
|
||||
std::vector<size_t> hcs =
|
||||
{
|
||||
cdb_core::HashCode::Combine(1),
|
||||
cdb_core::HashCode::Combine(1, 2),
|
||||
cdb_core::HashCode::Combine(1, 2, 3),
|
||||
cdb_core::HashCode::Combine(1, 2, 3, 4),
|
||||
cdb_core::HashCode::Combine(1, 2, 3, 4, 5),
|
||||
cdb_core::HashCode::Combine(1, 2, 3, 4, 5, 6),
|
||||
cdb_core::HashCode::Combine(1, 2, 3, 4, 5, 6, 7),
|
||||
cdb_core::HashCode::Combine(1, 2, 3, 4, 5, 6, 7, 8),
|
||||
|
||||
cdb_core::HashCode::Combine(2),
|
||||
cdb_core::HashCode::Combine(2, 3),
|
||||
cdb_core::HashCode::Combine(2, 3, 4),
|
||||
cdb_core::HashCode::Combine(2, 3, 4, 5),
|
||||
cdb_core::HashCode::Combine(2, 3, 4, 5, 6),
|
||||
cdb_core::HashCode::Combine(2, 3, 4, 5, 6, 7),
|
||||
cdb_core::HashCode::Combine(2, 3, 4, 5, 6, 7, 8),
|
||||
cdb_core::HashCode::Combine(2, 3, 4, 5, 6, 7, 8, 9),
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < hcs.size(); i++)
|
||||
{
|
||||
for (size_t j = 0; j < hcs.size(); j++)
|
||||
{
|
||||
if (i == j)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
Assert::AreNotEqual(hcs[i], hcs[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(CombineAdd1)
|
||||
{
|
||||
cdb_core::HashCode hc{};
|
||||
hc.Add(1);
|
||||
Assert::AreEqual(hc.ToHashCode(), cdb_core::HashCode::Combine(1));
|
||||
}
|
||||
|
||||
TEST_METHOD(CombineAdd2)
|
||||
{
|
||||
cdb_core::HashCode hc{};
|
||||
hc.Add(1);
|
||||
hc.Add(2);
|
||||
Assert::AreEqual(hc.ToHashCode(), cdb_core::HashCode::Combine(1, 2));
|
||||
}
|
||||
|
||||
TEST_METHOD(CombineAdd3)
|
||||
{
|
||||
cdb_core::HashCode hc{};
|
||||
hc.Add(1);
|
||||
hc.Add(2);
|
||||
hc.Add(3);
|
||||
Assert::AreEqual(hc.ToHashCode(), cdb_core::HashCode::Combine(1, 2, 3));
|
||||
}
|
||||
|
||||
TEST_METHOD(CombineAdd4)
|
||||
{
|
||||
cdb_core::HashCode hc{};
|
||||
hc.Add(1);
|
||||
hc.Add(2);
|
||||
hc.Add(3);
|
||||
hc.Add(4);
|
||||
Assert::AreEqual(hc.ToHashCode(), cdb_core::HashCode::Combine(1, 2, 3, 4));
|
||||
}
|
||||
|
||||
TEST_METHOD(CombineAdd5)
|
||||
{
|
||||
cdb_core::HashCode hc{};
|
||||
hc.Add(1);
|
||||
hc.Add(2);
|
||||
hc.Add(3);
|
||||
hc.Add(4);
|
||||
hc.Add(5);
|
||||
Assert::AreEqual(hc.ToHashCode(), cdb_core::HashCode::Combine(1, 2, 3, 4, 5));
|
||||
}
|
||||
|
||||
TEST_METHOD(CombineAdd6)
|
||||
{
|
||||
cdb_core::HashCode hc{};
|
||||
hc.Add(1);
|
||||
hc.Add(2);
|
||||
hc.Add(3);
|
||||
hc.Add(4);
|
||||
hc.Add(5);
|
||||
hc.Add(6);
|
||||
Assert::AreEqual(hc.ToHashCode(), cdb_core::HashCode::Combine(1, 2, 3, 4, 5, 6));
|
||||
}
|
||||
|
||||
TEST_METHOD(CombineAdd7)
|
||||
{
|
||||
cdb_core::HashCode hc{};
|
||||
hc.Add(1);
|
||||
hc.Add(2);
|
||||
hc.Add(3);
|
||||
hc.Add(4);
|
||||
hc.Add(5);
|
||||
hc.Add(6);
|
||||
hc.Add(7);
|
||||
Assert::AreEqual(hc.ToHashCode(), cdb_core::HashCode::Combine(1, 2, 3, 4, 5, 6, 7));
|
||||
}
|
||||
|
||||
TEST_METHOD(CombineAdd8)
|
||||
{
|
||||
cdb_core::HashCode hc{};
|
||||
hc.Add(1);
|
||||
hc.Add(2);
|
||||
hc.Add(3);
|
||||
hc.Add(4);
|
||||
hc.Add(5);
|
||||
hc.Add(6);
|
||||
hc.Add(7);
|
||||
hc.Add(8);
|
||||
Assert::AreEqual(hc.ToHashCode(), cdb_core::HashCode::Combine(1, 2, 3, 4, 5, 6, 7, 8));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
namespace std
|
||||
{
|
||||
template<>
|
||||
struct hash<cdb_core_test::ConstHashCodeType>
|
||||
{
|
||||
std::size_t operator()(cdb_core_test::ConstHashCodeType const&) const noexcept
|
||||
{
|
||||
return cdb_core_test::ConstComparer::ConstantValue;
|
||||
}
|
||||
};
|
||||
}
|
||||
230
src/Core/Core.Native.Tests.Unit/MemoryUnitTests.cpp
Normal file
230
src/Core/Core.Native.Tests.Unit/MemoryUnitTests.cpp
Normal file
@@ -0,0 +1,230 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#include "pch.h"
|
||||
#include "CppUnitTest.h"
|
||||
|
||||
namespace cdb_core_test
|
||||
{
|
||||
using Assert = Microsoft::VisualStudio::CppUnitTestFramework::Assert;
|
||||
using Logger = Microsoft::VisualStudio::CppUnitTestFramework::Logger;
|
||||
|
||||
template<class T>
|
||||
void TestRange(T m, int start, int length)
|
||||
{
|
||||
int i = 0;
|
||||
for (auto b : m.AsSpan())
|
||||
{
|
||||
Assert::AreEqual(static_cast<byte>(i + start), b);
|
||||
i++;
|
||||
}
|
||||
Assert::AreEqual(length, i);
|
||||
Assert::AreEqual(static_cast<uint32_t>(length), m.Length());
|
||||
Assert::AreEqual(length == 0, m.IsEmpty());
|
||||
}
|
||||
|
||||
TEST_CLASS(MemoryUnitTests)
|
||||
{
|
||||
public:
|
||||
|
||||
TEST_METHOD(SliceTest)
|
||||
{
|
||||
Logger::WriteMessage("Memory<byte>:");
|
||||
cdb_core::Memory<byte> m{new byte[10], 10};
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
m.AsSpan()[i] = static_cast<byte>(i);
|
||||
}
|
||||
TestRange(m, 0, 10);
|
||||
|
||||
{
|
||||
const cdb_core::ReadOnlyMemory<byte> m1 = static_cast<const cdb_core::Memory<byte>&>(m).Slice(5);
|
||||
TestRange(m1, 5, 5);
|
||||
}
|
||||
|
||||
{
|
||||
const auto m2 = m.Slice(5, 2);
|
||||
TestRange(m2, 5, 2);
|
||||
}
|
||||
|
||||
{
|
||||
const auto m3 = m.Slice(10);
|
||||
TestRange(m3, 0, 0);
|
||||
}
|
||||
|
||||
{
|
||||
const auto m4 = m.Slice(0, 0);
|
||||
TestRange(m4, 10, 0);
|
||||
}
|
||||
|
||||
{
|
||||
cdb_core::Memory<byte> m5{new byte[5], 5};
|
||||
m.Slice(5, 5).AsSpan().CopyTo(m5.AsSpan());
|
||||
TestRange(m5, 5, 5);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(CopyTest)
|
||||
{
|
||||
Logger::WriteMessage("Memory<byte>:");
|
||||
cdb_core::Memory<byte> m{new byte[10], 10};
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
m.AsSpan()[i] = static_cast<byte>(i);
|
||||
}
|
||||
TestRange(m, 0, 10);
|
||||
|
||||
{
|
||||
cdb_core::Memory<byte> m2{new byte[2], 2};
|
||||
m.Slice(5, 2).AsSpan().CopyTo(m2.AsSpan());
|
||||
TestRange(m2, 5, 2);
|
||||
}
|
||||
|
||||
{
|
||||
cdb_core::Memory<byte> m3{new byte[0], 0};
|
||||
m.Slice(10).AsSpan().CopyTo(m3.AsSpan());
|
||||
TestRange(m3, 0, 0);
|
||||
}
|
||||
|
||||
{
|
||||
cdb_core::Memory<byte> m3{nullptr, 0};
|
||||
m.Slice(10).AsSpan().CopyTo(m3.AsSpan());
|
||||
TestRange(m3, 0, 0);
|
||||
}
|
||||
|
||||
{
|
||||
cdb_core::Memory<byte> m5{new byte[5], 5};
|
||||
m.Slice(5, 5).AsSpan().CopyTo(m5.AsSpan());
|
||||
TestRange(m5, 5, 5);
|
||||
}
|
||||
|
||||
{
|
||||
cdb_core::Memory<byte> m6{m.AsSpan()};
|
||||
TestRange(m6, 0, 10);
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<byte> v{};
|
||||
v.reserve(m.Length());
|
||||
for (auto b : m.AsSpan()) { v.emplace_back(b); }
|
||||
cdb_core::Memory<byte> m7{v};
|
||||
TestRange(m7, 0, 10);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(CastTest)
|
||||
{
|
||||
const int num = 3;
|
||||
cdb_core::Memory<int32_t> m{new int32_t[num], static_cast<uint32_t>(num)};
|
||||
for (int i = 0; i < num; i++)
|
||||
{
|
||||
m.AsSpan()[i] = i + 1;
|
||||
}
|
||||
|
||||
cdb_core::Span<byte> mb = cdb_core::MemoryMarshal::Cast<int32_t, byte>(m.AsSpan());
|
||||
byte expectedBytes[] = {
|
||||
static_cast<byte>(1),
|
||||
static_cast<byte>(0),
|
||||
static_cast<byte>(0),
|
||||
static_cast<byte>(0),
|
||||
static_cast<byte>(2),
|
||||
static_cast<byte>(0),
|
||||
static_cast<byte>(0),
|
||||
static_cast<byte>(0),
|
||||
static_cast<byte>(3),
|
||||
static_cast<byte>(0),
|
||||
static_cast<byte>(0),
|
||||
static_cast<byte>(0),
|
||||
};
|
||||
for (size_t i = 0; i < std::size(expectedBytes); i++)
|
||||
{
|
||||
Assert::AreEqual(expectedBytes[i], mb[static_cast<uint32_t>(i)], cdb_core::make_string<std::wstring>(L"i = %llu", i).c_str());
|
||||
}
|
||||
Assert::AreEqual(std::size(expectedBytes), static_cast<size_t>(mb.Length()));
|
||||
|
||||
cdb_core::Span<int64_t> m64 = cdb_core::MemoryMarshal::Cast<int32_t, int64_t>(m.AsSpan());
|
||||
int64_t expected64[] = {static_cast<int64_t>(0x200000001)};
|
||||
for (size_t i = 0; i < std::size(expected64); i++)
|
||||
{
|
||||
Assert::AreEqual(expected64[i], m64[static_cast<uint32_t>(i)], cdb_core::make_string<std::wstring>(L"i = %llu", i).c_str());
|
||||
}
|
||||
Assert::AreEqual(std::size(expected64), static_cast<size_t>(m64.Length()));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void MemoryEqualityTest(T a, T b, T c, T d)
|
||||
{
|
||||
// ReSharper disable once CppIdenticalOperandsInBinaryExpression
|
||||
Assert::IsTrue(a == a);
|
||||
Assert::AreNotEqual(a, b);
|
||||
Assert::IsFalse(a == b);
|
||||
Assert::IsTrue(a != b);
|
||||
Assert::AreEqual(a, c);
|
||||
Assert::IsTrue(a == c);
|
||||
Assert::IsFalse(a != c);
|
||||
Assert::AreNotEqual(a, d);
|
||||
Assert::IsFalse(a == d);
|
||||
Assert::IsTrue(a != d);
|
||||
}
|
||||
|
||||
TEST_METHOD(EqualityTest)
|
||||
{
|
||||
cdb_core::Memory<std::byte> a = cdb_core::Memory<std::byte>{5};
|
||||
cdb_core::Memory<std::byte> b = cdb_core::Memory<std::byte>{5};
|
||||
cdb_core::Memory<std::byte> c = a.Slice(0);
|
||||
cdb_core::Memory<std::byte> d = a.Slice(1);
|
||||
|
||||
MemoryEqualityTest(a, b, c, d);
|
||||
MemoryEqualityTest(a.AsSpan(), b.AsSpan(), c.AsSpan(), d.AsSpan());
|
||||
MemoryEqualityTest(
|
||||
cdb_core::ReadOnlySpan<byte>(a.AsSpan()),
|
||||
cdb_core::ReadOnlySpan<byte>(b.AsSpan()),
|
||||
cdb_core::ReadOnlySpan<byte>(c.AsSpan()),
|
||||
cdb_core::ReadOnlySpan<byte>(d.AsSpan()));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
namespace Microsoft::VisualStudio::CppUnitTestFramework
|
||||
{
|
||||
template<> inline std::wstring ToString<std::byte>(const std::byte& q)
|
||||
{
|
||||
RETURN_WIDE_STRING(static_cast<int>(q));
|
||||
}
|
||||
|
||||
template<> inline std::wstring ToString<std::byte>(const std::byte* q)
|
||||
{
|
||||
RETURN_WIDE_STRING(q);
|
||||
}
|
||||
}
|
||||
|
||||
namespace Microsoft::VisualStudio::CppUnitTestFramework
|
||||
{
|
||||
template<>
|
||||
std::wstring ToString<cdb_core::Memory<std::byte>>(
|
||||
const cdb_core::Memory<std::byte>& q)
|
||||
{
|
||||
return cdb_core::make_string(L"{p:%p l:%u}", &q.AsSpan()[0], q.Length());
|
||||
}
|
||||
}
|
||||
|
||||
namespace Microsoft::VisualStudio::CppUnitTestFramework
|
||||
{
|
||||
template<>
|
||||
std::wstring ToString<cdb_core::Span<std::byte>>(
|
||||
const cdb_core::Span<std::byte>& q)
|
||||
{
|
||||
return cdb_core::make_string(L"{p:%p l:%u}", &q[0], q.Length());
|
||||
}
|
||||
}
|
||||
|
||||
namespace Microsoft::VisualStudio::CppUnitTestFramework
|
||||
{
|
||||
template<>
|
||||
std::wstring ToString<cdb_core::ReadOnlySpan<std::byte>>(
|
||||
const cdb_core::ReadOnlySpan<std::byte>& q)
|
||||
{
|
||||
return cdb_core::make_string(L"{p:%p l:%u}", &q[0], q.Length());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>16.0</VCProjectVersion>
|
||||
<ProjectGuid>{BE99633B-5D7D-41DA-8550-035E6689B243}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>cdb_core_tests</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
<ProjectSubType>NativeUnitTestProject</ProjectSubType>
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<UseOfMfc>false</UseOfMfc>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<UseOfMfc>false</UseOfMfc>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>$(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<AdditionalLibraryDirectories>$(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>$(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<AdditionalLibraryDirectories>$(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Base64UnitTests.cpp" />
|
||||
<ClCompile Include="ContractUnitTests.cpp" />
|
||||
<ClCompile Include="Crc32UnitTests.cpp" />
|
||||
<ClCompile Include="DeepCompareUnitTests.cpp" />
|
||||
<ClCompile Include="HashCodeUnitTests.cpp" />
|
||||
<ClCompile Include="MemoryUnitTests.cpp" />
|
||||
<ClCompile Include="TimeSpanTests.cpp" />
|
||||
<ClCompile Include="TlaAllocatorUnitTests.cpp" />
|
||||
<ClCompile Include="StringsUnitTests.cpp" />
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Core.Native\Microsoft.Azure.Cosmos.Core.Native.vcxproj">
|
||||
<Project>{eaed7d41-3de6-4c41-a0e4-40d53ea3daba}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
</Project>
|
||||
111
src/Core/Core.Native.Tests.Unit/StringsUnitTests.cpp
Normal file
111
src/Core/Core.Native.Tests.Unit/StringsUnitTests.cpp
Normal file
@@ -0,0 +1,111 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#include "pch.h"
|
||||
#include "CppUnitTest.h"
|
||||
|
||||
namespace cdb_core_test
|
||||
{
|
||||
using namespace std::literals;
|
||||
using Assert = Microsoft::VisualStudio::CppUnitTestFramework::Assert;
|
||||
using Logger = Microsoft::VisualStudio::CppUnitTestFramework::Logger;
|
||||
|
||||
TEST_CLASS(StringsUnitTests)
|
||||
{
|
||||
public:
|
||||
/// <summary>
|
||||
/// Verifies the code samples used in the Doc Comments actually compile.
|
||||
/// </summary>
|
||||
TEST_METHOD(DocCommentsExamples)
|
||||
{
|
||||
{
|
||||
std::string u8 = cdb_core::make_string("foo: %s", "some value");
|
||||
std::wstring u16 = cdb_core::string_join(L"foo: %s", L"some wide-string value");
|
||||
tla::string s = cdb_core::string_join("foo: %s", "some value");
|
||||
}
|
||||
|
||||
{
|
||||
Logger::WriteMessage(cdb_core::make_string<std::string>("foo: %s", "some value").c_str());
|
||||
Logger::WriteMessage(cdb_core::make_string<std::wstring>(L"foo: %s", L"some wide-string value").c_str());
|
||||
}
|
||||
|
||||
{
|
||||
std::string u8 = cdb_core::string_join("a", "b", "c");
|
||||
std::wstring u16 = cdb_core::string_join(L"a", L"b", L"c");
|
||||
tla::string s = cdb_core::string_join("a", "b", "c");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that make_string inserts the arguments in the proper order.
|
||||
/// </summary>
|
||||
TEST_METHOD(MakeStringOrderTest)
|
||||
{
|
||||
MakeStringOrder<std::string>("%s %s %s", "a", "b", "c", "a b c");
|
||||
MakeStringOrder<std::wstring>(L"%s %s %s", L"a", L"b", L"c", L"a b c");
|
||||
MakeStringOrder<tla::string>("%s %s %s", "a", "b", "c", "a b c");
|
||||
MakeStringOrder<tla::wstring>(L"%s %s %s", L"a", L"b", L"c", L"a b c");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that make_string properly supports string precision format specifiers.
|
||||
/// </summary>
|
||||
TEST_METHOD(MakeStringPrecisionTest)
|
||||
{
|
||||
MakeStringPrecision<std::string>("a %.*s c", "b"sv, "a b c");
|
||||
MakeStringPrecision<std::wstring>(L"a %.*s c", L"b"sv, L"a b c");
|
||||
MakeStringPrecision<tla::string>("a %.*s c", "b"sv, "a b c");
|
||||
MakeStringPrecision<tla::wstring>(L"a %.*s c", L"b"sv, L"a b c");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that string join return type inference works across various return types.
|
||||
/// </summary>
|
||||
TEST_METHOD(StringJoinTest)
|
||||
{
|
||||
StringJoin<std::string>("a", "b", "c", "abc");
|
||||
StringJoin<std::wstring>(L"a", L"b", L"c", L"abc");
|
||||
StringJoin<tla::string>("a", "b", "c", "abc");
|
||||
StringJoin<tla::wstring>(L"a", L"b", L"c", L"abc");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that string join accepts heterogeneous argument types as long as the return
|
||||
/// value has an append operator with that rvalue.
|
||||
/// </summary>
|
||||
TEST_METHOD(HeterogeneousStringJoinTest)
|
||||
{
|
||||
const std::string u8 = cdb_core::string_join("a", "b"s, "c"sv);
|
||||
Assert::AreEqual("abc", u8.c_str());
|
||||
const tla::string s = cdb_core::string_join("a", "b"s, "c"sv);
|
||||
Assert::AreEqual("abc", s.c_str());
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename TString, typename TElem = typename TString::value_type>
|
||||
void StringJoin(const TElem* a, const TElem* b, const TElem* c, const TElem* expected)
|
||||
{
|
||||
Logger::WriteMessage(cdb_core::make_string<std::string>("%s\n", typeid(TString).name()).c_str());
|
||||
TString abc = cdb_core::string_join(a, b, c);
|
||||
Assert::AreEqual(expected, abc.c_str());
|
||||
|
||||
TString abc2 = cdb_core::string_join(TString(a), TString(b), TString(c));
|
||||
Assert::AreEqual(expected, abc2.c_str());
|
||||
}
|
||||
|
||||
template<typename TString, typename TElem = typename TString::value_type>
|
||||
void MakeStringOrder(const TElem* format, const TElem* a, const TElem* b, const TElem* c, const TElem* expected)
|
||||
{
|
||||
TString s = cdb_core::make_string<TString>(format, a, b, c);
|
||||
Assert::AreEqual(expected, s.data());
|
||||
}
|
||||
|
||||
template<typename TString, typename TElem = typename TString::value_type>
|
||||
void MakeStringPrecision(const TElem* format, std::basic_string_view<TElem> arg, const TElem* expected)
|
||||
{
|
||||
TString s = cdb_core::make_string<TString>(format, arg.size(), arg.data());
|
||||
Assert::AreEqual(expected, s.data());
|
||||
}
|
||||
};
|
||||
}
|
||||
41
src/Core/Core.Native.Tests.Unit/TimeSpanTests.cpp
Normal file
41
src/Core/Core.Native.Tests.Unit/TimeSpanTests.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#include "pch.h"
|
||||
#include "CppUnitTest.h"
|
||||
|
||||
namespace cdb_core_test
|
||||
{
|
||||
using namespace std::literals;
|
||||
using Assert = Microsoft::VisualStudio::CppUnitTestFramework::Assert;
|
||||
|
||||
TEST_CLASS(TimeSpanTests)
|
||||
{
|
||||
TEST_METHOD(TestTimeSpanCreationMilliseconds)
|
||||
{
|
||||
for (int64_t i = INT16_MIN; i < INT16_MAX; i += 97)
|
||||
{
|
||||
cdb_core::TimeSpan timeSpan = cdb_core::TimeSpan::FromMilliseconds(i);
|
||||
Assert::AreEqual(i, timeSpan.GetTotalMilliseconds());
|
||||
Assert::AreEqual(i / 1000, timeSpan.GetTotalSeconds());
|
||||
|
||||
// 10,000 ticks in 1 ms
|
||||
Assert::AreEqual(i * 10000, timeSpan.GetTotalTicks());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(TestTimeSpanCreationSeconds)
|
||||
{
|
||||
for (int64_t i = INT16_MIN; i < INT16_MAX; i += 137)
|
||||
{
|
||||
cdb_core::TimeSpan timeSpan = cdb_core::TimeSpan::FromSeconds(i);
|
||||
Assert::AreEqual(i * 1000, timeSpan.GetTotalMilliseconds());
|
||||
Assert::AreEqual(i, timeSpan.GetTotalSeconds());
|
||||
|
||||
// 10,000,000 ticks in 1s
|
||||
Assert::AreEqual(i * 10000000, timeSpan.GetTotalTicks());
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
41
src/Core/Core.Native.Tests.Unit/TlaAllocatorUnitTests.cpp
Normal file
41
src/Core/Core.Native.Tests.Unit/TlaAllocatorUnitTests.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#include "pch.h"
|
||||
#include "CppUnitTest.h"
|
||||
|
||||
namespace cdb_core_test
|
||||
{
|
||||
using namespace tla::literals;
|
||||
using Assert = Microsoft::VisualStudio::CppUnitTestFramework::Assert;
|
||||
using Logger = Microsoft::VisualStudio::CppUnitTestFramework::Logger;
|
||||
|
||||
TEST_CLASS(TlaAllocatorUnitTests)
|
||||
{
|
||||
public:
|
||||
TEST_METHOD(StringTest) noexcept
|
||||
{
|
||||
const tla::string abc = cdb_core::string_join("a", "b", "c"_s);
|
||||
Assert::AreEqual("abc", abc.c_str());
|
||||
|
||||
const tla::string abc2 = cdb_core::string_join(tla::string("a"), tla::string("b"), tla::string("c"));
|
||||
Assert::AreEqual("abc", abc2.c_str());
|
||||
|
||||
const tla::wstring abc3 = cdb_core::string_join(tla::wstring(L"a"), tla::wstring(L"b"), tla::wstring(L"c"));
|
||||
Assert::AreEqual(L"abc", abc3.c_str());
|
||||
|
||||
struct T
|
||||
{
|
||||
static size_t GetHashCode() noexcept { return 0; }
|
||||
};
|
||||
|
||||
Logger::WriteMessage(typeid(&T::GetHashCode).name());
|
||||
Logger::WriteMessage("\n");
|
||||
Logger::WriteMessage(typeid(size_t (T::*)() const noexcept).name());
|
||||
Logger::WriteMessage("\n");
|
||||
const bool s = std::is_same<decltype(&T::GetHashCode), size_t (T::*)() const noexcept>::value;
|
||||
Logger::WriteMessage(cdb_core::make_string<tla::string>("%s\n", s ? "true" : "false").c_str());
|
||||
}
|
||||
};
|
||||
}
|
||||
5
src/Core/Core.Native.Tests.Unit/pch.cpp
Normal file
5
src/Core/Core.Native.Tests.Unit/pch.cpp
Normal file
@@ -0,0 +1,5 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#include "pch.h"
|
||||
11
src/Core/Core.Native.Tests.Unit/pch.h
Normal file
11
src/Core/Core.Native.Tests.Unit/pch.h
Normal file
@@ -0,0 +1,11 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
using std::byte;
|
||||
|
||||
#include "../Core.Native/Core.Native.h"
|
||||
557
src/Core/Core.Native/Base64.h
Normal file
557
src/Core/Core.Native/Base64.h
Normal file
@@ -0,0 +1,557 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
#include "Contract.h"
|
||||
#include "ReadOnlySpan.h"
|
||||
#include "Span.h"
|
||||
|
||||
namespace cdb_core
|
||||
{
|
||||
template<class T> struct ReadOnlySpan;
|
||||
template<class T> struct Span;
|
||||
|
||||
struct Base64 final
|
||||
{
|
||||
enum class Flags
|
||||
{
|
||||
None = 0,
|
||||
NoPad = 1,
|
||||
NoLinefeed = 2,
|
||||
Url = 4,
|
||||
};
|
||||
|
||||
// length functions
|
||||
constexpr static uint32_t GetEncodeRequiredLength(uint32_t srcLen, Flags flags = Flags::None) noexcept;
|
||||
template<class TElem, class TTraits>
|
||||
constexpr static uint32_t GetDecodeRequiredLength(std::basic_string_view<TElem, TTraits> src) noexcept;
|
||||
constexpr static uint32_t GetDecodeRequiredLength(uint32_t srcLen) noexcept;
|
||||
|
||||
/// <summary>
|
||||
/// Converts a sequence of bytes into a Base64 text string.
|
||||
/// </summary>
|
||||
/// <typeparam name="TChar">The kind of text element.</typeparam>
|
||||
/// <param name="src">The source bytes to convert.</param>
|
||||
/// <param name="dest">A buffer to receive the text string. This buffer MUST be at least as large as indicated by <see cref="GetEncodeRequiredLength" />.</param>
|
||||
/// <param name="flags">Optional flags</param>
|
||||
/// <returns>A sub-span over <paramref name="dest"/> containing the encoded string.</returns>
|
||||
template<typename TChar>
|
||||
constexpr static Span<TChar> Encode(ReadOnlySpan<std::byte> src, Span<TChar> dest,
|
||||
Flags flags = Flags::None) noexcept;
|
||||
|
||||
/// <summary>
|
||||
/// Converts a Base64 text string back into a sequence of bytes.
|
||||
/// </summary>
|
||||
/// <typeparam name="TChar">The kind of text element.</typeparam>
|
||||
/// <param name="src">The source Base64 string to convert.</param>
|
||||
/// <param name="dest">A buffer to receive the byte sequence. This buffer MUST be at least as large as indicated by <see cref="GetDecodeRequiredLength" />.</param>
|
||||
/// <param name="flags">Optional flags</param>
|
||||
/// <returns>A sub-span over <paramref name="dest"/> containing the encoded string.</returns>
|
||||
template<class TElem, class TTraits>
|
||||
constexpr static Span<std::byte> Decode(std::basic_string_view<TElem, TTraits> src, Span<std::byte> dest,
|
||||
Flags flags = Flags::None) noexcept;
|
||||
|
||||
/// <summary>
|
||||
/// Converts a Base64 text string back into a sequence of bytes.
|
||||
/// </summary>
|
||||
/// <typeparam name="TChar">The kind of text element.</typeparam>
|
||||
/// <param name="src">The source Base64 string to convert.</param>
|
||||
/// <param name="dest">A buffer to receive the byte sequence. This buffer MUST be at least as large as indicated by <see cref="GetDecodeRequiredLength" />.</param>
|
||||
/// <param name="flags">Optional flags</param>
|
||||
/// <returns>A sub-span over <paramref name="dest"/> containing the encoded string.</returns>
|
||||
template<typename TChar>
|
||||
constexpr static Span<std::byte> Decode(ReadOnlySpan<TChar> src, Span<std::byte> dest,
|
||||
Flags flags = Flags::None) noexcept;
|
||||
private:
|
||||
// Helper functions
|
||||
// Overloaded function to get encoding characters
|
||||
// i from 0 to 63 -> base64 encoding character
|
||||
// i = 64 '='
|
||||
// i = 65 '\r'
|
||||
// i = 66 '\n'
|
||||
// i = 67 '\0'
|
||||
template<typename TChar> constexpr static TChar EncodeBase64Char(std::byte nIndex, Flags flags) noexcept;
|
||||
template<typename TChar> constexpr static int DecodeBase64Char(TChar ch, Flags flags) noexcept;
|
||||
};
|
||||
|
||||
template<typename TChar> constexpr TChar Base64::EncodeBase64Char(std::byte nIndex, Flags flags) noexcept
|
||||
{
|
||||
// ReSharper disable once CppStaticAssertFailure
|
||||
static_assert(false, "Unknown TChar");
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<typename TChar> constexpr int Base64::DecodeBase64Char(const TChar ch, Flags flags) noexcept
|
||||
{
|
||||
// ReSharper disable once CppStaticAssertFailure
|
||||
static_assert(false, "Unknown TChar");
|
||||
return 0;
|
||||
}
|
||||
|
||||
constexpr Base64::Flags operator|(Base64::Flags a, Base64::Flags b) noexcept
|
||||
{
|
||||
return static_cast<Base64::Flags>(static_cast<int>(a) | static_cast<int>(b));
|
||||
}
|
||||
|
||||
constexpr Base64::Flags operator&(Base64::Flags a, Base64::Flags b) noexcept
|
||||
{
|
||||
return static_cast<Base64::Flags>(static_cast<int>(a) & static_cast<int>(b));
|
||||
}
|
||||
|
||||
template<typename TChar>
|
||||
constexpr Span<TChar> Base64::Encode(ReadOnlySpan<std::byte> src, Span<TChar> dest, Flags flags) noexcept
|
||||
{
|
||||
Contract::Requires(dest.Length() >= GetEncodeRequiredLength(src.Length(), flags));
|
||||
|
||||
const std::byte* pSrc = &src[0];
|
||||
int srcLen = src.Length();
|
||||
TChar* pDest = &dest[0];
|
||||
|
||||
int written(0);
|
||||
int len1((srcLen / 3) * 4);
|
||||
int len2(len1 / 76);
|
||||
int len3(19);
|
||||
|
||||
for (int i = 0; i <= len2; i++)
|
||||
{
|
||||
if (i == len2)
|
||||
{
|
||||
len3 = (len1 % 76) / 4;
|
||||
}
|
||||
|
||||
for (int j = 0; j < len3; j++)
|
||||
{
|
||||
uint32_t current(0);
|
||||
for (int n = 0; n < 3; n++)
|
||||
{
|
||||
current |= static_cast<uint32_t>(*pSrc++);
|
||||
current <<= 8;
|
||||
}
|
||||
for (int k = 0; k < 4; k++)
|
||||
{
|
||||
std::byte b = static_cast<std::byte>(current >> 26);
|
||||
*pDest = EncodeBase64Char<TChar>(b, flags);
|
||||
++pDest;
|
||||
current <<= 6;
|
||||
}
|
||||
}
|
||||
written += len3 * 4;
|
||||
|
||||
if ((flags & Flags::NoLinefeed) == Flags::None)
|
||||
{
|
||||
// Insert \r\n here
|
||||
*pDest = EncodeBase64Char<TChar>(byte{65}, Flags::None);
|
||||
++pDest;
|
||||
*pDest = EncodeBase64Char<TChar>(byte{66}, Flags::None);
|
||||
++pDest;
|
||||
written += 2;
|
||||
}
|
||||
}
|
||||
|
||||
if ((written != 0) && ((flags & Flags::NoLinefeed) == Flags::None))
|
||||
{
|
||||
pDest -= 2;
|
||||
written -= 2;
|
||||
}
|
||||
|
||||
len2 = (srcLen % 3) ? (srcLen % 3 + 1) : 0;
|
||||
if (len2)
|
||||
{
|
||||
uint32_t current(0);
|
||||
for (int n = 0; n < 3; n++)
|
||||
{
|
||||
if (n < (srcLen % 3))
|
||||
{
|
||||
current |= static_cast<uint32_t>(*pSrc++);
|
||||
}
|
||||
current <<= 8;
|
||||
}
|
||||
for (int k = 0; k < len2; k++)
|
||||
{
|
||||
std::byte b = static_cast<std::byte>(current >> 26);
|
||||
*pDest = EncodeBase64Char<TChar>(b, flags);
|
||||
++pDest;
|
||||
current <<= 6;
|
||||
}
|
||||
written += len2;
|
||||
if ((flags & Flags::NoPad) == Flags::None)
|
||||
{
|
||||
len3 = len2 ? 4 - len2 : 0;
|
||||
for (int j = 0; j < len3; j++)
|
||||
{
|
||||
// Insert '=' here
|
||||
*pDest = EncodeBase64Char<TChar>(byte{64}, flags);
|
||||
++pDest;
|
||||
}
|
||||
written += len3;
|
||||
}
|
||||
}
|
||||
|
||||
return dest.Slice(0, written);
|
||||
}
|
||||
|
||||
template<class TElem, class TTraits>
|
||||
constexpr Span<std::byte> Base64::Decode(std::basic_string_view<TElem, TTraits> src, Span<std::byte> dest,
|
||||
Flags flags) noexcept
|
||||
{
|
||||
Contract::Requires(static_cast<uint64_t>(src.size()) <= UINT32_MAX);
|
||||
return Base64::Decode(ReadOnlySpan<TElem>{src.data(), static_cast<uint32_t>(src.size())}, dest, flags);
|
||||
}
|
||||
|
||||
template<typename TChar>
|
||||
constexpr Span<std::byte> Base64::Decode(ReadOnlySpan<TChar> src, Span<std::byte> dest, Flags flags) noexcept
|
||||
{
|
||||
const TChar* pSrc = &src[0];
|
||||
std::byte* pDest = &dest[0];
|
||||
|
||||
// Walk the source buffer, each four character sequence is converted to 3 bytes.
|
||||
// CRLFs and =, and any characters not in the encoding table are skipped.
|
||||
const TChar* pEnd = pSrc + src.Length();
|
||||
uint32_t written = 0;
|
||||
while (pSrc < pEnd && (*pSrc) != 0)
|
||||
{
|
||||
uint32_t current = 0;
|
||||
uint32_t bits = 0;
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
if (pSrc >= pEnd)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
int ch = DecodeBase64Char<TChar>(*pSrc, flags);
|
||||
++pSrc;
|
||||
if (ch == -1)
|
||||
{
|
||||
// skip this char
|
||||
// TODO: Change signature of method to support failure.
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
current <<= 6;
|
||||
current |= ch;
|
||||
bits += 6;
|
||||
}
|
||||
|
||||
Contract::Invariant(written + (bits / 8) <= dest.Length());
|
||||
|
||||
// current has the 3 bytes to write to the output buffer, left to right
|
||||
current <<= 24 - bits;
|
||||
for (uint32_t i = 0; i < bits / 8; i++)
|
||||
{
|
||||
*pDest = static_cast<std::byte>((current & 0x00ff0000) >> 16);
|
||||
pDest++;
|
||||
current <<= 8;
|
||||
written++;
|
||||
}
|
||||
}
|
||||
|
||||
return dest.Slice(0, written);
|
||||
}
|
||||
|
||||
constexpr uint32_t Base64::GetEncodeRequiredLength(const uint32_t srcLen, const Flags flags) noexcept
|
||||
{
|
||||
uint32_t nSrcLen4 = srcLen * 4;
|
||||
uint32_t retval = nSrcLen4 / 3;
|
||||
if ((flags & Flags::NoPad) == Flags::None)
|
||||
{
|
||||
retval += srcLen % 3;
|
||||
}
|
||||
|
||||
uint32_t numLinefeed = retval / 76 + 1;
|
||||
uint32_t onLastLine = retval % 76;
|
||||
|
||||
if (onLastLine && onLastLine % 4)
|
||||
{
|
||||
retval += 4 - (onLastLine % 4);
|
||||
}
|
||||
|
||||
numLinefeed *= 2;
|
||||
if ((flags & Flags::NoLinefeed) == Flags::None)
|
||||
{
|
||||
retval += numLinefeed;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
template<class TElem, class TTraits>
|
||||
constexpr uint32_t Base64::GetDecodeRequiredLength(std::basic_string_view<TElem, TTraits> src) noexcept
|
||||
{
|
||||
Contract::Requires(static_cast<uint64_t>(src.size()) <= UINT32_MAX);
|
||||
return static_cast<uint32_t>(src.size());
|
||||
}
|
||||
|
||||
constexpr uint32_t Base64::GetDecodeRequiredLength(const uint32_t srcLen) noexcept
|
||||
{
|
||||
return srcLen;
|
||||
}
|
||||
|
||||
constexpr char Base64CharEncodingTable[68] = {
|
||||
'A',
|
||||
'B',
|
||||
'C',
|
||||
'D',
|
||||
'E',
|
||||
'F',
|
||||
'G',
|
||||
'H',
|
||||
'I',
|
||||
'J',
|
||||
'K',
|
||||
'L',
|
||||
'M',
|
||||
'N',
|
||||
'O',
|
||||
'P',
|
||||
'Q',
|
||||
'R',
|
||||
'S',
|
||||
'T',
|
||||
'U',
|
||||
'V',
|
||||
'W',
|
||||
'X',
|
||||
'Y',
|
||||
'Z',
|
||||
'a',
|
||||
'b',
|
||||
'c',
|
||||
'd',
|
||||
'e',
|
||||
'f',
|
||||
'g',
|
||||
'h',
|
||||
'i',
|
||||
'j',
|
||||
'k',
|
||||
'l',
|
||||
'm',
|
||||
'n',
|
||||
'o',
|
||||
'p',
|
||||
'q',
|
||||
'r',
|
||||
's',
|
||||
't',
|
||||
'u',
|
||||
'v',
|
||||
'w',
|
||||
'x',
|
||||
'y',
|
||||
'z',
|
||||
'0',
|
||||
'1',
|
||||
'2',
|
||||
'3',
|
||||
'4',
|
||||
'5',
|
||||
'6',
|
||||
'7',
|
||||
'8',
|
||||
'9',
|
||||
'+',
|
||||
'/',
|
||||
'=',
|
||||
'\r',
|
||||
'\n',
|
||||
'\0'
|
||||
};
|
||||
|
||||
template<>
|
||||
constexpr char Base64::EncodeBase64Char<char>(std::byte index, const Flags flags) noexcept
|
||||
{
|
||||
char retval = Base64CharEncodingTable[static_cast<int>(index)];
|
||||
if ((flags & Flags::Url) != Flags::None)
|
||||
{
|
||||
if (retval == '+')
|
||||
{
|
||||
retval = '-';
|
||||
}
|
||||
if (retval == '/')
|
||||
{
|
||||
retval = '_';
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
constexpr wchar_t Base64WideEncodingTable[68] = {
|
||||
L'A',
|
||||
L'B',
|
||||
L'C',
|
||||
L'D',
|
||||
L'E',
|
||||
L'F',
|
||||
L'G',
|
||||
L'H',
|
||||
L'I',
|
||||
L'J',
|
||||
L'K',
|
||||
L'L',
|
||||
L'M',
|
||||
L'N',
|
||||
L'O',
|
||||
L'P',
|
||||
L'Q',
|
||||
L'R',
|
||||
L'S',
|
||||
L'T',
|
||||
L'U',
|
||||
L'V',
|
||||
L'W',
|
||||
L'X',
|
||||
L'Y',
|
||||
L'Z',
|
||||
L'a',
|
||||
L'b',
|
||||
L'c',
|
||||
L'd',
|
||||
L'e',
|
||||
L'f',
|
||||
L'g',
|
||||
L'h',
|
||||
L'i',
|
||||
L'j',
|
||||
L'k',
|
||||
L'l',
|
||||
L'm',
|
||||
L'n',
|
||||
L'o',
|
||||
L'p',
|
||||
L'q',
|
||||
L'r',
|
||||
L's',
|
||||
L't',
|
||||
L'u',
|
||||
L'v',
|
||||
L'w',
|
||||
L'x',
|
||||
L'y',
|
||||
L'z',
|
||||
L'0',
|
||||
L'1',
|
||||
L'2',
|
||||
L'3',
|
||||
L'4',
|
||||
L'5',
|
||||
L'6',
|
||||
L'7',
|
||||
L'8',
|
||||
L'9',
|
||||
L'+',
|
||||
L'/',
|
||||
L'=',
|
||||
L'\r',
|
||||
L'\n',
|
||||
L'\0'
|
||||
};
|
||||
|
||||
template<>
|
||||
constexpr wchar_t Base64::EncodeBase64Char<wchar_t>(std::byte index, const Flags flags) noexcept
|
||||
{
|
||||
wchar_t retval = Base64WideEncodingTable[static_cast<int>(index)];
|
||||
|
||||
if ((flags & Flags::Url) != Flags::None)
|
||||
{
|
||||
if (retval == L'+')
|
||||
{
|
||||
retval = L'-';
|
||||
}
|
||||
if (retval == L'/')
|
||||
{
|
||||
retval = L'_';
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
template<>
|
||||
constexpr int Base64::DecodeBase64Char<char>(const char ch, const Flags flags) noexcept
|
||||
{
|
||||
// returns -1 if the character is invalid
|
||||
// or should be skipped
|
||||
// otherwise, returns the 6-bit code for the character
|
||||
// from the encoding table
|
||||
if (ch >= 'A' && ch <= 'Z')
|
||||
{
|
||||
return ch - 'A' + 0; // 0 range starts at 'A'
|
||||
}
|
||||
if (ch >= 'a' && ch <= 'z')
|
||||
{
|
||||
return ch - 'a' + 26; // 26 range starts at 'a'
|
||||
}
|
||||
if (ch >= '0' && ch <= '9')
|
||||
{
|
||||
return ch - '0' + 52; // 52 range starts at '0'
|
||||
}
|
||||
|
||||
if ((flags & Flags::Url) != Flags::None)
|
||||
{
|
||||
if (ch == '-')
|
||||
{
|
||||
return 62; // base64url '+' -> '-'
|
||||
}
|
||||
if (ch == '_')
|
||||
{
|
||||
return 63; // base64url '/' -> '_'
|
||||
}
|
||||
}
|
||||
|
||||
if (ch == '+')
|
||||
{
|
||||
return 62; // base64 '+' is 62
|
||||
}
|
||||
if (ch == '/')
|
||||
{
|
||||
return 63; // base64 '/' is 63
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
template<>
|
||||
constexpr int Base64::DecodeBase64Char<wchar_t>(const wchar_t ch, const Flags flags) noexcept
|
||||
{
|
||||
// returns -1 if the character is invalid
|
||||
// or should be skipped
|
||||
// otherwise, returns the 6-bit code for the character
|
||||
// from the encoding table
|
||||
if (ch >= L'A' && ch <= L'Z')
|
||||
{
|
||||
return ch - L'A' + 0; // 0 range starts at 'A'
|
||||
}
|
||||
if (ch >= L'a' && ch <= L'z')
|
||||
{
|
||||
return ch - L'a' + 26; // 26 range starts at 'a'
|
||||
}
|
||||
if (ch >= L'0' && ch <= L'9')
|
||||
{
|
||||
return ch - L'0' + 52; // 52 range starts at '0'
|
||||
}
|
||||
|
||||
if ((flags & Flags::Url) != Flags::None)
|
||||
{
|
||||
if (ch == L'-')
|
||||
{
|
||||
return 62; // base64url '+' -> '-'
|
||||
}
|
||||
if (ch == L'_')
|
||||
{
|
||||
return 63; // base64url '/' -> '_'
|
||||
}
|
||||
}
|
||||
|
||||
if (ch == L'+')
|
||||
{
|
||||
return 62; // base64 '+' is 62
|
||||
}
|
||||
if (ch == L'/')
|
||||
{
|
||||
return 63; // base64 '/' is 63
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
19
src/Core/Core.Native/Blittable.h
Normal file
19
src/Core/Core.Native/Blittable.h
Normal file
@@ -0,0 +1,19 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
namespace cdb_core
|
||||
{
|
||||
template<typename C>
|
||||
struct is_blittable
|
||||
{
|
||||
constexpr static bool value =
|
||||
std::is_nothrow_default_constructible_v<C> &&
|
||||
std::is_nothrow_copy_constructible_v<C> &&
|
||||
std::is_nothrow_copy_assignable_v<C>;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
inline constexpr bool is_blittable_v = is_blittable<T>::value;
|
||||
}
|
||||
26
src/Core/Core.Native/Contract.cpp
Normal file
26
src/Core/Core.Native/Contract.cpp
Normal file
@@ -0,0 +1,26 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#include "pch.h"
|
||||
#include "Contract.h"
|
||||
#include "Strings.h"
|
||||
|
||||
namespace cdb_core
|
||||
{
|
||||
std::wstring Contract::MakeError(std::string_view api, std::string_view message)
|
||||
{
|
||||
if (message.empty())
|
||||
{
|
||||
return make_string(L"%.*S", api.size(), api.data());
|
||||
}
|
||||
return make_string(L"%.*S Failure: %.*S", api.size(), api.data(), message.size(), message.data());
|
||||
}
|
||||
|
||||
[[noreturn]] void Contract::Fail(std::string_view api, std::string_view message)
|
||||
{
|
||||
std::wstring error = MakeError(api, message);
|
||||
_ASSERT_EXPR(false, error.c_str());
|
||||
std::terminate();
|
||||
}
|
||||
}
|
||||
92
src/Core/Core.Native/Contract.h
Normal file
92
src/Core/Core.Native/Contract.h
Normal file
@@ -0,0 +1,92 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
// Forward Declarations
|
||||
namespace cdb_core_test
|
||||
{
|
||||
class ContractUnitTests;
|
||||
}
|
||||
|
||||
namespace cdb_core
|
||||
{
|
||||
class Contract final
|
||||
{
|
||||
public:
|
||||
Contract() = delete;
|
||||
~Contract() = delete;
|
||||
Contract(const Contract& other) = delete;
|
||||
Contract(Contract&& other) noexcept = delete;
|
||||
Contract& operator=(const Contract& other) = delete;
|
||||
Contract& operator=(Contract&& other) noexcept = delete;
|
||||
|
||||
constexpr static void Assert([[maybe_unused]] bool condition)
|
||||
{
|
||||
#if _DEBUG
|
||||
if (!condition)
|
||||
{
|
||||
Fail("Assert", std::string_view());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
constexpr static void Assert([[maybe_unused]] bool condition, std::string_view message)
|
||||
{
|
||||
#if _DEBUG
|
||||
if (!condition)
|
||||
{
|
||||
Fail("Assert", message);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
constexpr static void Requires(bool condition)
|
||||
{
|
||||
if (!condition)
|
||||
{
|
||||
Fail("Requires", std::string_view());
|
||||
}
|
||||
}
|
||||
|
||||
constexpr static void Requires(bool condition, std::string_view message)
|
||||
{
|
||||
if (!condition)
|
||||
{
|
||||
Fail("Requires", message);
|
||||
}
|
||||
}
|
||||
|
||||
constexpr static void Invariant(bool condition)
|
||||
{
|
||||
if (!condition)
|
||||
{
|
||||
Fail("Invariant", std::string_view());
|
||||
}
|
||||
}
|
||||
|
||||
constexpr static void Invariant(bool condition, std::string_view message)
|
||||
{
|
||||
if (!condition)
|
||||
{
|
||||
Fail("Invariant", message);
|
||||
}
|
||||
}
|
||||
|
||||
[[noreturn]] static void Fail()
|
||||
{
|
||||
Fail("Fail", std::string_view());
|
||||
}
|
||||
|
||||
[[noreturn]] static void Fail(std::string_view message)
|
||||
{
|
||||
Fail("Fail", message);
|
||||
}
|
||||
|
||||
private:
|
||||
friend class cdb_core_test::ContractUnitTests;
|
||||
[[noreturn]] static void Fail(std::string_view api, std::string_view message);
|
||||
[[nodiscard]] static std::wstring MakeError(std::string_view api, std::string_view message);
|
||||
};
|
||||
}
|
||||
33
src/Core/Core.Native/Core.Native.h
Normal file
33
src/Core/Core.Native/Core.Native.h
Normal file
@@ -0,0 +1,33 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "framework.h"
|
||||
|
||||
#include "Contract.h"
|
||||
#include "HashCode.h"
|
||||
#include "tla.h"
|
||||
#include "Endian.h"
|
||||
#include "Blittable.h"
|
||||
#include "IsAllSame.h"
|
||||
#include "Span.h"
|
||||
#include "ReadOnlySpan.h"
|
||||
#include "MemoryMarshal.h"
|
||||
#include "Memory.h"
|
||||
#include "ReadOnlyMemory.h"
|
||||
#include "Strings.h"
|
||||
#include "make_unique.h"
|
||||
#include "Stringable.h"
|
||||
#include "EqualityComparable.h"
|
||||
#include "DeepCompare.h"
|
||||
#include "Hashable.h"
|
||||
#include "Utf8Span.h"
|
||||
#include "ref_ptr.h"
|
||||
#include "Failure.h"
|
||||
#include "Result.h"
|
||||
#include "TimeSpan.h"
|
||||
#include "Base64.h"
|
||||
#include "Stopwatch.h"
|
||||
#include "Crc32.h"
|
||||
2159
src/Core/Core.Native/Crc32.cpp
Normal file
2159
src/Core/Core.Native/Crc32.cpp
Normal file
File diff suppressed because it is too large
Load Diff
37
src/Core/Core.Native/Crc32.h
Normal file
37
src/Core/Core.Native/Crc32.h
Normal file
@@ -0,0 +1,37 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace cdb_core
|
||||
{
|
||||
template<typename T> struct ReadOnlySpan;
|
||||
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
//
|
||||
// File implements Slicing-by-8 CRC Generation, as described in
|
||||
// "Novel Table Lookup-Based Algorithms for High-Performance CRC Generation"
|
||||
// IEEE TRANSACTIONS ON COMPUTERS, VOL. 57, NO. 11, NOVEMBER 2008
|
||||
//
|
||||
// Copyright(c) 2004-2006 Intel Corporation - All Rights Reserved
|
||||
//
|
||||
// This software program is licensed subject to the BSD License,
|
||||
// available at http://www.opensource.org/licenses/bsd-license.html.
|
||||
|
||||
/// <summary>CRC Generator.</summary>
|
||||
struct Crc32 final
|
||||
{
|
||||
// Static Class
|
||||
[[nodiscard]] Crc32() = delete;
|
||||
~Crc32() = delete;
|
||||
Crc32(const Crc32& other) = delete;
|
||||
Crc32(Crc32&& other) noexcept = delete;
|
||||
Crc32& operator=(const Crc32& other) = delete;
|
||||
Crc32& operator=(Crc32&& other) noexcept = delete;
|
||||
|
||||
[[nodiscard]] static uint32_t Update(uint32_t crc32, ReadOnlySpan<byte> span) noexcept;
|
||||
};
|
||||
}
|
||||
61
src/Core/Core.Native/DeepCompare.h
Normal file
61
src/Core/Core.Native/DeepCompare.h
Normal file
@@ -0,0 +1,61 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
namespace cdb_core
|
||||
{
|
||||
template<typename T>
|
||||
struct DeepComparer final
|
||||
{
|
||||
bool operator()(const T& x, const T& y) const noexcept { return x == y; }
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
static bool DeepCompare(const T& x, const T& y) noexcept
|
||||
{
|
||||
return DeepComparer<T>{}(x, y);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
struct DeepComparer<std::vector<T>> final
|
||||
{
|
||||
bool operator()(const std::vector<T>& x, const std::vector<T>& y) const noexcept
|
||||
{
|
||||
if (x.size() != y.size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < x.size(); i++)
|
||||
{
|
||||
if (!DeepCompare(x[i], y[i]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct DeepComparer<std::unique_ptr<T>> final
|
||||
{
|
||||
bool operator()(const std::unique_ptr<T>& x, const std::unique_ptr<T>& y) const noexcept
|
||||
{
|
||||
if ((x == nullptr) && (y == nullptr))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if ((x == nullptr) || (y == nullptr))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return DeepCompare(*x, *y);
|
||||
}
|
||||
};
|
||||
}
|
||||
33
src/Core/Core.Native/Endian.h
Normal file
33
src/Core/Core.Native/Endian.h
Normal file
@@ -0,0 +1,33 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace cdb_core
|
||||
{
|
||||
struct Endian final
|
||||
{
|
||||
// Static Class
|
||||
Endian() = delete;
|
||||
~Endian() = delete;
|
||||
Endian(const Endian& other) = delete;
|
||||
Endian(Endian&& other) noexcept = delete;
|
||||
Endian& operator=(const Endian& other) = delete;
|
||||
Endian& operator=(Endian&& other) noexcept = delete;
|
||||
|
||||
constexpr static bool IsLittleEndian() noexcept;
|
||||
constexpr static bool IsBigEndian() noexcept;
|
||||
};
|
||||
|
||||
constexpr bool Endian::IsLittleEndian() noexcept
|
||||
{
|
||||
// TODO: this should use std::endian when we move to C++ v20.
|
||||
return true;
|
||||
}
|
||||
|
||||
constexpr bool Endian::IsBigEndian() noexcept
|
||||
{
|
||||
return !Endian::IsLittleEndian();
|
||||
}
|
||||
}
|
||||
23
src/Core/Core.Native/EqualityComparable.h
Normal file
23
src/Core/Core.Native/EqualityComparable.h
Normal file
@@ -0,0 +1,23 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
#include <string>
|
||||
|
||||
namespace cdb_core
|
||||
{
|
||||
template<typename T, typename = void>
|
||||
struct is_equality_comparable : std::false_type { };
|
||||
|
||||
/// <summary>
|
||||
/// Check for operator==.
|
||||
/// </summary>
|
||||
// ReSharper disable once CppIdenticalOperandsInBinaryExpression
|
||||
template<typename T>
|
||||
struct is_equality_comparable<T, typename std::enable_if<true, decltype(std::declval<T&>() == std::declval<T&>(), (
|
||||
void)0)>::type> : std::true_type { };
|
||||
|
||||
template<typename T>
|
||||
inline constexpr bool is_equality_comparable_v = is_equality_comparable<T>::value;
|
||||
}
|
||||
24
src/Core/Core.Native/Failure.cpp
Normal file
24
src/Core/Core.Native/Failure.cpp
Normal file
@@ -0,0 +1,24 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#include "pch.h"
|
||||
#include "Failure.h"
|
||||
|
||||
namespace cdb_core
|
||||
{
|
||||
const Failure Failure::None = Failure();
|
||||
|
||||
Failure& Failure::operator=(const Failure& other)
|
||||
{
|
||||
if (this == &other)
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
m_failureCode = other.m_failureCode;
|
||||
m_hResult = other.m_hResult;
|
||||
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
72
src/Core/Core.Native/Failure.h
Normal file
72
src/Core/Core.Native/Failure.h
Normal file
@@ -0,0 +1,72 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
#include "Contract.h"
|
||||
|
||||
namespace cdb_core
|
||||
{
|
||||
/// <summary>
|
||||
/// Failure is a wrapper around Failed states in the system.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Encapsulates a Failure Code specific to an application
|
||||
/// and an associated Win32 HResult if the failure originated
|
||||
/// from a windows API call.
|
||||
/// </remarks>
|
||||
class [[nodiscard]] Failure
|
||||
{
|
||||
public:
|
||||
static const Failure None;
|
||||
constexpr static uint32_t NoneCode = 0;
|
||||
|
||||
template<typename TEnum, typename = std::enable_if_t<std::is_enum<TEnum>::value, int>>
|
||||
Failure(const TEnum failureCode) noexcept;
|
||||
template<typename TEnum, typename = std::enable_if_t<std::is_enum<TEnum>::value, int>>
|
||||
Failure(const TEnum failureCode, const HRESULT hResult) noexcept;
|
||||
~Failure() = default;
|
||||
Failure(const Failure&) = default;
|
||||
Failure(Failure&&) = default;
|
||||
|
||||
Failure& operator=(const Failure& other);
|
||||
Failure& operator=(Failure&&) = default;
|
||||
|
||||
[[nodiscard]] bool IsFailed() const noexcept;
|
||||
|
||||
[[nodiscard]] uint32_t GetFailureCode() const noexcept;
|
||||
|
||||
template<typename TEnum, typename = std::enable_if_t<std::is_enum<TEnum>::value, int>>
|
||||
[[nodiscard]] TEnum GetFailureCode() const noexcept;
|
||||
|
||||
[[nodiscard]] HRESULT GetHResult() const noexcept;
|
||||
|
||||
private:
|
||||
|
||||
constexpr explicit Failure() : m_failureCode(NoneCode), m_hResult{0} { }
|
||||
|
||||
uint32_t m_failureCode;
|
||||
HRESULT m_hResult;
|
||||
};
|
||||
|
||||
template<typename TEnum, typename>
|
||||
Failure::Failure(const TEnum failureCode) noexcept : Failure(failureCode, S_OK) { }
|
||||
|
||||
template<typename TEnum, typename>
|
||||
Failure::Failure(const TEnum failureCode, const HRESULT hResult) noexcept :
|
||||
m_failureCode(static_cast<uint32_t>(failureCode)),
|
||||
m_hResult(hResult)
|
||||
{
|
||||
Contract::Requires(m_failureCode != 0,
|
||||
"Failure constructor should not use FailureCode::None. Use Failure::None instead");
|
||||
}
|
||||
|
||||
inline bool Failure::IsFailed() const noexcept { return m_failureCode != NoneCode; }
|
||||
|
||||
inline uint32_t Failure::GetFailureCode() const noexcept { return m_failureCode; }
|
||||
|
||||
template<typename TEnum, typename>
|
||||
TEnum Failure::GetFailureCode() const noexcept { return static_cast<TEnum>(m_failureCode); }
|
||||
|
||||
inline HRESULT Failure::GetHResult() const noexcept { return m_hResult; }
|
||||
}
|
||||
498
src/Core/Core.Native/HashCode.h
Normal file
498
src/Core/Core.Native/HashCode.h
Normal file
@@ -0,0 +1,498 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
// ReSharper disable four CommentTypo
|
||||
/*
|
||||
|
||||
This xxHash64 implementation is based on the Netcore xxHash32 C# port which was in turn
|
||||
based on the xxHash32 code published by Yann Collet:
|
||||
|
||||
https://raw.githubusercontent.com/Cyan4973/xxHash/5c174cfa4e45a42f94082dc0d4539b39696afea1/xxhash.c
|
||||
|
||||
xxHash - Fast Hash algorithm
|
||||
Copyright (C) 2012-2016, Yann Collet
|
||||
|
||||
BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* 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.
|
||||
|
||||
You can contact the author at :
|
||||
- xxHash homepage: http://www.xxhash.com
|
||||
- xxHash source repository : https://github.com/Cyan4973/xxHash
|
||||
|
||||
*/
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace cdb_core
|
||||
{
|
||||
/// <summary>Utility functions for combining hash codes..</summary>
|
||||
struct HashCode final
|
||||
{
|
||||
template<typename T1, typename H1 = std::hash<T1>>
|
||||
constexpr static size_t Combine(const T1& value1)
|
||||
{
|
||||
// Provide a way of diffusing bits from something with a limited
|
||||
// input hash space. For example, many enums only have a few
|
||||
// possible hashes, only using the bottom few bits of the code. Some
|
||||
// collections are built on the assumption that hashes are spread
|
||||
// over a larger space, so diffusing the bits may help the
|
||||
// collection work more efficiently.
|
||||
|
||||
uint64_t hc1 = static_cast<uint64_t>(H1{}.operator()(value1));
|
||||
|
||||
uint64_t hash = MixEmptyState();
|
||||
hash += 8;
|
||||
|
||||
hash = QueueRound(hash, hc1);
|
||||
|
||||
hash = MixFinal(hash);
|
||||
return static_cast<size_t>(hash);
|
||||
}
|
||||
|
||||
template<typename T1, typename T2, typename H1 = std::hash<T1>, typename H2 = std::hash<T2>>
|
||||
constexpr static size_t Combine(const T1& value1, const T2& value2)
|
||||
{
|
||||
uint64_t hc1 = static_cast<uint64_t>(H1{}.operator()(value1));
|
||||
uint64_t hc2 = static_cast<uint64_t>(H2{}.operator()(value2));
|
||||
|
||||
uint64_t hash = MixEmptyState();
|
||||
hash += 16;
|
||||
|
||||
hash = QueueRound(hash, hc1);
|
||||
hash = QueueRound(hash, hc2);
|
||||
|
||||
hash = MixFinal(hash);
|
||||
return static_cast<size_t>(hash);
|
||||
}
|
||||
|
||||
template<
|
||||
typename T1,
|
||||
typename T2,
|
||||
typename T3,
|
||||
typename H1 = std::hash<T1>,
|
||||
typename H2 = std::hash<T2>,
|
||||
typename H3 = std::hash<T3>>
|
||||
constexpr static size_t Combine(const T1& value1, const T2& value2, const T3& value3)
|
||||
{
|
||||
uint64_t hc1 = static_cast<uint64_t>(H1{}.operator()(value1));
|
||||
uint64_t hc2 = static_cast<uint64_t>(H2{}.operator()(value2));
|
||||
uint64_t hc3 = static_cast<uint64_t>(H3{}.operator()(value3));
|
||||
|
||||
uint64_t hash = MixEmptyState();
|
||||
hash += 24;
|
||||
|
||||
hash = QueueRound(hash, hc1);
|
||||
hash = QueueRound(hash, hc2);
|
||||
hash = QueueRound(hash, hc3);
|
||||
|
||||
hash = MixFinal(hash);
|
||||
return static_cast<size_t>(hash);
|
||||
}
|
||||
|
||||
template<
|
||||
typename T1,
|
||||
typename T2,
|
||||
typename T3,
|
||||
typename T4,
|
||||
typename H1 = std::hash<T1>,
|
||||
typename H2 = std::hash<T2>,
|
||||
typename H3 = std::hash<T3>,
|
||||
typename H4 = std::hash<T4>>
|
||||
constexpr static size_t Combine(const T1& value1, const T2& value2, const T3& value3, const T4& value4)
|
||||
{
|
||||
uint64_t hc1 = static_cast<uint64_t>(H1{}.operator()(value1));
|
||||
uint64_t hc2 = static_cast<uint64_t>(H2{}.operator()(value2));
|
||||
uint64_t hc3 = static_cast<uint64_t>(H3{}.operator()(value3));
|
||||
uint64_t hc4 = static_cast<uint64_t>(H4{}.operator()(value4));
|
||||
|
||||
uint64_t v1;
|
||||
uint64_t v2;
|
||||
uint64_t v3;
|
||||
uint64_t v4;
|
||||
Initialize(v1, v2, v3, v4);
|
||||
|
||||
v1 = Round(v1, hc1);
|
||||
v2 = Round(v2, hc2);
|
||||
v3 = Round(v3, hc3);
|
||||
v4 = Round(v4, hc4);
|
||||
|
||||
uint64_t hash = MixState(v1, v2, v3, v4);
|
||||
hash += 32;
|
||||
|
||||
hash = MixFinal(hash);
|
||||
return static_cast<size_t>(hash);
|
||||
}
|
||||
|
||||
template<
|
||||
typename T1,
|
||||
typename T2,
|
||||
typename T3,
|
||||
typename T4,
|
||||
typename T5,
|
||||
typename H1 = std::hash<T1>,
|
||||
typename H2 = std::hash<T2>,
|
||||
typename H3 = std::hash<T3>,
|
||||
typename H4 = std::hash<T4>,
|
||||
typename H5 = std::hash<T5>>
|
||||
constexpr static size_t Combine(const T1& value1, const T2& value2, const T3& value3, const T4& value4,
|
||||
const T5& value5)
|
||||
{
|
||||
uint64_t hc1 = static_cast<uint64_t>(H1{}.operator()(value1));
|
||||
uint64_t hc2 = static_cast<uint64_t>(H2{}.operator()(value2));
|
||||
uint64_t hc3 = static_cast<uint64_t>(H3{}.operator()(value3));
|
||||
uint64_t hc4 = static_cast<uint64_t>(H4{}.operator()(value4));
|
||||
uint64_t hc5 = static_cast<uint64_t>(H5{}.operator()(value5));
|
||||
|
||||
uint64_t v1;
|
||||
uint64_t v2;
|
||||
uint64_t v3;
|
||||
uint64_t v4;
|
||||
Initialize(v1, v2, v3, v4);
|
||||
|
||||
v1 = Round(v1, hc1);
|
||||
v2 = Round(v2, hc2);
|
||||
v3 = Round(v3, hc3);
|
||||
v4 = Round(v4, hc4);
|
||||
|
||||
uint64_t hash = MixState(v1, v2, v3, v4);
|
||||
hash += 40;
|
||||
|
||||
hash = QueueRound(hash, hc5);
|
||||
|
||||
hash = MixFinal(hash);
|
||||
return static_cast<size_t>(hash);
|
||||
}
|
||||
|
||||
template<
|
||||
typename T1,
|
||||
typename T2,
|
||||
typename T3,
|
||||
typename T4,
|
||||
typename T5,
|
||||
typename T6,
|
||||
typename H1 = std::hash<T1>,
|
||||
typename H2 = std::hash<T2>,
|
||||
typename H3 = std::hash<T3>,
|
||||
typename H4 = std::hash<T4>,
|
||||
typename H5 = std::hash<T5>,
|
||||
typename H6 = std::hash<T6>>
|
||||
constexpr static size_t Combine(const T1& value1, const T2& value2, const T3& value3, const T4& value4,
|
||||
const T5& value5, const T6& value6)
|
||||
{
|
||||
uint64_t hc1 = static_cast<uint64_t>(H1{}.operator()(value1));
|
||||
uint64_t hc2 = static_cast<uint64_t>(H2{}.operator()(value2));
|
||||
uint64_t hc3 = static_cast<uint64_t>(H3{}.operator()(value3));
|
||||
uint64_t hc4 = static_cast<uint64_t>(H4{}.operator()(value4));
|
||||
uint64_t hc5 = static_cast<uint64_t>(H5{}.operator()(value5));
|
||||
uint64_t hc6 = static_cast<uint64_t>(H6{}.operator()(value6));
|
||||
|
||||
uint64_t v1;
|
||||
uint64_t v2;
|
||||
uint64_t v3;
|
||||
uint64_t v4;
|
||||
Initialize(v1, v2, v3, v4);
|
||||
|
||||
v1 = Round(v1, hc1);
|
||||
v2 = Round(v2, hc2);
|
||||
v3 = Round(v3, hc3);
|
||||
v4 = Round(v4, hc4);
|
||||
|
||||
uint64_t hash = MixState(v1, v2, v3, v4);
|
||||
hash += 48;
|
||||
|
||||
hash = QueueRound(hash, hc5);
|
||||
hash = QueueRound(hash, hc6);
|
||||
|
||||
hash = MixFinal(hash);
|
||||
return static_cast<size_t>(hash);
|
||||
}
|
||||
|
||||
template<
|
||||
typename T1,
|
||||
typename T2,
|
||||
typename T3,
|
||||
typename T4,
|
||||
typename T5,
|
||||
typename T6,
|
||||
typename T7,
|
||||
typename H1 = std::hash<T1>,
|
||||
typename H2 = std::hash<T2>,
|
||||
typename H3 = std::hash<T3>,
|
||||
typename H4 = std::hash<T4>,
|
||||
typename H5 = std::hash<T5>,
|
||||
typename H6 = std::hash<T6>,
|
||||
typename H7 = std::hash<T7>>
|
||||
constexpr static size_t Combine(const T1& value1, const T2& value2, const T3& value3, const T4& value4,
|
||||
const T5& value5, const T6& value6, const T7& value7)
|
||||
{
|
||||
uint64_t hc1 = static_cast<uint64_t>(H1{}.operator()(value1));
|
||||
uint64_t hc2 = static_cast<uint64_t>(H2{}.operator()(value2));
|
||||
uint64_t hc3 = static_cast<uint64_t>(H3{}.operator()(value3));
|
||||
uint64_t hc4 = static_cast<uint64_t>(H4{}.operator()(value4));
|
||||
uint64_t hc5 = static_cast<uint64_t>(H5{}.operator()(value5));
|
||||
uint64_t hc6 = static_cast<uint64_t>(H6{}.operator()(value6));
|
||||
uint64_t hc7 = static_cast<uint64_t>(H7{}.operator()(value7));
|
||||
|
||||
uint64_t v1;
|
||||
uint64_t v2;
|
||||
uint64_t v3;
|
||||
uint64_t v4;
|
||||
Initialize(v1, v2, v3, v4);
|
||||
|
||||
v1 = Round(v1, hc1);
|
||||
v2 = Round(v2, hc2);
|
||||
v3 = Round(v3, hc3);
|
||||
v4 = Round(v4, hc4);
|
||||
|
||||
uint64_t hash = MixState(v1, v2, v3, v4);
|
||||
hash += 56;
|
||||
|
||||
hash = QueueRound(hash, hc5);
|
||||
hash = QueueRound(hash, hc6);
|
||||
hash = QueueRound(hash, hc7);
|
||||
|
||||
hash = MixFinal(hash);
|
||||
return static_cast<size_t>(hash);
|
||||
}
|
||||
|
||||
template<
|
||||
typename T1,
|
||||
typename T2,
|
||||
typename T3,
|
||||
typename T4,
|
||||
typename T5,
|
||||
typename T6,
|
||||
typename T7,
|
||||
typename T8,
|
||||
typename H1 = std::hash<T1>,
|
||||
typename H2 = std::hash<T2>,
|
||||
typename H3 = std::hash<T3>,
|
||||
typename H4 = std::hash<T4>,
|
||||
typename H5 = std::hash<T5>,
|
||||
typename H6 = std::hash<T6>,
|
||||
typename H7 = std::hash<T7>,
|
||||
typename H8 = std::hash<T8>>
|
||||
constexpr static size_t Combine(const T1& value1, const T2& value2, const T3& value3, const T4& value4,
|
||||
const T5& value5, const T6& value6, const T7& value7, const T8& value8)
|
||||
{
|
||||
uint64_t hc1 = static_cast<uint64_t>(H1{}.operator()(value1));
|
||||
uint64_t hc2 = static_cast<uint64_t>(H2{}.operator()(value2));
|
||||
uint64_t hc3 = static_cast<uint64_t>(H3{}.operator()(value3));
|
||||
uint64_t hc4 = static_cast<uint64_t>(H4{}.operator()(value4));
|
||||
uint64_t hc5 = static_cast<uint64_t>(H5{}.operator()(value5));
|
||||
uint64_t hc6 = static_cast<uint64_t>(H6{}.operator()(value6));
|
||||
uint64_t hc7 = static_cast<uint64_t>(H7{}.operator()(value7));
|
||||
uint64_t hc8 = static_cast<uint64_t>(H8{}.operator()(value8));
|
||||
|
||||
uint64_t v1;
|
||||
uint64_t v2;
|
||||
uint64_t v3;
|
||||
uint64_t v4;
|
||||
Initialize(v1, v2, v3, v4);
|
||||
|
||||
v1 = Round(v1, hc1);
|
||||
v2 = Round(v2, hc2);
|
||||
v3 = Round(v3, hc3);
|
||||
v4 = Round(v4, hc4);
|
||||
|
||||
v1 = Round(v1, hc5);
|
||||
v2 = Round(v2, hc6);
|
||||
v3 = Round(v3, hc7);
|
||||
v4 = Round(v4, hc8);
|
||||
|
||||
uint64_t hash = MixState(v1, v2, v3, v4);
|
||||
hash += 64;
|
||||
|
||||
hash = MixFinal(hash);
|
||||
return static_cast<size_t>(hash);
|
||||
}
|
||||
|
||||
template<typename T1, typename H1 = std::hash<T1>>
|
||||
constexpr void Add(const T1& value) noexcept
|
||||
{
|
||||
AddHash(static_cast<uint64_t>(H1{}.operator()(value)));
|
||||
}
|
||||
|
||||
constexpr void AddHash(uint64_t value) noexcept
|
||||
{
|
||||
// The original xxHash works as follows:
|
||||
// 0. Initialize immediately. We can't do this in a struct (no
|
||||
// default ctor).
|
||||
// 1. Accumulate blocks of length 32 (4 uints) into 4 accumulators.
|
||||
// 2. Accumulate remaining blocks of length 4 (1 uint64_t) into the
|
||||
// hash.
|
||||
// 3. Accumulate remaining blocks of length 1 into the hash.
|
||||
|
||||
// There is no need for #3 as this type only accepts int64s. _queue1,
|
||||
// _queue2 and _queue3 are basically a buffer so that when
|
||||
// ToHashCode is called we can execute #2 correctly.
|
||||
|
||||
// We need to initialize the xxHash64 state (_v1 to _v4) lazily (see
|
||||
// #0) and the last place that can be done if you look at the
|
||||
// original code is just before the first block of 32 bytes is mixed
|
||||
// in. The xxHash64 state is never used for streams containing fewer
|
||||
// than 64 bytes.
|
||||
|
||||
// To see what's really going on here, have a look at the Combine
|
||||
// methods.
|
||||
|
||||
// Storing the value of _length locally shaves of quite a few bytes
|
||||
// in the resulting machine code.
|
||||
uint64_t previousLength = m_length++;
|
||||
uint64_t position = previousLength % 4;
|
||||
|
||||
// Switch can't be inlined.
|
||||
|
||||
if (position == 0)
|
||||
{
|
||||
m_queue1 = value;
|
||||
}
|
||||
else if (position == 1)
|
||||
{
|
||||
m_queue2 = value;
|
||||
}
|
||||
else if (position == 2)
|
||||
{
|
||||
m_queue3 = value;
|
||||
}
|
||||
else // position == 3
|
||||
{
|
||||
if (previousLength == 3)
|
||||
{
|
||||
Initialize(m_v1, m_v2, m_v3, m_v4);
|
||||
}
|
||||
|
||||
m_v1 = Round(m_v1, m_queue1);
|
||||
m_v2 = Round(m_v2, m_queue2);
|
||||
m_v3 = Round(m_v3, m_queue3);
|
||||
m_v4 = Round(m_v4, value);
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr size_t ToHashCode() const noexcept
|
||||
{
|
||||
// Storing the value of _length locally shaves of quite a few bytes
|
||||
// in the resulting machine code.
|
||||
uint64_t length = m_length;
|
||||
|
||||
// position refers to the *next* queue position in this method, so
|
||||
// position == 1 means that _queue1 is populated; _queue2 would have
|
||||
// been populated on the next call to Add.
|
||||
uint64_t position = length % 4;
|
||||
|
||||
// If the length is less than 4, _v1 to _v4 don't contain anything
|
||||
// yet. xxHash64 treats this differently.
|
||||
|
||||
uint64_t hash = length < 4 ? MixEmptyState() : MixState(m_v1, m_v2, m_v3, m_v4);
|
||||
|
||||
// _length is incremented once per Add(uint64_t) and is therefore 8
|
||||
// times too small (xxHash length is in bytes, not int64s).
|
||||
|
||||
hash += length * 8;
|
||||
|
||||
// Mix what remains in the queue
|
||||
|
||||
// Switch can't be inlined right now, so use as few branches as
|
||||
// possible by manually excluding impossible scenarios (position > 1
|
||||
// is always false if position is not > 0).
|
||||
if (position > 0)
|
||||
{
|
||||
hash = QueueRound(hash, m_queue1);
|
||||
if (position > 1)
|
||||
{
|
||||
hash = QueueRound(hash, m_queue2);
|
||||
if (position > 2)
|
||||
{
|
||||
hash = QueueRound(hash, m_queue3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hash = MixFinal(hash);
|
||||
return static_cast<size_t>(hash);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
constexpr static void Initialize(uint64_t& v1, uint64_t& v2, uint64_t& v3, uint64_t& v4)
|
||||
{
|
||||
v1 = s_seed + Prime1 + Prime2;
|
||||
v2 = s_seed + Prime2;
|
||||
v3 = s_seed;
|
||||
v4 = s_seed - Prime1;
|
||||
}
|
||||
|
||||
constexpr static uint64_t RotateLeft(uint64_t x, int r)
|
||||
{
|
||||
return ((x << r) | (x >> (64 - r)));
|
||||
}
|
||||
|
||||
constexpr static uint64_t Round(uint64_t hash, uint64_t input)
|
||||
{
|
||||
return RotateLeft(hash + input * Prime2, 31) * Prime1;
|
||||
}
|
||||
|
||||
constexpr static uint64_t QueueRound(uint64_t hash, uint64_t queuedValue)
|
||||
{
|
||||
return RotateLeft(hash ^ Round(0, queuedValue), 27) * Prime1 + Prime4;
|
||||
}
|
||||
|
||||
constexpr static uint64_t MixState(uint64_t v1, uint64_t v2, uint64_t v3, uint64_t v4)
|
||||
{
|
||||
return RotateLeft(v1, 1) + RotateLeft(v2, 7) + RotateLeft(v3, 12) + RotateLeft(v4, 18);
|
||||
}
|
||||
|
||||
constexpr static uint64_t MixEmptyState()
|
||||
{
|
||||
return s_seed + Prime5;
|
||||
}
|
||||
|
||||
constexpr static uint64_t MixFinal(uint64_t hash)
|
||||
{
|
||||
hash ^= hash >> 33;
|
||||
hash *= Prime2;
|
||||
hash ^= hash >> 29;
|
||||
hash *= Prime3;
|
||||
hash ^= hash >> 32;
|
||||
return hash;
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr uint64_t s_seed = 0x9FB21C651E98DF25ULL;
|
||||
|
||||
static constexpr uint64_t Prime1 = 11400714785074694791ULL;
|
||||
static constexpr uint64_t Prime2 = 14029467366897019727ULL;
|
||||
static constexpr uint64_t Prime3 = 1609587929392839161ULL;
|
||||
static constexpr uint64_t Prime4 = 9650029242287828579ULL;
|
||||
static constexpr uint64_t Prime5 = 2870177450012600261ULL;
|
||||
|
||||
uint64_t m_v1{0}, m_v2{0}, m_v3{0}, m_v4{0};
|
||||
uint64_t m_queue1{0}, m_queue2{0}, m_queue3{0};
|
||||
uint64_t m_length{0};
|
||||
};
|
||||
}
|
||||
33
src/Core/Core.Native/Hashable.h
Normal file
33
src/Core/Core.Native/Hashable.h
Normal file
@@ -0,0 +1,33 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace cdb_core
|
||||
{
|
||||
/// <summary>Check for a GetHashCode method.</summary>
|
||||
/// <remarks>Method must have this signature:
|
||||
/// <code>size_t GetHashCode() const noexcept</code>
|
||||
/// </remarks>
|
||||
template<typename C>
|
||||
struct is_hashable
|
||||
{
|
||||
private:
|
||||
template<typename T>
|
||||
constexpr static typename std::is_same<decltype(&T::GetHashCode), size_t (T::*)() const noexcept>::type Check(T*)
|
||||
{
|
||||
return std::true_type{};
|
||||
}
|
||||
|
||||
template<typename>
|
||||
constexpr static std::false_type Check(...) { return std::false_type{}; }
|
||||
|
||||
public:
|
||||
constexpr static bool value = decltype(
|
||||
Check<std::remove_const_t<std::remove_reference_t<std::remove_pointer_t<C>>>>(nullptr))::value;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
inline constexpr bool is_hashable_v = is_hashable<T>::value;
|
||||
}
|
||||
25
src/Core/Core.Native/IsAllSame.h
Normal file
25
src/Core/Core.Native/IsAllSame.h
Normal file
@@ -0,0 +1,25 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
#include <type_traits>
|
||||
|
||||
namespace cdb_core
|
||||
{
|
||||
template<typename L, typename R, typename... Rest>
|
||||
struct is_all_same
|
||||
{
|
||||
constexpr static bool value =
|
||||
std::is_same<L, R>::value && is_all_same<L, Rest...>::value;
|
||||
};
|
||||
|
||||
template<typename L, typename R>
|
||||
struct is_all_same<L, R>
|
||||
{
|
||||
constexpr static bool value = std::is_same<L, R>::value;
|
||||
};
|
||||
|
||||
template<typename L, typename... R>
|
||||
inline constexpr bool is_all_same_v = is_all_same<L, R...>::value;
|
||||
}
|
||||
171
src/Core/Core.Native/Memory.h
Normal file
171
src/Core/Core.Native/Memory.h
Normal file
@@ -0,0 +1,171 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
|
||||
#include "Blittable.h"
|
||||
#include "Contract.h"
|
||||
#include "Span.h"
|
||||
|
||||
namespace cdb_core
|
||||
{
|
||||
template<typename T> struct ReadOnlyMemory;
|
||||
|
||||
/// <summary>
|
||||
/// Memory represents a owned (possibly shared) contiguous region of arbitrary memory.
|
||||
/// </summary>
|
||||
template<typename T>
|
||||
struct Memory final
|
||||
{
|
||||
static_assert(is_blittable_v<T>, "Only blittable types can be used in a Memory<T>.");
|
||||
|
||||
constexpr Memory() noexcept;
|
||||
~Memory() noexcept = default;
|
||||
explicit constexpr Memory(uint32_t length) noexcept;
|
||||
explicit constexpr Memory(const ReadOnlySpan<T>& buffer) noexcept;
|
||||
constexpr Memory(T* buffer, uint32_t length) noexcept;
|
||||
template<class Destructor,
|
||||
std::enable_if_t<std::conjunction_v<std::is_move_constructible<Destructor>,
|
||||
std::is_nothrow_invocable<Destructor&, T*&>>, int> = 0>
|
||||
Memory(T* buffer, uint32_t length, Destructor destructor);
|
||||
|
||||
Memory(const Memory<T>&) noexcept = default;
|
||||
Memory(Memory<T>&&) noexcept = default;
|
||||
Memory<T>& operator=(const Memory<T>& other) noexcept = default;
|
||||
Memory<T>& operator=(Memory<T>&& other) noexcept = default;
|
||||
|
||||
/// <summary>The number of items in the Memory.</summary>
|
||||
[[nodiscard]] uint32_t Length() const noexcept;
|
||||
|
||||
[[nodiscard]] operator ReadOnlyMemory<T>() const noexcept;
|
||||
|
||||
[[nodiscard]] ReadOnlySpan<T> AsSpan() const noexcept;
|
||||
|
||||
[[nodiscard]] Span<T> AsSpan() noexcept;
|
||||
|
||||
/// <summary>Returns true if length is 0.</summary>
|
||||
[[nodiscard]] bool IsEmpty() const noexcept;
|
||||
|
||||
[[nodiscard]] ReadOnlyMemory<T> Slice(uint32_t start) const noexcept;
|
||||
[[nodiscard]] Memory<T> Slice(uint32_t start) noexcept;
|
||||
|
||||
[[nodiscard]] ReadOnlyMemory<T> Slice(uint32_t start, uint32_t length) const noexcept;
|
||||
[[nodiscard]] Memory<T> Slice(uint32_t start, uint32_t length) noexcept;
|
||||
|
||||
bool operator==(const Memory<T>& rhs) const noexcept;
|
||||
bool operator!=(const Memory<T>& rhs) const noexcept;
|
||||
|
||||
private:
|
||||
Memory(std::shared_ptr<std::byte[]> buffer, uint32_t index, uint32_t length) noexcept;
|
||||
|
||||
std::shared_ptr<std::byte[]> m_buffer;
|
||||
uint32_t m_index;
|
||||
uint32_t m_length;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
constexpr Memory<T>::Memory() noexcept :
|
||||
m_buffer(),
|
||||
m_index(0),
|
||||
m_length(0) {}
|
||||
|
||||
template<typename T>
|
||||
constexpr Memory<T>::Memory(uint32_t length) noexcept :
|
||||
m_buffer(reinterpret_cast<std::byte*>(new T[length])),
|
||||
m_index(0),
|
||||
m_length(length) {}
|
||||
|
||||
template<typename T>
|
||||
constexpr Memory<T>::Memory(const ReadOnlySpan<T>& buffer) noexcept :
|
||||
m_buffer(buffer.Length() == 0 ? nullptr : reinterpret_cast<std::byte*>(new T[buffer.Length()])),
|
||||
m_index(0),
|
||||
m_length(buffer.Length())
|
||||
{
|
||||
buffer.CopyTo(AsSpan());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr Memory<T>::Memory(T* buffer, uint32_t length) noexcept :
|
||||
m_buffer(reinterpret_cast<std::byte*>(buffer)),
|
||||
m_index(0),
|
||||
m_length(length) {}
|
||||
|
||||
template<typename T>
|
||||
template<class Destructor,
|
||||
std::enable_if_t<std::conjunction_v<std::is_move_constructible<Destructor>,
|
||||
std::is_nothrow_invocable<Destructor&, T*&>>, int>>
|
||||
Memory<T>::Memory(T* buffer, uint32_t length, Destructor destructor) : m_buffer(reinterpret_cast<std::byte*>(buffer),
|
||||
destructor),
|
||||
m_index(0),
|
||||
m_length(length) {}
|
||||
|
||||
template<typename T>
|
||||
Memory<T>::Memory(std::shared_ptr<std::byte[]> buffer, uint32_t index, uint32_t length) noexcept :
|
||||
m_buffer(std::move(buffer)),
|
||||
m_index(index),
|
||||
m_length(length) {}
|
||||
|
||||
template<typename T>
|
||||
uint32_t Memory<T>::Length() const noexcept { return m_length; }
|
||||
|
||||
template<typename T>
|
||||
Memory<T>::operator ReadOnlyMemory<T>() const noexcept
|
||||
{
|
||||
return ReadOnlyMemory<T>(reinterpret_cast<T*>(m_buffer.get()) + m_index, m_length);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ReadOnlySpan<T> Memory<T>::AsSpan() const noexcept
|
||||
{
|
||||
return ReadOnlySpan<T>(reinterpret_cast<T*>(m_buffer.get()) + m_index, m_length);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Span<T> Memory<T>::AsSpan() noexcept
|
||||
{
|
||||
return Span<T>(reinterpret_cast<T*>(m_buffer.get()) + m_index, m_length);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool Memory<T>::IsEmpty() const noexcept { return m_length == 0; }
|
||||
|
||||
template<typename T>
|
||||
ReadOnlyMemory<T> Memory<T>::Slice(uint32_t start, uint32_t length) const noexcept
|
||||
{
|
||||
Contract::Requires(static_cast<uint64_t>(start) + static_cast<uint64_t>(length) <= static_cast<uint64_t>(m_length));
|
||||
return ReadOnlyMemory<T>(m_buffer, m_index + start, length);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Memory<T> Memory<T>::Slice(uint32_t start, uint32_t length) noexcept
|
||||
{
|
||||
Contract::Requires(static_cast<uint64_t>(start) + static_cast<uint64_t>(length) <= static_cast<uint64_t>(m_length));
|
||||
return Memory<T>(m_buffer, m_index + start, length);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ReadOnlyMemory<T> Memory<T>::Slice(uint32_t start) const noexcept
|
||||
{
|
||||
Contract::Requires(start <= m_length);
|
||||
return ReadOnlyMemory<T>(m_buffer, m_index + start, m_length - start);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Memory<T> Memory<T>::Slice(uint32_t start) noexcept
|
||||
{
|
||||
Contract::Requires(start <= m_length);
|
||||
return Memory<T>(m_buffer, m_index + start, m_length - start);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool Memory<T>::operator==(const Memory<T>& rhs) const noexcept
|
||||
{
|
||||
return m_buffer == rhs.m_buffer && m_index == rhs.m_index && m_length == rhs.m_length;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool Memory<T>::operator!=(const Memory<T>& rhs) const noexcept { return !operator==(rhs); }
|
||||
}
|
||||
62
src/Core/Core.Native/MemoryMarshal.h
Normal file
62
src/Core/Core.Native/MemoryMarshal.h
Normal file
@@ -0,0 +1,62 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Memory.h"
|
||||
#include "Span.h"
|
||||
#include "ReadOnlySpan.h"
|
||||
|
||||
namespace cdb_core
|
||||
{
|
||||
struct MemoryMarshal final
|
||||
{
|
||||
MemoryMarshal() = delete;
|
||||
|
||||
template<typename T, typename U>
|
||||
static Span<U> Cast(Span<T> source)
|
||||
{
|
||||
const size_t lengthU = (source.m_length * sizeof(T)) / sizeof(U);
|
||||
return Span<U>(reinterpret_cast<U*>(source.m_pointer), static_cast<uint32_t>(lengthU));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void Write(Span<std::byte> destination, const T& value)
|
||||
{
|
||||
static_assert(is_blittable_v<T>, "Only blittable types can be used in a Span<T>.");
|
||||
Contract::Requires(destination.m_length >= sizeof(T));
|
||||
*reinterpret_cast<T*>(destination.m_pointer) = value;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static const T& Read(const Span<std::byte>& source)
|
||||
{
|
||||
static_assert(is_blittable_v<T>, "Only blittable types can be used in a Span<T>.");
|
||||
Contract::Requires(source.m_length >= sizeof(T));
|
||||
return *reinterpret_cast<T*>(source.m_pointer);
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
static ReadOnlySpan<U> Cast(const ReadOnlySpan<T> source)
|
||||
{
|
||||
const size_t lengthU = (source.m_length * sizeof(T)) / sizeof(U);
|
||||
return ReadOnlySpan<U>(reinterpret_cast<const U*>(source.m_pointer), static_cast<uint32_t>(lengthU));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static const T& Read(const ReadOnlySpan<std::byte>& source)
|
||||
{
|
||||
static_assert(is_blittable_v<T>, "Only blittable types can be used in a ReadOnlySpan<T>.");
|
||||
Contract::Requires(source.m_length >= sizeof(T));
|
||||
return *reinterpret_cast<const T*>(source.m_pointer);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]] static Memory<T> AsMemory(const ReadOnlyMemory<T>& source) noexcept
|
||||
{
|
||||
static_assert(is_blittable_v<T>, "Only blittable types can be used in a ReadOnlySpan<T>.");
|
||||
return Memory<T>(reinterpret_cast<T*>(source.m_buffer.get()) + source.m_index, source.m_length);
|
||||
}
|
||||
};
|
||||
}
|
||||
107
src/Core/Core.Native/Microsoft.Azure.Cosmos.Core.Native.vcxproj
Normal file
107
src/Core/Core.Native/Microsoft.Azure.Cosmos.Core.Native.vcxproj
Normal file
@@ -0,0 +1,107 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>16.0</VCProjectVersion>
|
||||
<ProjectGuid>{EAED7D41-3DE6-4C41-A0E4-40D53EA3DABA}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>cdb_core</RootNamespace>
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
</Link>
|
||||
<Lib>
|
||||
<AdditionalDependencies>rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
</Link>
|
||||
<Lib>
|
||||
<AdditionalDependencies>rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Base64.h" />
|
||||
<ClInclude Include="Blittable.h" />
|
||||
<ClInclude Include="Contract.h" />
|
||||
<ClInclude Include="Core.Native.h" />
|
||||
<ClInclude Include="Crc32.h" />
|
||||
<ClInclude Include="DeepCompare.h" />
|
||||
<ClInclude Include="Endian.h" />
|
||||
<ClInclude Include="EqualityComparable.h" />
|
||||
<ClInclude Include="Failure.h" />
|
||||
<ClInclude Include="framework.h" />
|
||||
<ClInclude Include="Hashable.h" />
|
||||
<ClInclude Include="HashCode.h" />
|
||||
<ClInclude Include="IsAllSame.h" />
|
||||
<ClInclude Include="make_unique.h" />
|
||||
<ClInclude Include="MemoryMarshal.h" />
|
||||
<ClInclude Include="ReadOnlyMemory.h" />
|
||||
<ClInclude Include="ReadOnlySpan.h" />
|
||||
<ClInclude Include="Result.h" />
|
||||
<ClInclude Include="Stopwatch.h" />
|
||||
<ClInclude Include="TimeSpan.h" />
|
||||
<ClInclude Include="tla.h" />
|
||||
<ClInclude Include="Stringable.h" />
|
||||
<ClInclude Include="Memory.h" />
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="ref_ptr.h" />
|
||||
<ClInclude Include="Span.h" />
|
||||
<ClInclude Include="Strings.h" />
|
||||
<ClInclude Include="Utf8Span.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Contract.cpp" />
|
||||
<ClCompile Include="Crc32.cpp" />
|
||||
<ClCompile Include="Failure.cpp" />
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Stopwatch.cpp" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
</Project>
|
||||
132
src/Core/Core.Native/ReadOnlyMemory.h
Normal file
132
src/Core/Core.Native/ReadOnlyMemory.h
Normal file
@@ -0,0 +1,132 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
|
||||
#include "Blittable.h"
|
||||
#include "Contract.h"
|
||||
#include "ReadOnlySpan.h"
|
||||
|
||||
namespace cdb_core
|
||||
{
|
||||
struct MemoryMarshal;
|
||||
|
||||
/// <summary>
|
||||
/// ReadOnlyMemory represents a read-only view of a owned (possibly shared) contiguous region of arbitrary memory.
|
||||
/// </summary>
|
||||
template<typename T>
|
||||
struct ReadOnlyMemory final
|
||||
{
|
||||
static_assert(is_blittable_v<T>, "Only blittable types can be used in a ReadOnlyMemory<T>.");
|
||||
|
||||
constexpr ReadOnlyMemory() noexcept;
|
||||
~ReadOnlyMemory() noexcept = default;
|
||||
explicit constexpr ReadOnlyMemory(const ReadOnlySpan<T>& buffer) noexcept;
|
||||
template<class Destructor,
|
||||
std::enable_if_t<std::conjunction_v<std::is_move_constructible<Destructor>,
|
||||
std::is_nothrow_invocable<Destructor&, T*&>>, int> = 0>
|
||||
ReadOnlyMemory(T* buffer, uint32_t length, Destructor destructor);
|
||||
|
||||
ReadOnlyMemory(const ReadOnlyMemory<T>&) noexcept = default;
|
||||
ReadOnlyMemory(ReadOnlyMemory<T>&&) noexcept = default;
|
||||
ReadOnlyMemory<T>& operator=(const ReadOnlyMemory<T>& other) noexcept = default;
|
||||
ReadOnlyMemory<T>& operator=(ReadOnlyMemory<T>&& other) noexcept = default;
|
||||
|
||||
/// <summary>The number of items in the ReadOnlyMemory.</summary>
|
||||
[[nodiscard]]
|
||||
uint32_t Length() const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
ReadOnlySpan<T> AsSpan() const noexcept;
|
||||
|
||||
/// <summary>Returns true if length is 0.</summary>
|
||||
[[nodiscard]]
|
||||
bool IsEmpty() const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
ReadOnlyMemory<T> Slice(uint32_t start) const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
ReadOnlyMemory<T> Slice(uint32_t start, uint32_t length) const noexcept;
|
||||
|
||||
bool operator==(const ReadOnlyMemory<T>& rhs) const noexcept;
|
||||
bool operator!=(const ReadOnlyMemory<T>& rhs) const noexcept;
|
||||
|
||||
private:
|
||||
friend struct Memory<T>;
|
||||
friend struct MemoryMarshal;
|
||||
ReadOnlyMemory(std::shared_ptr<std::byte[]> buffer, uint32_t index, uint32_t length) noexcept;
|
||||
|
||||
std::shared_ptr<std::byte[]> m_buffer;
|
||||
uint32_t m_index;
|
||||
uint32_t m_length;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
constexpr ReadOnlyMemory<T>::ReadOnlyMemory() noexcept :
|
||||
m_buffer(),
|
||||
m_index(0),
|
||||
m_length(0) {}
|
||||
|
||||
template<typename T>
|
||||
constexpr ReadOnlyMemory<T>::ReadOnlyMemory(const ReadOnlySpan<T>& buffer) noexcept :
|
||||
m_buffer(buffer.Length() == 0 ? nullptr : reinterpret_cast<std::byte*>(new T[buffer.Length()])),
|
||||
m_index(0),
|
||||
m_length(buffer.Length())
|
||||
{
|
||||
buffer.CopyTo(AsSpan());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
template<class Destructor,
|
||||
std::enable_if_t<std::conjunction_v<std::is_move_constructible<Destructor>,
|
||||
std::is_nothrow_invocable<Destructor&, T*&>>, int>>
|
||||
ReadOnlyMemory<T>::ReadOnlyMemory(T* buffer, uint32_t length, Destructor destructor) : m_buffer(reinterpret_cast<std::byte*>(buffer),
|
||||
destructor),
|
||||
m_index(0),
|
||||
m_length(length) {}
|
||||
|
||||
template<typename T>
|
||||
ReadOnlyMemory<T>::ReadOnlyMemory(std::shared_ptr<std::byte[]> buffer, uint32_t index, uint32_t length) noexcept :
|
||||
m_buffer(std::move(buffer)),
|
||||
m_index(index),
|
||||
m_length(length) {}
|
||||
|
||||
template<typename T>
|
||||
uint32_t ReadOnlyMemory<T>::Length() const noexcept { return m_length; }
|
||||
|
||||
template<typename T>
|
||||
ReadOnlySpan<T> ReadOnlyMemory<T>::AsSpan() const noexcept
|
||||
{
|
||||
return ReadOnlySpan<T>(reinterpret_cast<T*>(m_buffer.get()) + m_index, m_length);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool ReadOnlyMemory<T>::IsEmpty() const noexcept { return m_length == 0; }
|
||||
|
||||
template<typename T>
|
||||
ReadOnlyMemory<T> ReadOnlyMemory<T>::Slice(uint32_t start, uint32_t length) const noexcept
|
||||
{
|
||||
Contract::Requires(static_cast<uint64_t>(start) + static_cast<uint64_t>(length) <= static_cast<uint64_t>(m_length));
|
||||
return ReadOnlyMemory<T>(m_buffer, m_index + start, length);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ReadOnlyMemory<T> ReadOnlyMemory<T>::Slice(uint32_t start) const noexcept
|
||||
{
|
||||
Contract::Requires(start <= m_length);
|
||||
return ReadOnlyMemory<T>(m_buffer, m_index + start, m_length - start);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool ReadOnlyMemory<T>::operator==(const ReadOnlyMemory<T>& rhs) const noexcept
|
||||
{
|
||||
return m_buffer == rhs.m_buffer && m_index == rhs.m_index && m_length == rhs.m_length;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool ReadOnlyMemory<T>::operator!=(const ReadOnlyMemory<T>& rhs) const noexcept { return !operator==(rhs); }
|
||||
}
|
||||
173
src/Core/Core.Native/ReadOnlySpan.h
Normal file
173
src/Core/Core.Native/ReadOnlySpan.h
Normal file
@@ -0,0 +1,173 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include "Blittable.h"
|
||||
|
||||
namespace cdb_core
|
||||
{
|
||||
template<typename T> struct Span;
|
||||
|
||||
/// <summary>A readonly, bounds-checked, pointer to an buffer.</summary>
|
||||
/// <remarks></remarks>
|
||||
template<class T>
|
||||
struct ReadOnlySpan
|
||||
{
|
||||
static_assert(is_blittable_v<T>, "Only blittable types can be used in a ReadOnlySpan<T>.");
|
||||
|
||||
constexpr ReadOnlySpan() noexcept;
|
||||
~ReadOnlySpan() noexcept = default;
|
||||
constexpr ReadOnlySpan(const T* pointer, uint32_t length) noexcept;
|
||||
template<size_t N> constexpr ReadOnlySpan(const std::array<T, N>&) noexcept;
|
||||
constexpr ReadOnlySpan(const std::vector<T>&) noexcept;
|
||||
constexpr ReadOnlySpan(const Span<T>&) noexcept;
|
||||
ReadOnlySpan(const ReadOnlySpan<T>&) noexcept = default;
|
||||
ReadOnlySpan(ReadOnlySpan<T>&&) noexcept = default;
|
||||
ReadOnlySpan<T>& operator=(const ReadOnlySpan<T>& other) noexcept = default;
|
||||
ReadOnlySpan<T>& operator=(ReadOnlySpan<T>&& other) noexcept = default;
|
||||
|
||||
/// <summary>Returns a reference to specified element of the ReadOnlySpan.</summary>
|
||||
constexpr const T& operator[](uint32_t index) const noexcept;
|
||||
|
||||
/// <summary>The number of items in the span.</summary>
|
||||
[[nodiscard]]
|
||||
constexpr uint32_t Length() const noexcept;
|
||||
|
||||
/// <summary>Returns true if Length is 0.</summary>
|
||||
[[nodiscard]]
|
||||
constexpr bool IsEmpty() const noexcept;
|
||||
|
||||
bool operator==(const ReadOnlySpan<T>& rhs) const noexcept;
|
||||
bool operator!=(const ReadOnlySpan<T>& rhs) const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
ReadOnlySpan<T> Slice(uint32_t start) const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
ReadOnlySpan<T> Slice(uint32_t start, uint32_t length) const noexcept;
|
||||
|
||||
void CopyTo(Span<T> destination) const noexcept;
|
||||
|
||||
template<typename = std::enable_if_t<std::is_same_v<std::byte, T>>>
|
||||
[[nodiscard]] constexpr int SequenceCompareTo(ReadOnlySpan<T> other) const noexcept;
|
||||
|
||||
template<typename = std::enable_if_t<std::is_same_v<std::byte, T>>>
|
||||
[[nodiscard]] constexpr bool SequenceEqual(ReadOnlySpan<T> other) const noexcept;
|
||||
|
||||
struct ReadOnlySpanIter
|
||||
{
|
||||
bool operator==(const ReadOnlySpanIter& other) const noexcept { return m_pointer == other.m_pointer; }
|
||||
bool operator!=(const ReadOnlySpanIter& other) const noexcept { return m_pointer != other.m_pointer; }
|
||||
|
||||
const ReadOnlySpanIter& operator++() noexcept
|
||||
{
|
||||
++m_pointer;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const T& operator*() const noexcept { return *m_pointer; }
|
||||
private:
|
||||
explicit ReadOnlySpanIter(const T* p) : m_pointer(p) {}
|
||||
friend struct ReadOnlySpan<T>;
|
||||
const T* m_pointer;
|
||||
};
|
||||
|
||||
[[nodiscard]] ReadOnlySpanIter begin() const noexcept { return ReadOnlySpanIter{m_pointer}; }
|
||||
[[nodiscard]] ReadOnlySpanIter end() const noexcept { return ReadOnlySpanIter{m_pointer + m_length}; }
|
||||
|
||||
private:
|
||||
friend struct MemoryMarshal;
|
||||
template<typename U> friend struct Span;
|
||||
|
||||
const T* m_pointer;
|
||||
uint32_t m_length;
|
||||
};
|
||||
|
||||
template<class T> constexpr ReadOnlySpan<T>::ReadOnlySpan() noexcept : m_pointer(nullptr), m_length(0) {}
|
||||
|
||||
template<class T> constexpr ReadOnlySpan<T>::ReadOnlySpan(const T* pointer, uint32_t length) noexcept :
|
||||
m_pointer(pointer),
|
||||
m_length(length) { }
|
||||
|
||||
template<class T> template<size_t N> constexpr ReadOnlySpan<T>::ReadOnlySpan(const std::array<T, N>& arr) noexcept :
|
||||
m_pointer{arr.data()},
|
||||
m_length{static_cast<uint32_t>(arr.size())} { }
|
||||
|
||||
// Class Template Argument Deduction (CTAD) hint
|
||||
template<typename T, size_t N> ReadOnlySpan(std::array<T, N>&) noexcept -> ReadOnlySpan<T>;
|
||||
|
||||
template<class T> constexpr ReadOnlySpan<T>::ReadOnlySpan(const std::vector<T>& arr) noexcept :
|
||||
m_pointer{arr.data()},
|
||||
m_length{static_cast<uint32_t>(arr.size())} { }
|
||||
|
||||
// Class Template Argument Deduction (CTAD) hint
|
||||
template<typename T> ReadOnlySpan(const std::vector<T>& arr) noexcept -> ReadOnlySpan<T>;
|
||||
|
||||
template<class T> constexpr ReadOnlySpan<T>::ReadOnlySpan(const Span<T>& span) noexcept :
|
||||
m_pointer(span.m_pointer),
|
||||
m_length(span.m_length) { }
|
||||
|
||||
// Class Template Argument Deduction (CTAD) hint
|
||||
template<typename T> ReadOnlySpan(const Span<T>& span) noexcept -> ReadOnlySpan<T>;
|
||||
|
||||
template<class T> constexpr const T& ReadOnlySpan<T>::operator[](uint32_t index) const noexcept
|
||||
{
|
||||
Contract::Assert(index < m_length);
|
||||
return m_pointer[index];
|
||||
}
|
||||
|
||||
template<class T> constexpr uint32_t ReadOnlySpan<T>::Length() const noexcept { return m_length; }
|
||||
|
||||
template<class T> constexpr bool ReadOnlySpan<T>::IsEmpty() const noexcept { return m_length == 0; }
|
||||
|
||||
template<typename T>
|
||||
bool ReadOnlySpan<T>::operator==(const ReadOnlySpan<T>& rhs) const noexcept
|
||||
{
|
||||
return m_pointer == rhs.m_pointer && m_length == rhs.m_length;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool ReadOnlySpan<T>::operator!=(const ReadOnlySpan<T>& rhs) const noexcept { return !operator==(rhs); }
|
||||
|
||||
template<typename T>
|
||||
ReadOnlySpan<T> ReadOnlySpan<T>::Slice(uint32_t start, uint32_t length) const noexcept
|
||||
{
|
||||
Contract::Requires(static_cast<uint64_t>(start) + static_cast<uint64_t>(length) <= static_cast<uint64_t>(m_length));
|
||||
return ReadOnlySpan<T>(m_pointer + start, length);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ReadOnlySpan<T> ReadOnlySpan<T>::Slice(uint32_t start) const noexcept
|
||||
{
|
||||
Contract::Requires(start <= m_length);
|
||||
return ReadOnlySpan<T>(m_pointer + start, m_length - start);
|
||||
}
|
||||
|
||||
template<typename T> void ReadOnlySpan<T>::CopyTo(Span<T> destination) const noexcept
|
||||
{
|
||||
Contract::Requires(m_length <= destination.m_length);
|
||||
::memmove_s(static_cast<void*>(destination.m_pointer), destination.m_length * sizeof(T),
|
||||
static_cast<void*>(const_cast<T*>(m_pointer)), m_length * sizeof(T));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
template<typename>
|
||||
constexpr int ReadOnlySpan<T>::SequenceCompareTo(ReadOnlySpan<T> other) const noexcept
|
||||
{
|
||||
uint32_t len = std::min<uint32_t>(m_length, other.m_length);
|
||||
int cmp = ::memcmp(m_pointer, other.m_pointer, len * sizeof(T));
|
||||
return (cmp == 0) ? static_cast<int>(m_length - other.m_length) : cmp;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
template<typename>
|
||||
constexpr bool ReadOnlySpan<T>::SequenceEqual(ReadOnlySpan<T> other) const noexcept
|
||||
{
|
||||
return m_length == other.m_length &&
|
||||
::memcmp(m_pointer, other.m_pointer, m_length * sizeof(T)) == 0;
|
||||
}
|
||||
}
|
||||
98
src/Core/Core.Native/Result.h
Normal file
98
src/Core/Core.Native/Result.h
Normal file
@@ -0,0 +1,98 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
#include "Failure.h"
|
||||
#include <variant>
|
||||
|
||||
namespace cdb_core
|
||||
{
|
||||
/// <summary>
|
||||
/// Result is analogous to an "Either" - holds a successful
|
||||
/// result or a failure. Result<> is a value type.
|
||||
/// </summary>
|
||||
template<typename TResult>
|
||||
class [[nodiscard]] Result
|
||||
{
|
||||
public:
|
||||
constexpr Result(TResult result) noexcept;
|
||||
constexpr Result(Failure failure) noexcept;
|
||||
Result(Result<TResult>&&) noexcept = default;
|
||||
~Result() = default;
|
||||
Result& operator=(Result<TResult>&&) noexcept = default;
|
||||
|
||||
[[nodiscard]] bool IsSuccess() const;
|
||||
TResult GetResult();
|
||||
|
||||
[[nodiscard]] Failure GetFailure() const noexcept;
|
||||
|
||||
private:
|
||||
std::variant<TResult, Failure> m_value;
|
||||
};
|
||||
|
||||
template<typename TResult>
|
||||
constexpr Result<TResult>::Result(TResult result) noexcept : m_value{ std::in_place_index<0>, std::move(result) } { }
|
||||
|
||||
template<typename TResult>
|
||||
constexpr Result<TResult>::Result(Failure failure) noexcept : m_value{ std::in_place_index<1>, std::move(failure) } { }
|
||||
|
||||
template<typename TResult>
|
||||
TResult Result<TResult>::GetResult()
|
||||
{
|
||||
Contract::Requires(IsSuccess());
|
||||
return std::move(std::get<0>(m_value));
|
||||
}
|
||||
|
||||
template<typename TResult>
|
||||
bool Result<TResult>::IsSuccess() const
|
||||
{
|
||||
return m_value.index() == 0;
|
||||
}
|
||||
|
||||
template<typename TResult>
|
||||
Failure Result<TResult>::GetFailure() const noexcept
|
||||
{
|
||||
Contract::Requires(!IsSuccess());
|
||||
return std::move(std::get<1>(m_value));
|
||||
}
|
||||
|
||||
template<>
|
||||
class [[nodiscard]] Result<void>
|
||||
{
|
||||
public:
|
||||
Result();
|
||||
explicit Result(Failure failure);
|
||||
Result(const Result<void>&) = default;
|
||||
Result(Result<void>&&) = default;
|
||||
Result<void>& operator=(const Result<void>&) = default;
|
||||
Result<void>& operator=(Result<void>&&) = default;
|
||||
|
||||
[[nodiscard]]
|
||||
bool IsSuccess() const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
Failure GetFailure() const noexcept;
|
||||
|
||||
private:
|
||||
Failure m_failure;
|
||||
};
|
||||
|
||||
inline Result<void>::Result() : m_failure(Failure::None) { }
|
||||
|
||||
inline Result<void>::Result(Failure failure) : m_failure(failure)
|
||||
{
|
||||
Contract::Requires(failure.IsFailed(),
|
||||
"Result constructed with Failure must be failed. Use Result() if no failure occurred");
|
||||
}
|
||||
|
||||
inline bool Result<void>::IsSuccess() const noexcept
|
||||
{
|
||||
return !m_failure.IsFailed();
|
||||
}
|
||||
|
||||
inline Failure Result<void>::GetFailure() const noexcept
|
||||
{
|
||||
return m_failure;
|
||||
}
|
||||
}
|
||||
192
src/Core/Core.Native/Span.h
Normal file
192
src/Core/Core.Native/Span.h
Normal file
@@ -0,0 +1,192 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include "Blittable.h"
|
||||
|
||||
namespace cdb_core
|
||||
{
|
||||
template<typename T> struct ReadOnlySpan;
|
||||
|
||||
/// <summary>A bounds-checked, pointer to an buffer.</summary>
|
||||
/// <remarks></remarks>
|
||||
template<class T>
|
||||
struct Span
|
||||
{
|
||||
static_assert(is_blittable_v<T>, "Only blittable types can be used in a Span<T>.");
|
||||
|
||||
constexpr Span() noexcept;
|
||||
~Span() noexcept = default;
|
||||
constexpr Span(T* pointer, uint32_t length) noexcept;
|
||||
template<size_t N> constexpr Span(std::array<T, N>&) noexcept;
|
||||
constexpr Span(std::vector<T>&) noexcept;
|
||||
Span(const Span<T>&) noexcept = default;
|
||||
Span(Span<T>&&) noexcept = default;
|
||||
Span<T>& operator=(const Span<T>& other) noexcept = default;
|
||||
Span<T>& operator=(Span<T>&& other) noexcept = default;
|
||||
|
||||
/// <summary>Returns a reference to specified element of the Span.</summary>
|
||||
const T& operator[](uint32_t index) const noexcept;
|
||||
T& operator[](uint32_t index) noexcept;
|
||||
|
||||
/// <summary>The number of items in the span.</summary>
|
||||
[[nodiscard]] uint32_t Length() const noexcept;
|
||||
|
||||
/// <summary>Returns true if Length is 0.</summary>
|
||||
[[nodiscard]] bool IsEmpty() const noexcept;
|
||||
|
||||
bool operator==(const Span<T>& rhs) const noexcept;
|
||||
bool operator!=(const Span<T>& rhs) const noexcept;
|
||||
|
||||
[[nodiscard]] ReadOnlySpan<T> Slice(uint32_t start) const noexcept;
|
||||
[[nodiscard]] Span<T> Slice(uint32_t start) noexcept;
|
||||
|
||||
[[nodiscard]] ReadOnlySpan<T> Slice(uint32_t start, uint32_t length) const noexcept;
|
||||
[[nodiscard]] Span<T> Slice(uint32_t start, uint32_t length) noexcept;
|
||||
|
||||
void CopyTo(Span<T> destination) const noexcept;
|
||||
|
||||
template<typename = std::enable_if_t<std::is_same_v<std::byte, T>>>
|
||||
void Fill(std::byte value) noexcept;
|
||||
|
||||
template<typename = std::enable_if_t<std::is_same_v<std::byte, T>>>
|
||||
[[nodiscard]] constexpr int SequenceCompareTo(ReadOnlySpan<T> other) const noexcept;
|
||||
|
||||
template<typename = std::enable_if_t<std::is_same_v<std::byte, T>>>
|
||||
[[nodiscard]] constexpr bool SequenceEqual(ReadOnlySpan<T> other) const noexcept;
|
||||
|
||||
struct SpanIter
|
||||
{
|
||||
bool operator==(const SpanIter& other) const noexcept { return m_pointer == other.m_pointer; }
|
||||
bool operator!=(const SpanIter& other) const noexcept { return m_pointer != other.m_pointer; }
|
||||
|
||||
const SpanIter& operator++() noexcept
|
||||
{
|
||||
++m_pointer;
|
||||
return *this;
|
||||
}
|
||||
|
||||
T& operator*() const noexcept { return *m_pointer; }
|
||||
private:
|
||||
explicit SpanIter(T* p) : m_pointer(p) {}
|
||||
friend struct Span<T>;
|
||||
T* m_pointer;
|
||||
};
|
||||
|
||||
[[nodiscard]] SpanIter begin() const noexcept { return SpanIter{m_pointer}; }
|
||||
[[nodiscard]] SpanIter end() const noexcept { return SpanIter{m_pointer + m_length}; }
|
||||
|
||||
private:
|
||||
friend struct MemoryMarshal;
|
||||
template<typename U> friend struct ReadOnlySpan;
|
||||
|
||||
T* m_pointer;
|
||||
uint32_t m_length;
|
||||
};
|
||||
|
||||
template<class T> constexpr Span<T>::Span() noexcept : m_pointer(nullptr), m_length(0) {}
|
||||
|
||||
template<class T>
|
||||
constexpr Span<T>::Span(T* pointer, uint32_t length) noexcept : m_pointer(pointer), m_length(length) { }
|
||||
|
||||
template<class T> template<size_t N> constexpr Span<T>::Span(std::array<T, N>& arr) noexcept :
|
||||
m_pointer{arr.data()},
|
||||
m_length{static_cast<uint32_t>(arr.size())} { }
|
||||
|
||||
// Class Template Argument Deduction (CTAD) hint
|
||||
template<typename T, size_t N> Span(std::array<T, N>&) noexcept -> Span<T>;
|
||||
|
||||
template<class T> constexpr Span<T>::Span(std::vector<T>& arr) noexcept :
|
||||
m_pointer{arr.data()},
|
||||
m_length{static_cast<uint32_t>(arr.size())} { }
|
||||
|
||||
// Class Template Argument Deduction (CTAD) hint
|
||||
template<typename T, size_t N> Span(std::vector<T>&) noexcept -> Span<T>;
|
||||
|
||||
template<class T> const T& Span<T>::operator[](uint32_t index) const noexcept
|
||||
{
|
||||
Contract::Assert(index < m_length);
|
||||
return m_pointer[index];
|
||||
}
|
||||
|
||||
template<class T> T& Span<T>::operator[](uint32_t index) noexcept
|
||||
{
|
||||
Contract::Assert(index < m_length);
|
||||
return m_pointer[index];
|
||||
}
|
||||
|
||||
template<class T> uint32_t Span<T>::Length() const noexcept { return m_length; }
|
||||
|
||||
template<class T> bool Span<T>::IsEmpty() const noexcept { return m_length == 0; }
|
||||
|
||||
template<typename T>
|
||||
bool Span<T>::operator==(const Span<T>& rhs) const noexcept
|
||||
{
|
||||
return m_pointer == rhs.m_pointer && m_length == rhs.m_length;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool Span<T>::operator!=(const Span<T>& rhs) const noexcept { return !operator==(rhs); }
|
||||
|
||||
template<typename T>
|
||||
ReadOnlySpan<T> Span<T>::Slice(uint32_t start, uint32_t length) const noexcept
|
||||
{
|
||||
Contract::Requires(static_cast<uint64_t>(start) + static_cast<uint64_t>(length) <= static_cast<uint64_t>(m_length));
|
||||
return Span<T>(m_pointer + start, length);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Span<T> Span<T>::Slice(uint32_t start, uint32_t length) noexcept
|
||||
{
|
||||
Contract::Requires(static_cast<uint64_t>(start) + static_cast<uint64_t>(length) <= static_cast<uint64_t>(m_length));
|
||||
return Span<T>(m_pointer + start, length);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ReadOnlySpan<T> Span<T>::Slice(uint32_t start) const noexcept
|
||||
{
|
||||
Contract::Requires(start <= m_length);
|
||||
return ReadOnlySpan<T>(m_pointer + start, m_length - start);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Span<T> Span<T>::Slice(uint32_t start) noexcept
|
||||
{
|
||||
Contract::Requires(start <= m_length);
|
||||
return Span<T>(m_pointer + start, m_length - start);
|
||||
}
|
||||
|
||||
template<typename T> void Span<T>::CopyTo(Span<T> destination) const noexcept
|
||||
{
|
||||
Contract::Requires(m_length <= destination.m_length);
|
||||
::memmove_s(static_cast<void*>(destination.m_pointer), destination.m_length * sizeof(T),
|
||||
static_cast<void*>(m_pointer), m_length * sizeof(T));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
template<typename>
|
||||
void Span<T>::Fill(std::byte value) noexcept
|
||||
{
|
||||
::memset(m_pointer, static_cast<int>(value), m_length * sizeof(T));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
template<typename>
|
||||
constexpr int Span<T>::SequenceCompareTo(ReadOnlySpan<T> other) const noexcept
|
||||
{
|
||||
uint32_t len = std::min<uint32_t>(m_length, other.m_length);
|
||||
int cmp = ::memcmp(m_pointer, other.m_pointer, len * sizeof(T));
|
||||
return (cmp == 0) ? static_cast<int>(m_length - other.m_length) : cmp;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
template<typename>
|
||||
constexpr bool Span<T>::SequenceEqual(ReadOnlySpan<T> other) const noexcept
|
||||
{
|
||||
return m_length == other.m_length &&
|
||||
::memcmp(m_pointer, other.m_pointer, m_length * sizeof(T)) == 0;
|
||||
}
|
||||
}
|
||||
45
src/Core/Core.Native/Stopwatch.cpp
Normal file
45
src/Core/Core.Native/Stopwatch.cpp
Normal file
@@ -0,0 +1,45 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#include "pch.h"
|
||||
#include "Stopwatch.h"
|
||||
|
||||
namespace cdb_core
|
||||
{
|
||||
static LARGE_INTEGER InitFrequency() noexcept
|
||||
{
|
||||
LARGE_INTEGER freq;
|
||||
QueryPerformanceFrequency(&freq);
|
||||
return freq;
|
||||
}
|
||||
|
||||
LARGE_INTEGER Stopwatch::s_frequency = InitFrequency();
|
||||
|
||||
void Stopwatch::Start() noexcept
|
||||
{
|
||||
if (m_running)
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_running = true;
|
||||
QueryPerformanceCounter(&m_start);
|
||||
}
|
||||
|
||||
void Stopwatch::Stop() noexcept
|
||||
{
|
||||
if (!m_running)
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_running = false;
|
||||
LARGE_INTEGER end;
|
||||
QueryPerformanceCounter(&end);
|
||||
m_elapsed.QuadPart += end.QuadPart - m_start.QuadPart;
|
||||
}
|
||||
|
||||
void Stopwatch::Reset() noexcept
|
||||
{
|
||||
m_elapsed.QuadPart = 0;
|
||||
}
|
||||
}
|
||||
48
src/Core/Core.Native/Stopwatch.h
Normal file
48
src/Core/Core.Native/Stopwatch.h
Normal file
@@ -0,0 +1,48 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "TimeSpan.h"
|
||||
|
||||
namespace cdb_core
|
||||
{
|
||||
class Stopwatch final
|
||||
{
|
||||
public:
|
||||
Stopwatch() noexcept = default;
|
||||
~Stopwatch() noexcept = default;
|
||||
Stopwatch(const Stopwatch& other) = default;
|
||||
Stopwatch(Stopwatch&& other) noexcept = default;
|
||||
Stopwatch& operator=(const Stopwatch& other) = default;
|
||||
Stopwatch& operator=(Stopwatch&& other) noexcept = default;
|
||||
|
||||
void Start() noexcept;
|
||||
void Stop() noexcept;
|
||||
void Reset() noexcept;
|
||||
[[nodiscard]] TimeSpan Elapsed() const noexcept;
|
||||
|
||||
[[nodiscard]] int64_t ElapsedRaw() const noexcept;
|
||||
[[nodiscard]] static TimeSpan AsTimeSpan(int64_t raw) noexcept;
|
||||
|
||||
private:
|
||||
bool m_running;
|
||||
LARGE_INTEGER m_start;
|
||||
LARGE_INTEGER m_elapsed;
|
||||
|
||||
static LARGE_INTEGER s_frequency;
|
||||
};
|
||||
|
||||
inline int64_t Stopwatch::ElapsedRaw() const noexcept { return m_elapsed.QuadPart; }
|
||||
|
||||
inline TimeSpan Stopwatch::Elapsed() const noexcept
|
||||
{
|
||||
return Stopwatch::AsTimeSpan(m_elapsed.QuadPart);
|
||||
}
|
||||
|
||||
inline TimeSpan Stopwatch::AsTimeSpan(int64_t raw) noexcept
|
||||
{
|
||||
return TimeSpan::FromTicks((raw * 10000000) / s_frequency.QuadPart);
|
||||
}
|
||||
}
|
||||
33
src/Core/Core.Native/Stringable.h
Normal file
33
src/Core/Core.Native/Stringable.h
Normal file
@@ -0,0 +1,33 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
#include <string>
|
||||
|
||||
namespace cdb_core
|
||||
{
|
||||
/// <summary>Check for a ToString method.</summary>
|
||||
/// <remarks>Method must have this signature:
|
||||
/// <code>TString ToString() const noexcept</code>
|
||||
/// </remarks>
|
||||
template<typename C, typename TString = std::string>
|
||||
struct is_stringable
|
||||
{
|
||||
private:
|
||||
template<typename T>
|
||||
constexpr static typename std::is_same<decltype(&T::ToString), TString (T::*)() const noexcept>::type Check(T*)
|
||||
{
|
||||
return std::true_type{};
|
||||
};
|
||||
|
||||
template<typename>
|
||||
constexpr static std::false_type Check(...) { return std::false_type{}; };
|
||||
public:
|
||||
constexpr static bool value = decltype(Check<std::remove_const_t<std::remove_reference_t<std::remove_pointer_t<C>>>
|
||||
>(nullptr))::value;
|
||||
};
|
||||
|
||||
template<typename T, typename TString = std::string>
|
||||
inline constexpr bool is_stringable_v = is_stringable<T, TString>::value;
|
||||
}
|
||||
226
src/Core/Core.Native/Strings.h
Normal file
226
src/Core/Core.Native/Strings.h
Normal file
@@ -0,0 +1,226 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "tla.h"
|
||||
|
||||
namespace cdb_core
|
||||
{
|
||||
/// <summary>
|
||||
/// These types are not meant to be used directly but instead exist to facilitate return type inference.
|
||||
/// </summary>
|
||||
namespace Internal
|
||||
{
|
||||
// Forward references.
|
||||
template<typename TFormat, typename... Args> struct StringFormat;
|
||||
template<typename... Args> struct Joiner;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Makes a string using printf style format specifiers.
|
||||
/// </summary>
|
||||
/// <typeparam name="TFormat">The type of the format string.</typeparam>
|
||||
/// <typeparam name="TArgs">The types of the arguments to the format specifiers.</typeparam>
|
||||
/// <param name="format">The format string.</param>
|
||||
/// <param name="args">The arguments to the format specifiers.</param>
|
||||
/// <returns>A string object.</returns>
|
||||
/// <example>
|
||||
/// The return type of this method is not meant to be used directly, but instead to facilitate return type
|
||||
/// inference. E.g.
|
||||
/// <code>
|
||||
/// std::string u8 = make_string("foo: %s", "some value");
|
||||
/// std::wstring u16 = string_join(L"foo: %s", L"some wide-string value");
|
||||
/// tla::string s = string_join("foo: %s", "some value");
|
||||
/// </code>
|
||||
/// </example>
|
||||
template<class TFormat, typename... TArgs>
|
||||
Internal::StringFormat<TFormat, TArgs...> make_string(TFormat format, TArgs ... args)
|
||||
{
|
||||
return Internal::StringFormat<TFormat, TArgs...>(format, args...);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Makes a string using <c>printf</c> style format specifiers.
|
||||
/// </summary>
|
||||
/// <remarks>This is an explicit version of the above method for return type inference.</remarks>
|
||||
/// <typeparam name="TFormat">The type of the format string.</typeparam>
|
||||
/// <typeparam name="TArgs">The types of the arguments to the format specifiers.</typeparam>
|
||||
/// <param name="format">The format string.</param>
|
||||
/// <param name="args">The arguments to the format specifiers.</param>
|
||||
/// <returns>A string object.</returns>
|
||||
/// <example>
|
||||
/// This overload allows you to specify the return type explicitly for use in expressions where
|
||||
/// the return value will be consumed instead of bound to an lvalue.
|
||||
/// <code>
|
||||
/// Logger::WriteMessage(make_string<std::string>("foo: %s", "some value").c_str());
|
||||
/// Logger::WriteMessage(make_string<std::wstring>(L"foo: %s", L"some wide-string value").c_str());
|
||||
/// </code>
|
||||
/// </example>
|
||||
template<class TResult, class TFormat, typename... TArgs>
|
||||
TResult make_string(TFormat format, TArgs ... args)
|
||||
{
|
||||
return Internal::StringFormat<TFormat, TArgs...>(format, args...);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Makes a string by combining existing (possibly heterogeneous) string elements together.
|
||||
/// </summary>
|
||||
/// <typeparam name="TArgs">The types of the string elements to join.</typeparam>
|
||||
/// <param name="args">The string elements to join.</param>
|
||||
/// <returns>A string object.</returns>
|
||||
/// <example>
|
||||
/// The return type of this method is not meant to be used directly, but instead to facilitate return type
|
||||
/// inference. E.g.
|
||||
/// <code>
|
||||
/// std::string u8 = string_join("a", "b", "c");
|
||||
/// std::wstring u16 = string_join(L"a", L"b", L"c");
|
||||
/// tla::string s = string_join("a", "b", "c");
|
||||
/// </code>
|
||||
/// </example>
|
||||
template<typename... Args>
|
||||
Internal::Joiner<Args...> string_join(Args ... args)
|
||||
{
|
||||
return Internal::Joiner<Args...>{args...};
|
||||
}
|
||||
|
||||
namespace Internal
|
||||
{
|
||||
template<typename... Args>
|
||||
struct StringFormatter {};
|
||||
|
||||
template<>
|
||||
struct StringFormatter<>
|
||||
{
|
||||
[[nodiscard]] constexpr StringFormatter() noexcept = default;
|
||||
~StringFormatter() noexcept = default;
|
||||
StringFormatter(const StringFormatter<>& other) noexcept = delete;
|
||||
StringFormatter(StringFormatter<>&& other) noexcept = default;
|
||||
StringFormatter<>& operator=(const StringFormatter<>& other) noexcept = delete;
|
||||
StringFormatter<>& operator=(StringFormatter<>&& other) noexcept = delete;
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning( disable: 4840 )
|
||||
template<typename TResult, typename TFormat, typename... TFormatArgs>
|
||||
std::enable_if_t<std::is_same_v<typename TResult::value_type, char>, TResult> Format(
|
||||
TFormat format, TFormatArgs ... args)
|
||||
{
|
||||
const size_t size = snprintf(nullptr, 0, &format[0], args ...) + 1; // Extra space for '\0'
|
||||
std::unique_ptr<char[]> buf(new char[size]);
|
||||
snprintf(buf.get(), size, &format[0], args...);
|
||||
return TResult(buf.get(), buf.get() + size - 1); // exclude terminating null
|
||||
}
|
||||
|
||||
#pragma warning( disable: 4996 )
|
||||
template<typename TResult, typename TFormat, typename... TFormatArgs>
|
||||
std::enable_if_t<std::is_same_v<typename TResult::value_type, wchar_t>, TResult> Format(
|
||||
TFormat format, TFormatArgs ... args)
|
||||
{
|
||||
const size_t size = _snwprintf(nullptr, 0, &format[0], args ...) + 1; // Extra space for '\0'
|
||||
std::unique_ptr<wchar_t[]> buf(new wchar_t[size]);
|
||||
_snwprintf(buf.get(), size, &format[0], args...);
|
||||
return TResult(buf.get(), buf.get() + size - 1); // exclude terminating null
|
||||
}
|
||||
#pragma warning( pop )
|
||||
};
|
||||
|
||||
template<typename T, typename... Args>
|
||||
struct StringFormatter<T, Args...> : private StringFormatter<Args...>
|
||||
{
|
||||
using Base = StringFormatter<Args...>;
|
||||
|
||||
[[nodiscard]] constexpr StringFormatter(T first, Args ... args) noexcept : Base{args...}, m_first{first} {}
|
||||
~StringFormatter() noexcept = default;
|
||||
StringFormatter(const StringFormatter& other) noexcept = delete;
|
||||
StringFormatter(StringFormatter&& other) noexcept = default;
|
||||
StringFormatter& operator=(const StringFormatter& other) noexcept = delete;
|
||||
StringFormatter& operator=(StringFormatter&& other) noexcept = delete;
|
||||
|
||||
protected:
|
||||
template<typename TResult, typename TFormat, typename... TFormatArgs>
|
||||
TResult Format(TFormat format, TFormatArgs ... args)
|
||||
{
|
||||
return Base::template Format<TResult>(format, args..., m_first);
|
||||
}
|
||||
|
||||
private:
|
||||
T m_first;
|
||||
};
|
||||
|
||||
template<typename TFormat, typename... Args>
|
||||
struct StringFormat : private StringFormatter<Args...>
|
||||
{
|
||||
using Base = StringFormatter<Args...>;
|
||||
|
||||
[[nodiscard]] constexpr StringFormat(TFormat format, Args ... args) noexcept : Base{args...}, m_format{format} {}
|
||||
~StringFormat() noexcept = default;
|
||||
StringFormat(const StringFormat& other) noexcept = delete;
|
||||
StringFormat(StringFormat&& other) noexcept = default;
|
||||
StringFormat& operator=(const StringFormat& other) noexcept = delete;
|
||||
StringFormat& operator=(StringFormat&& other) noexcept = delete;
|
||||
|
||||
// ReSharper disable once CppNonExplicitConversionOperator
|
||||
template<class TElem, class TTraits, class TAlloc>
|
||||
operator std::basic_string<TElem, TTraits, TAlloc>()
|
||||
{
|
||||
return Base::template Format<std::basic_string<TElem, TTraits, TAlloc>>(m_format);
|
||||
}
|
||||
|
||||
private:
|
||||
TFormat m_format;
|
||||
};
|
||||
|
||||
template<typename... Args>
|
||||
struct Joiner {};
|
||||
|
||||
template<>
|
||||
struct Joiner<>
|
||||
{
|
||||
[[nodiscard]] constexpr Joiner() noexcept = default;
|
||||
~Joiner() noexcept = default;
|
||||
Joiner(const Joiner<>& other) noexcept = delete;
|
||||
Joiner(Joiner<>&& other) noexcept = default;
|
||||
Joiner<>& operator=(const Joiner<>& other) noexcept = delete;
|
||||
Joiner<>& operator=(Joiner<>&& other) noexcept = delete;
|
||||
|
||||
template<typename TResult>
|
||||
static void Add(TResult& ret) { }
|
||||
};
|
||||
|
||||
template<typename T, typename... Args>
|
||||
struct Joiner<T, Args...> : private Joiner<Args...>
|
||||
{
|
||||
using Base = Joiner<Args...>;
|
||||
|
||||
[[nodiscard]] constexpr Joiner(T first, Args ... args) noexcept : Base{args...}, m_first{first} {}
|
||||
~Joiner() noexcept = default;
|
||||
Joiner(const Joiner& other) noexcept = delete;
|
||||
Joiner(Joiner&& other) noexcept = default;
|
||||
Joiner& operator=(const Joiner& other) noexcept = delete;
|
||||
Joiner& operator=(Joiner&& other) noexcept = delete;
|
||||
|
||||
// ReSharper disable once CppNonExplicitConversionOperator
|
||||
template<typename TResult>
|
||||
operator TResult()
|
||||
{
|
||||
TResult ret{m_first};
|
||||
Base::Add(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
protected:
|
||||
template<typename TResult>
|
||||
void Add(TResult& ret)
|
||||
{
|
||||
ret += m_first;
|
||||
Base::Add(ret);
|
||||
}
|
||||
|
||||
private:
|
||||
T m_first;
|
||||
};
|
||||
}
|
||||
}
|
||||
83
src/Core/Core.Native/TimeSpan.h
Normal file
83
src/Core/Core.Native/TimeSpan.h
Normal file
@@ -0,0 +1,83 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
|
||||
namespace cdb_core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a time interval.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// TimeSpan mimics the behavior of C# TimeSpan and provides APIs for getting time intervals
|
||||
/// in similar APIs for various durations.
|
||||
/// </remarks>
|
||||
struct TimeSpan final
|
||||
{
|
||||
public:
|
||||
static TimeSpan FromTicks(int64_t oneHundredNanosecondTicks) noexcept;
|
||||
static TimeSpan FromMilliseconds(int64_t milliseconds) noexcept;
|
||||
static TimeSpan FromSeconds(int64_t seconds) noexcept;
|
||||
|
||||
[[nodiscard]] int64_t GetTotalMilliseconds() const noexcept;
|
||||
[[nodiscard]] int64_t GetTotalSeconds() const noexcept;
|
||||
[[nodiscard]] int64_t GetTotalTicks() const noexcept;
|
||||
|
||||
[[nodiscard]] double GetMilliseconds() const noexcept;
|
||||
[[nodiscard]] double GetMicroseconds() const noexcept;
|
||||
|
||||
private:
|
||||
using durationTicks = std::chrono::duration<int64_t, std::ratio<1, 10000000>>;
|
||||
constexpr TimeSpan(durationTicks ticks);
|
||||
durationTicks m_ticks;
|
||||
};
|
||||
|
||||
constexpr TimeSpan::TimeSpan(durationTicks ticks) : m_ticks(ticks) { }
|
||||
|
||||
inline TimeSpan TimeSpan::FromTicks(int64_t oneHundredNanosecondTicks) noexcept
|
||||
{
|
||||
durationTicks durationTicks{oneHundredNanosecondTicks};
|
||||
return {durationTicks};
|
||||
}
|
||||
|
||||
inline TimeSpan TimeSpan::FromMilliseconds(int64_t milliseconds) noexcept
|
||||
{
|
||||
std::chrono::milliseconds durationMs{milliseconds};
|
||||
return {std::chrono::duration_cast<durationTicks>(durationMs)};
|
||||
}
|
||||
|
||||
inline TimeSpan TimeSpan::FromSeconds(int64_t seconds) noexcept
|
||||
{
|
||||
std::chrono::seconds durationSec{seconds};
|
||||
return {std::chrono::duration_cast<durationTicks>(durationSec)};
|
||||
}
|
||||
|
||||
[[nodiscard]] inline int64_t TimeSpan::GetTotalMilliseconds() const noexcept
|
||||
{
|
||||
return std::chrono::duration_cast<std::chrono::milliseconds>(m_ticks).count();
|
||||
}
|
||||
|
||||
[[nodiscard]] inline int64_t TimeSpan::GetTotalSeconds() const noexcept
|
||||
{
|
||||
return std::chrono::duration_cast<std::chrono::seconds>(m_ticks).count();
|
||||
}
|
||||
|
||||
[[nodiscard]] inline int64_t TimeSpan::GetTotalTicks() const noexcept
|
||||
{
|
||||
return m_ticks.count();
|
||||
}
|
||||
|
||||
[[nodiscard]] inline double TimeSpan::GetMilliseconds() const noexcept
|
||||
{
|
||||
return
|
||||
std::chrono::duration_cast<std::chrono::duration<double, std::chrono::milliseconds::period>>(m_ticks).count();
|
||||
}
|
||||
|
||||
[[nodiscard]] inline double TimeSpan::GetMicroseconds() const noexcept
|
||||
{
|
||||
return std::chrono::duration_cast<std::chrono::duration<double, std::chrono::microseconds::period>>(m_ticks).count();
|
||||
}
|
||||
}
|
||||
44
src/Core/Core.Native/Utf8Span.h
Normal file
44
src/Core/Core.Native/Utf8Span.h
Normal file
@@ -0,0 +1,44 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
#include "ReadOnlySpan.h"
|
||||
|
||||
namespace cdb_core
|
||||
{
|
||||
struct Utf8Span final
|
||||
{
|
||||
Utf8Span() = delete;
|
||||
~Utf8Span() = delete;
|
||||
Utf8Span(const Utf8Span& other) = delete;
|
||||
Utf8Span(Utf8Span&& other) noexcept = delete;
|
||||
Utf8Span& operator=(const Utf8Span& other) = delete;
|
||||
Utf8Span& operator=(Utf8Span&& other) noexcept = delete;
|
||||
|
||||
/// <summary>Creates a <see cref="Utf8Span" /> without validating the underlying bytes.</summary>
|
||||
/// <param name="utf8Bytes">The bytes claiming to be UTF8.</param>
|
||||
/// <returns>A <see cref="Utf8Span" /> wrapping <paramref name="utf8Bytes" />.</returns>
|
||||
/// <remarks>
|
||||
/// This method is dangerous as consumers of the <see cref="Utf8Span" /> must assume the
|
||||
/// underlying bytes are indeed valid UTF8. The method should <bold>only</bold> be used when the UTF8
|
||||
/// sequence has already been externally valid or is known to be valid by construction.
|
||||
/// </remarks>
|
||||
constexpr static std::string_view UnsafeFromUtf8BytesNoValidation(const ReadOnlySpan<std::byte>& utf8Bytes) noexcept
|
||||
{
|
||||
return (utf8Bytes.IsEmpty())
|
||||
? std::string_view{}
|
||||
: std::string_view{reinterpret_cast<const char*>(&utf8Bytes[0]), utf8Bytes.Length()};
|
||||
}
|
||||
|
||||
/// <summary>The UTF8 byte sequence.</summary>
|
||||
constexpr static ReadOnlySpan<std::byte> GetSpan(const std::string_view utf8)
|
||||
{
|
||||
// ReSharper disable once CppCStyleCast
|
||||
return ReadOnlySpan<std::byte>{
|
||||
(const std::byte*)(utf8.data()), // NOLINT(clang-diagnostic-old-style-cast)
|
||||
static_cast<uint32_t>(utf8.size())
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
20
src/Core/Core.Native/framework.h
Normal file
20
src/Core/Core.Native/framework.h
Normal file
@@ -0,0 +1,20 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#define STRICT
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#define ENABLE_INTSAFE_SIGNED_FUNCTIONS
|
||||
#define NOMINMAX
|
||||
#include "Windows.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
using std::byte;
|
||||
64
src/Core/Core.Native/make_unique.h
Normal file
64
src/Core/Core.Native/make_unique.h
Normal file
@@ -0,0 +1,64 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
#include <memory>
|
||||
|
||||
namespace cdb_core
|
||||
{
|
||||
// forward declaration
|
||||
template<class F>
|
||||
struct make_unique_with_traits;
|
||||
|
||||
// function pointer
|
||||
template<class R, class Arg>
|
||||
struct make_unique_with_traits<R(*)(Arg)>
|
||||
{
|
||||
using return_type = R;
|
||||
using arg_type = Arg;
|
||||
};
|
||||
|
||||
// member function pointer
|
||||
template<class C, class R, class Arg>
|
||||
struct make_unique_with_traits<R(C::*)(Arg)> : make_unique_with_traits<R(*)(Arg)> {};
|
||||
|
||||
// const member function pointer
|
||||
template<class C, class R, class Arg>
|
||||
struct make_unique_with_traits<R(C::*)(Arg) const> : make_unique_with_traits<R(*)(Arg)> {};
|
||||
|
||||
/// <summary>Trait to derive return type and argument type from a generic callable.</summary>
|
||||
template<class TCallable>
|
||||
struct make_unique_with_traits
|
||||
{
|
||||
using call_type = make_unique_with_traits<decltype(&TCallable::operator())>;
|
||||
using return_type = typename call_type::return_type;
|
||||
using arg_type = std::remove_reference_t<typename call_type::arg_type>;
|
||||
};
|
||||
|
||||
// ReSharper disable once CppInconsistentNaming
|
||||
/// <summary>Makes a new object that uses delayed initialization.</summary>
|
||||
template<typename TCallable,
|
||||
typename Traits = make_unique_with_traits<TCallable>,
|
||||
typename T = typename Traits::arg_type,
|
||||
typename = std::enable_if_t<std::is_invocable_r_v<void, TCallable, T&>>>
|
||||
static std::unique_ptr<T> make_unique_with(TCallable&& initializer)
|
||||
{
|
||||
std::unique_ptr<T> p = std::make_unique<T>();
|
||||
initializer(*p);
|
||||
return std::move(p);
|
||||
}
|
||||
|
||||
// ReSharper disable once CppInconsistentNaming
|
||||
/// <summary>Makes a new object that uses delayed initialization.</summary>
|
||||
template<typename TCallable,
|
||||
typename Traits = make_unique_with_traits<TCallable>,
|
||||
typename T = typename Traits::arg_type,
|
||||
typename = std::enable_if_t<std::is_invocable_r_v<void, TCallable, T&>>>
|
||||
static T make_with(TCallable&& initializer)
|
||||
{
|
||||
T retval = T();
|
||||
initializer(retval);
|
||||
return std::move(retval);
|
||||
}
|
||||
}
|
||||
5
src/Core/Core.Native/pch.cpp
Normal file
5
src/Core/Core.Native/pch.cpp
Normal file
@@ -0,0 +1,5 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#include "pch.h"
|
||||
7
src/Core/Core.Native/pch.h
Normal file
7
src/Core/Core.Native/pch.h
Normal file
@@ -0,0 +1,7 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "framework.h"
|
||||
206
src/Core/Core.Native/ref_ptr.h
Normal file
206
src/Core/Core.Native/ref_ptr.h
Normal file
@@ -0,0 +1,206 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace cdb_core
|
||||
{
|
||||
// ReSharper disable once CppInconsistentNaming
|
||||
template<typename T>
|
||||
class ref_ptr final
|
||||
{
|
||||
public:
|
||||
|
||||
template<class... ArgsType>
|
||||
// ReSharper disable once CppInconsistentNaming
|
||||
[[nodiscard]] constexpr static ref_ptr<T> make(ArgsType&&... args);
|
||||
|
||||
constexpr ref_ptr() noexcept
|
||||
{
|
||||
m_p = nullptr;
|
||||
}
|
||||
|
||||
constexpr ref_ptr(T* lp) noexcept
|
||||
{
|
||||
m_p = lp;
|
||||
|
||||
if (m_p != nullptr)
|
||||
{
|
||||
m_p->AddRef();
|
||||
}
|
||||
}
|
||||
|
||||
constexpr ref_ptr(const ref_ptr<T>& lp) noexcept
|
||||
{
|
||||
m_p = lp.m_p;
|
||||
|
||||
if (m_p != nullptr)
|
||||
{
|
||||
m_p->AddRef();
|
||||
}
|
||||
}
|
||||
|
||||
constexpr ref_ptr(ref_ptr<T>&& lp) noexcept
|
||||
{
|
||||
m_p = lp.m_p;
|
||||
lp.m_p = nullptr;
|
||||
}
|
||||
|
||||
~ref_ptr() noexcept
|
||||
{
|
||||
if (m_p != nullptr)
|
||||
{
|
||||
m_p->Release();
|
||||
m_p = nullptr; // Make sure we AV in case someone is using CComObjectPtr after Release
|
||||
}
|
||||
}
|
||||
|
||||
constexpr const T& operator*() const noexcept
|
||||
{
|
||||
return *m_p;
|
||||
}
|
||||
|
||||
constexpr T& operator*() noexcept
|
||||
{
|
||||
return *m_p;
|
||||
}
|
||||
|
||||
constexpr const T* operator->() const noexcept
|
||||
{
|
||||
return m_p;
|
||||
}
|
||||
|
||||
constexpr T* operator->() noexcept
|
||||
{
|
||||
return m_p;
|
||||
}
|
||||
|
||||
constexpr bool operator!() const noexcept
|
||||
{
|
||||
return (m_p == nullptr);
|
||||
}
|
||||
|
||||
constexpr bool operator<(T* pT) const noexcept
|
||||
{
|
||||
return (m_p < pT);
|
||||
}
|
||||
|
||||
constexpr bool operator>(T* pT) const noexcept
|
||||
{
|
||||
return (m_p < pT);
|
||||
}
|
||||
|
||||
constexpr bool operator<=(T* pT) const noexcept
|
||||
{
|
||||
return (m_p <= pT);
|
||||
}
|
||||
|
||||
constexpr bool operator>=(T* pT) const noexcept
|
||||
{
|
||||
return (m_p < pT);
|
||||
}
|
||||
|
||||
constexpr bool operator==(T* pT) const noexcept
|
||||
{
|
||||
return (m_p == pT);
|
||||
}
|
||||
|
||||
constexpr bool operator!=(T* pT) const noexcept
|
||||
{
|
||||
return (m_p != pT);
|
||||
}
|
||||
|
||||
constexpr bool operator==(ref_ptr<T> pT) const noexcept
|
||||
{
|
||||
return (m_p == pT.m_p);
|
||||
}
|
||||
|
||||
constexpr bool operator!=(ref_ptr<T> pT) const noexcept
|
||||
{
|
||||
return (m_p != pT.m_p);
|
||||
}
|
||||
|
||||
constexpr void reset(T* lp) noexcept
|
||||
{
|
||||
if (m_p != lp)
|
||||
{
|
||||
if (lp != nullptr)
|
||||
{
|
||||
lp->AddRef();
|
||||
}
|
||||
|
||||
if (m_p != nullptr)
|
||||
{
|
||||
m_p->Release();
|
||||
}
|
||||
|
||||
m_p = lp;
|
||||
}
|
||||
}
|
||||
|
||||
// Release the interface and set to nullptr
|
||||
constexpr void reset() noexcept
|
||||
{
|
||||
T* pTemp = m_p;
|
||||
if (pTemp != nullptr)
|
||||
{
|
||||
m_p = nullptr;
|
||||
pTemp->Release();
|
||||
}
|
||||
}
|
||||
|
||||
constexpr ref_ptr& operator=(T* lp) noexcept
|
||||
{
|
||||
reset(lp);
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr ref_ptr& operator=(const ref_ptr<T>& lp) noexcept // NOLINT(bugprone-unhandled-self-assignment, cert-oop54-cpp)
|
||||
{
|
||||
reset(lp.m_p);
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr ref_ptr& operator=(ref_ptr<T>&& lp) noexcept
|
||||
{
|
||||
reset();
|
||||
m_p = lp.m_p;
|
||||
lp.m_p = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Attach to an existing interface (does not AddRef)
|
||||
constexpr void attach(T* p2) noexcept
|
||||
{
|
||||
if (m_p != nullptr)
|
||||
{
|
||||
m_p->Release();
|
||||
}
|
||||
m_p = p2;
|
||||
}
|
||||
|
||||
// Detach the interface (does not Release)
|
||||
constexpr T* release() noexcept
|
||||
{
|
||||
T* pt = m_p;
|
||||
m_p = nullptr;
|
||||
return pt;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr T* get() const noexcept
|
||||
{
|
||||
return m_p;
|
||||
}
|
||||
|
||||
private:
|
||||
T* m_p;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
template<class... ArgsType>
|
||||
[[nodiscard]] constexpr ref_ptr<T> ref_ptr<T>::make(ArgsType&&... args)
|
||||
{
|
||||
return std::move(ref_ptr<T>(new T(std::forward<ArgsType>(args)...)));
|
||||
}
|
||||
}
|
||||
142
src/Core/Core.Native/tla.h
Normal file
142
src/Core/Core.Native/tla.h
Normal file
@@ -0,0 +1,142 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
#include <type_traits>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include <forward_list>
|
||||
#include <list>
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <unordered_set>
|
||||
#include <unordered_map>
|
||||
#include <stack>
|
||||
#include "Contract.h"
|
||||
|
||||
namespace tla
|
||||
{
|
||||
template<typename T>
|
||||
struct allocator final : private std::allocator<T>
|
||||
{
|
||||
static_assert(!std::is_const_v<T>, "The C++ Standard forbids containers of const elements "
|
||||
"because allocator<const T> is ill-formed.");
|
||||
|
||||
using value_type = T;
|
||||
using pointer = T*;
|
||||
using const_pointer = const T*;
|
||||
using reference = T&;
|
||||
using const_reference = const T&;
|
||||
|
||||
using size_type = size_t;
|
||||
using difference_type = ptrdiff_t;
|
||||
|
||||
using propagate_on_container_move_assignment = std::true_type;
|
||||
using is_always_equal = std::true_type;
|
||||
|
||||
[[nodiscard]] constexpr allocator() noexcept = default;
|
||||
~allocator() noexcept = default;
|
||||
[[nodiscard]] constexpr allocator(const allocator& other) noexcept = default;
|
||||
[[nodiscard]] constexpr allocator(allocator&& other) noexcept = default;
|
||||
constexpr allocator& operator=(const allocator& other) noexcept = default;
|
||||
constexpr allocator& operator=(allocator&& other) noexcept = default;
|
||||
|
||||
template<class U>
|
||||
struct rebind
|
||||
{
|
||||
using other = allocator<U>;
|
||||
};
|
||||
|
||||
template<class U>
|
||||
[[nodiscard]] constexpr allocator(const allocator<U>&) noexcept {}
|
||||
|
||||
[[nodiscard]] T* allocate(const size_t n) noexcept
|
||||
{
|
||||
try
|
||||
{
|
||||
return std::allocator<T>::allocate(n);
|
||||
}
|
||||
catch (std::bad_alloc&)
|
||||
{
|
||||
cdb_core::Contract::Fail("Out of memory.");
|
||||
}
|
||||
}
|
||||
|
||||
void deallocate(T* const p, const size_t n) noexcept
|
||||
{
|
||||
std::allocator<T>::deallocate(p, n);
|
||||
}
|
||||
};
|
||||
|
||||
using string = std::basic_string<char, std::char_traits<char>, tla::allocator<char>>;
|
||||
using string_view = std::string_view;
|
||||
using wstring = std::basic_string<wchar_t, std::char_traits<wchar_t>, tla::allocator<wchar_t>>;
|
||||
using wstring_view = std::wstring_view;
|
||||
|
||||
inline namespace literals
|
||||
{
|
||||
inline namespace string_literals
|
||||
{
|
||||
[[nodiscard]] inline string operator"" _s(const char* _Str, size_t _Len)
|
||||
{
|
||||
return string(_Str, _Len);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline wstring operator"" _s(const wchar_t* _Str, size_t _Len)
|
||||
{
|
||||
return wstring(_Str, _Len);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr string_view operator"" _sv(const char* _Str, size_t _Len) noexcept
|
||||
{
|
||||
return string_view(_Str, _Len);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr wstring_view operator"" _sv(const wchar_t* _Str, size_t _Len) noexcept
|
||||
{
|
||||
return wstring_view(_Str, _Len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
using vector = std::vector<T, tla::allocator<T>>;
|
||||
|
||||
template<typename T>
|
||||
using deque = std::deque<T, tla::allocator<T>>;
|
||||
|
||||
template<typename T, typename Container = deque<T>>
|
||||
using stack = std::stack<T, Container>;
|
||||
|
||||
template<typename T>
|
||||
using forward_list = std::forward_list<T, tla::allocator<T>>;
|
||||
|
||||
template<typename T>
|
||||
using list = std::list<T, tla::allocator<T>>;
|
||||
|
||||
template<class Key, class Compare = std::less<Key>>
|
||||
using set = std::set<Key, Compare, tla::allocator<Key>>;
|
||||
|
||||
template<class Key, class T, class Compare = std::less<Key>>
|
||||
using map = std::map<Key, T, Compare, tla::allocator<std::pair<const Key, T>>>;
|
||||
|
||||
template<class Key, class Compare = std::less<Key>>
|
||||
using multiset = std::multiset<Key, Compare, tla::allocator<Key>>;
|
||||
|
||||
template<class Key, class T, class Compare = std::less<Key>>
|
||||
using multimap = std::multimap<Key, T, Compare, tla::allocator<std::pair<const Key, T>>>;
|
||||
|
||||
template<class Key, class Hash = std::hash<Key>, class Pred = std::equal_to<Key>>
|
||||
using unordered_set = std::unordered_set<Key, Hash, Pred, tla::allocator<Key>>;
|
||||
|
||||
template<class Key, class T, class Hash = std::hash<Key>, class Pred = std::equal_to<Key>>
|
||||
using unordered_map = std::unordered_map<Key, T, Hash, Pred, tla::allocator<std::pair<const Key, T>>>;
|
||||
|
||||
template<class Key, class Hash = std::hash<Key>, class Pred = std::equal_to<Key>>
|
||||
using unordered_multiset = std::unordered_multiset<Key, Hash, Pred, tla::allocator<Key>>;
|
||||
|
||||
template<class Key, class T, class Hash = std::hash<Key>, class Pred = std::equal_to<Key>>
|
||||
using unordered_multimap = std::unordered_multimap<Key, T, Hash, Pred, tla::allocator<std::pair<const Key, T>>>;
|
||||
}
|
||||
65
src/Core/Core/ClockResolution.cs
Normal file
65
src/Core/Core/ClockResolution.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Azure.Cosmos.Core
|
||||
{
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
|
||||
public sealed class ClockResolution : IDisposable
|
||||
{
|
||||
private static readonly Timecaps TimeCapabilities;
|
||||
private readonly TimeSpan period;
|
||||
private int disposed;
|
||||
|
||||
static ClockResolution()
|
||||
{
|
||||
int result = ClockResolution.TimeGetDevCaps(ref ClockResolution.TimeCapabilities, Marshal.SizeOf(typeof(Timecaps)));
|
||||
Contract.Assert(result == 0);
|
||||
}
|
||||
|
||||
public ClockResolution(TimeSpan period)
|
||||
{
|
||||
int millis = Convert.ToInt32(period.TotalMilliseconds);
|
||||
millis = Math.Min(Math.Max(ClockResolution.TimeCapabilities.PeriodMin, millis), ClockResolution.TimeCapabilities.PeriodMax);
|
||||
|
||||
int result = ClockResolution.TimeBeginPeriod(millis);
|
||||
Contract.Assert(result == 0);
|
||||
this.period = TimeSpan.FromMilliseconds(millis);
|
||||
}
|
||||
|
||||
public static TimeSpan MinimumPeriod => TimeSpan.FromMilliseconds(ClockResolution.TimeCapabilities.PeriodMin);
|
||||
|
||||
public static TimeSpan MaximumPeriod => TimeSpan.FromMilliseconds(ClockResolution.TimeCapabilities.PeriodMax);
|
||||
|
||||
public TimeSpan Period => this.period;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (Interlocked.CompareExchange(ref this.disposed, 1, 0) == 0)
|
||||
{
|
||||
int millis = Convert.ToInt32(this.period.TotalMilliseconds);
|
||||
_ = ClockResolution.TimeEndPeriod(millis);
|
||||
}
|
||||
}
|
||||
|
||||
[DllImport("winmm.dll", EntryPoint = "timeGetDevCaps")]
|
||||
private static extern int TimeGetDevCaps(ref Timecaps ptc, int cbtc);
|
||||
|
||||
[DllImport("winmm.dll", EntryPoint = "timeBeginPeriod")]
|
||||
private static extern int TimeBeginPeriod(int uPeriod);
|
||||
|
||||
[DllImport("winmm.dll", EntryPoint = "timeEndPeriod")]
|
||||
private static extern int TimeEndPeriod(int uPeriod);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct Timecaps
|
||||
{
|
||||
public readonly int PeriodMin;
|
||||
|
||||
public readonly int PeriodMax;
|
||||
}
|
||||
}
|
||||
}
|
||||
281
src/Core/Core/Collections/Trie.cs
Normal file
281
src/Core/Core/Collections/Trie.cs
Normal file
@@ -0,0 +1,281 @@
|
||||
namespace Microsoft.Azure.Cosmos.Core.Collections
|
||||
{
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
/// <summary>A trie, otherwise known as a prefix tree.</summary>
|
||||
/// <remarks>
|
||||
/// A trie is a map from key to value. Keys are formed by a sequence of symbols in some alphabet.
|
||||
/// Values are merely round-tripped by the data structure and are not otherwise interpreted.
|
||||
/// <p>
|
||||
/// Keys and symbols are compared using the binary collation (i.e. memcmp). Symbols must therefore
|
||||
/// be an unmanaged type (i.e. convertible to byte).
|
||||
/// </p>
|
||||
/// <p>See https://en.wikipedia.org/wiki/Trie for a detailed description of the Trie data structure.</p>
|
||||
/// </remarks>
|
||||
/// <typeparam name="TSymbol">The type of the individual elements that form a key.</typeparam>
|
||||
/// <typeparam name="TValue">The type of the value.</typeparam>
|
||||
public class Trie<TSymbol, TValue>
|
||||
where TSymbol : unmanaged
|
||||
{
|
||||
/// <summary>The smallest capacity allocated.</summary>
|
||||
private const int MinCapacity = 20;
|
||||
|
||||
/// <summary>All available allocated storage space.</summary>
|
||||
private Memory<Node> capacity;
|
||||
|
||||
/// <summary>
|
||||
/// A view of a subset of <see cref="capacity" /> that forms the current active nodes of the
|
||||
/// tree.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The trie is encoded as a variable branching n-ary tree of <see cref="Node" />'s. The tree begins
|
||||
/// at the root (which has no symbol). Each non-root node encodes a single symbol. The path from the
|
||||
/// root to a node encodes a unique sequence of symbols. If the sequence represents a key within the
|
||||
/// trie then its terminal flag is set and it also stores a value. Interior nodes of the tree may be
|
||||
/// terminal (implying that two keys, one of which is a proper prefix of the other, both exist within
|
||||
/// the trie).
|
||||
/// <p>
|
||||
/// Children of a node form a linked list, the head of which is pointed to by the
|
||||
/// <see cref="Node.Child" /> and the siblings of which are linked together via
|
||||
/// <see cref="Node.Next" />.
|
||||
/// </p>
|
||||
/// <p>
|
||||
/// The 0'th element of this view is always the root node which will store the value for the empty
|
||||
/// sequence (if it has been added). Both the children linked list and the n-ary tree left nodes are
|
||||
/// terminated by the address 0, which can never be a valid child because the root is always in
|
||||
/// position 0.
|
||||
/// </p>
|
||||
/// </remarks>
|
||||
private Memory<Node> tree;
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="Trie{TSymbol, TValue}" /> class.</summary>
|
||||
/// <param name="initialCapacity">The (optional) initial capacity.</param>
|
||||
public Trie(int initialCapacity = 0)
|
||||
{
|
||||
Contract.Requires(initialCapacity >= 0);
|
||||
|
||||
// Allocate some initial capacity.
|
||||
this.capacity = new Node[initialCapacity < Trie<TSymbol, TValue>.MinCapacity ? Trie<TSymbol, TValue>.MinCapacity : initialCapacity];
|
||||
|
||||
// Insert the root. The root will store the value for the empty sequence.
|
||||
this.tree = this.capacity.Slice(0, 1);
|
||||
}
|
||||
|
||||
/// <summary>Add an item to the trie.</summary>
|
||||
/// <param name="key">The key to be added.</param>
|
||||
/// <param name="value">The value to be associated with the key.</param>
|
||||
/// <returns>True if the item was added, false if the key already exists in the trie.</returns>
|
||||
public bool TryAdd(ReadOnlySpan<TSymbol> key, TValue value)
|
||||
{
|
||||
int cur = 0;
|
||||
foreach (TSymbol s in key)
|
||||
{
|
||||
if (this.FindChild(s, cur, out int child))
|
||||
{
|
||||
cur = child;
|
||||
}
|
||||
else
|
||||
{
|
||||
int address = this.AllocNode(s);
|
||||
if (child == 0)
|
||||
{
|
||||
this.tree.Span[cur].Child = address;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.tree.Span[child].Next = address;
|
||||
}
|
||||
|
||||
cur = address;
|
||||
}
|
||||
}
|
||||
|
||||
if ((this.tree.Span[cur].Flags & Flags.Terminal) != 0)
|
||||
{
|
||||
// Already exists.
|
||||
return false;
|
||||
}
|
||||
|
||||
this.tree.Span[cur].Flags |= Flags.Terminal;
|
||||
this.tree.Span[cur].Value = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>Add an item to the trie. if the key exists, replace the existing item.</summary>
|
||||
/// <param name="key">The key to be added.</param>
|
||||
/// <param name="value">The value to be associated with the key.</param>
|
||||
public void AddOrUpdate(ReadOnlySpan<TSymbol> key, TValue value)
|
||||
{
|
||||
int cur = 0;
|
||||
foreach (TSymbol s in key)
|
||||
{
|
||||
if (this.FindChild(s, cur, out int child))
|
||||
{
|
||||
cur = child;
|
||||
}
|
||||
else
|
||||
{
|
||||
int address = this.AllocNode(s);
|
||||
if (child == 0)
|
||||
{
|
||||
this.tree.Span[cur].Child = address;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.tree.Span[child].Next = address;
|
||||
}
|
||||
|
||||
cur = address;
|
||||
}
|
||||
}
|
||||
|
||||
this.tree.Span[cur].Flags |= Flags.Terminal;
|
||||
this.tree.Span[cur].Value = value;
|
||||
}
|
||||
|
||||
/// <summary>Find an item within a trie.</summary>
|
||||
/// <param name="key">The key to be added.</param>
|
||||
/// <param name="value">
|
||||
/// If <paramref name="key" /> was found then the value to be associated with the
|
||||
/// key, otherwise default.
|
||||
/// </param>
|
||||
/// <returns>True if the item was found, false otherwise.</returns>
|
||||
public bool TryGetValue(ReadOnlySpan<TSymbol> key, out TValue value)
|
||||
{
|
||||
int cur = 0;
|
||||
foreach (TSymbol s in key)
|
||||
{
|
||||
if (!this.FindChild(s, cur, out cur))
|
||||
{
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ((this.tree.Span[cur].Flags & Flags.Terminal) == 0)
|
||||
{
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
value = this.tree.Span[cur].Value;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>Safe memory compare of <see cref="TSymbol" />.</summary>
|
||||
/// <param name="left">The left argument.</param>
|
||||
/// <param name="right">The right argument.</param>
|
||||
/// <returns>
|
||||
/// True if the byte sequence of <paramref name="left" /> exactly matches the byte sequence of
|
||||
/// <paramref name="right" />.
|
||||
/// </returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static unsafe bool SymbolEquals(TSymbol left, TSymbol right)
|
||||
{
|
||||
ReadOnlySpan<byte> p = new ReadOnlySpan<byte>(&left, sizeof(TSymbol));
|
||||
ReadOnlySpan<byte> q = new ReadOnlySpan<byte>(&right, sizeof(TSymbol));
|
||||
return p.SequenceEqual(q);
|
||||
}
|
||||
|
||||
/// <summary>Add a new unattached node to the tree.</summary>
|
||||
/// <param name="s">The symbol for the node.</param>
|
||||
/// <returns>The address of the new node.</returns>
|
||||
private int AllocNode(TSymbol s)
|
||||
{
|
||||
int node = this.tree.Length;
|
||||
if (this.tree.Length == this.capacity.Length)
|
||||
{
|
||||
this.capacity = new Node[this.capacity.Length * 2];
|
||||
this.tree.CopyTo(this.capacity);
|
||||
}
|
||||
|
||||
this.tree = this.capacity.Slice(0, this.tree.Length + 1);
|
||||
|
||||
this.tree.Span[node] = new Node(Flags.None, s, 0, 0, default);
|
||||
return node;
|
||||
}
|
||||
|
||||
/// <summary>Find a child node of <paramref name="parent" /> with a matching symbol.</summary>
|
||||
/// <param name="s">The symbol to match.</param>
|
||||
/// <param name="parent">The address of the parent.</param>
|
||||
/// <param name="child">
|
||||
/// If successful, the address of the child, otherwise the address of the previous
|
||||
/// in the child link list where a new child should be added. 0 if the parent has no children.
|
||||
/// </param>
|
||||
/// <returns>True if a child with matching symbol was found, false otherwise.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private bool FindChild(TSymbol s, int parent, out int child)
|
||||
{
|
||||
int last = 0;
|
||||
child = this.tree.Span[parent].Child;
|
||||
while (child != 0)
|
||||
{
|
||||
if (typeof(TSymbol) == typeof(byte))
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
TSymbol t = this.tree.Span[child].Symbol;
|
||||
if (*(byte*)&t == *(byte*)&s)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (Trie<TSymbol, TValue>.SymbolEquals(this.tree.Span[child].Symbol, s))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
last = child;
|
||||
child = this.tree.Span[child].Next;
|
||||
}
|
||||
|
||||
child = last;
|
||||
return false;
|
||||
}
|
||||
|
||||
private struct Node
|
||||
{
|
||||
/// <summary>Flags indicating characteristics of the node.</summary>
|
||||
public Flags Flags;
|
||||
|
||||
/// <summary>The symbol forming the key prefix added by this node.</summary>
|
||||
public readonly TSymbol Symbol;
|
||||
|
||||
/// <summary>A pointer to the index of the first child node of this node.</summary>
|
||||
public int Child;
|
||||
|
||||
/// <summary>
|
||||
/// A pointer to the index of the next sibling of this node (next child of this node's
|
||||
/// parent).
|
||||
/// </summary>
|
||||
public int Next;
|
||||
|
||||
/// <summary>
|
||||
/// If the current node is a terminal, the value associated with current prefix, otherwise
|
||||
/// undefined.
|
||||
/// </summary>
|
||||
public TValue Value;
|
||||
|
||||
public Node(Flags flags, TSymbol symbol, int child, int next, TValue value)
|
||||
{
|
||||
this.Flags = flags;
|
||||
this.Symbol = symbol;
|
||||
this.Child = child;
|
||||
this.Next = next;
|
||||
this.Value = value;
|
||||
}
|
||||
}
|
||||
|
||||
[Flags]
|
||||
private enum Flags
|
||||
{
|
||||
/// <summary>Interior node within the trie that carries no value.</summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>Either interior or left node within the trie that has a value.</summary>
|
||||
Terminal = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
94
src/Core/Core/Contract.cs
Normal file
94
src/Core/Core/Contract.cs
Normal file
@@ -0,0 +1,94 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Azure.Cosmos.Core
|
||||
{
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
/// <summary>
|
||||
/// Used to check contract invariants in either Debug-only (Assert)
|
||||
/// or Debug and Release (Requires, Invariant, etc.). Contract failures
|
||||
/// will attempt to break into the debugger, will trace the error, and
|
||||
/// will throw a ContractViolationException.
|
||||
/// </summary>
|
||||
[ExcludeFromCodeCoverage]
|
||||
public static class Contract
|
||||
{
|
||||
[Conditional("DEBUG")]
|
||||
public static void Assert(bool condition)
|
||||
{
|
||||
if (!condition)
|
||||
{
|
||||
Contract.Fail("Assert", string.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
public static void Assert(bool condition, string message)
|
||||
{
|
||||
if (!condition)
|
||||
{
|
||||
Contract.Fail("Assert", message);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Requires(bool condition)
|
||||
{
|
||||
if (!condition)
|
||||
{
|
||||
Contract.Fail("Requires", string.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Requires(bool condition, string message)
|
||||
{
|
||||
if (!condition)
|
||||
{
|
||||
Contract.Fail("Requires", message);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Invariant(bool condition)
|
||||
{
|
||||
if (!condition)
|
||||
{
|
||||
Contract.Fail("Invariant", string.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Invariant(bool condition, string message)
|
||||
{
|
||||
if (!condition)
|
||||
{
|
||||
Contract.Fail("Invariant", message);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Fail()
|
||||
{
|
||||
Contract.Fail("Fail", string.Empty);
|
||||
}
|
||||
|
||||
public static void Fail(string message)
|
||||
{
|
||||
Contract.Fail("Fail", message);
|
||||
}
|
||||
|
||||
private static void Fail(string api, string message)
|
||||
{
|
||||
StackTrace stack = new StackTrace(2, true);
|
||||
string error = $"{api} Failure: {message}\n\tStack: {stack}";
|
||||
Trace.TraceError(error);
|
||||
Trace.Flush();
|
||||
|
||||
// Try breaking into the debugger if attached.
|
||||
Debugger.Break();
|
||||
|
||||
// This exception should NEVER be caught.
|
||||
// TODO: make this uncatchable.
|
||||
throw new ContractViolationException(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
34
src/Core/Core/ContractViolationException.cs
Normal file
34
src/Core/Core/ContractViolationException.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Azure.Cosmos.Core
|
||||
{
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
[Serializable]
|
||||
[ExcludeFromCodeCoverage]
|
||||
public class ContractViolationException : Exception
|
||||
{
|
||||
public ContractViolationException()
|
||||
{
|
||||
}
|
||||
|
||||
public ContractViolationException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public ContractViolationException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
}
|
||||
|
||||
protected ContractViolationException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
526
src/Core/Core/Crc32.cs
Normal file
526
src/Core/Core/Crc32.cs
Normal file
@@ -0,0 +1,526 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Azure.Cosmos.Core
|
||||
{
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
//
|
||||
// File implements Slicing-by-8 CRC Generation, as described in
|
||||
// "Novel Table Lookup-Based Algorithms for High-Performance CRC Generation"
|
||||
// IEEE TRANSACTIONS ON COMPUTERS, VOL. 57, NO. 11, NOVEMBER 2008
|
||||
//
|
||||
// Copyright(c) 2004-2006 Intel Corporation - All Rights Reserved
|
||||
//
|
||||
// This software program is licensed subject to the BSD License,
|
||||
// available at http://www.opensource.org/licenses/bsd-license.html.
|
||||
|
||||
/// <summary>
|
||||
/// CRC Generator.
|
||||
/// </summary>
|
||||
public static class Crc32
|
||||
{
|
||||
// Generated tables for managed crc calculation.
|
||||
// Each table n (starting at 0) contains remainders from the long division of
|
||||
// all possible byte values, shifted by an offset of (n * 4 bits).
|
||||
// The divisor used is the crc32 standard polynomial 0xEDB88320
|
||||
// Please see cited paper for more details.
|
||||
private static readonly uint[] CrcTable0 = new uint[256]
|
||||
{
|
||||
0x00000000u, 0x77073096u, 0xee0e612cu, 0x990951bau, 0x076dc419u,
|
||||
0x706af48fu, 0xe963a535u, 0x9e6495a3u, 0x0edb8832u, 0x79dcb8a4u,
|
||||
0xe0d5e91eu, 0x97d2d988u, 0x09b64c2bu, 0x7eb17cbdu, 0xe7b82d07u,
|
||||
0x90bf1d91u, 0x1db71064u, 0x6ab020f2u, 0xf3b97148u, 0x84be41deu,
|
||||
0x1adad47du, 0x6ddde4ebu, 0xf4d4b551u, 0x83d385c7u, 0x136c9856u,
|
||||
0x646ba8c0u, 0xfd62f97au, 0x8a65c9ecu, 0x14015c4fu, 0x63066cd9u,
|
||||
0xfa0f3d63u, 0x8d080df5u, 0x3b6e20c8u, 0x4c69105eu, 0xd56041e4u,
|
||||
0xa2677172u, 0x3c03e4d1u, 0x4b04d447u, 0xd20d85fdu, 0xa50ab56bu,
|
||||
0x35b5a8fau, 0x42b2986cu, 0xdbbbc9d6u, 0xacbcf940u, 0x32d86ce3u,
|
||||
0x45df5c75u, 0xdcd60dcfu, 0xabd13d59u, 0x26d930acu, 0x51de003au,
|
||||
0xc8d75180u, 0xbfd06116u, 0x21b4f4b5u, 0x56b3c423u, 0xcfba9599u,
|
||||
0xb8bda50fu, 0x2802b89eu, 0x5f058808u, 0xc60cd9b2u, 0xb10be924u,
|
||||
0x2f6f7c87u, 0x58684c11u, 0xc1611dabu, 0xb6662d3du, 0x76dc4190u,
|
||||
0x01db7106u, 0x98d220bcu, 0xefd5102au, 0x71b18589u, 0x06b6b51fu,
|
||||
0x9fbfe4a5u, 0xe8b8d433u, 0x7807c9a2u, 0x0f00f934u, 0x9609a88eu,
|
||||
0xe10e9818u, 0x7f6a0dbbu, 0x086d3d2du, 0x91646c97u, 0xe6635c01u,
|
||||
0x6b6b51f4u, 0x1c6c6162u, 0x856530d8u, 0xf262004eu, 0x6c0695edu,
|
||||
0x1b01a57bu, 0x8208f4c1u, 0xf50fc457u, 0x65b0d9c6u, 0x12b7e950u,
|
||||
0x8bbeb8eau, 0xfcb9887cu, 0x62dd1ddfu, 0x15da2d49u, 0x8cd37cf3u,
|
||||
0xfbd44c65u, 0x4db26158u, 0x3ab551ceu, 0xa3bc0074u, 0xd4bb30e2u,
|
||||
0x4adfa541u, 0x3dd895d7u, 0xa4d1c46du, 0xd3d6f4fbu, 0x4369e96au,
|
||||
0x346ed9fcu, 0xad678846u, 0xda60b8d0u, 0x44042d73u, 0x33031de5u,
|
||||
0xaa0a4c5fu, 0xdd0d7cc9u, 0x5005713cu, 0x270241aau, 0xbe0b1010u,
|
||||
0xc90c2086u, 0x5768b525u, 0x206f85b3u, 0xb966d409u, 0xce61e49fu,
|
||||
0x5edef90eu, 0x29d9c998u, 0xb0d09822u, 0xc7d7a8b4u, 0x59b33d17u,
|
||||
0x2eb40d81u, 0xb7bd5c3bu, 0xc0ba6cadu, 0xedb88320u, 0x9abfb3b6u,
|
||||
0x03b6e20cu, 0x74b1d29au, 0xead54739u, 0x9dd277afu, 0x04db2615u,
|
||||
0x73dc1683u, 0xe3630b12u, 0x94643b84u, 0x0d6d6a3eu, 0x7a6a5aa8u,
|
||||
0xe40ecf0bu, 0x9309ff9du, 0x0a00ae27u, 0x7d079eb1u, 0xf00f9344u,
|
||||
0x8708a3d2u, 0x1e01f268u, 0x6906c2feu, 0xf762575du, 0x806567cbu,
|
||||
0x196c3671u, 0x6e6b06e7u, 0xfed41b76u, 0x89d32be0u, 0x10da7a5au,
|
||||
0x67dd4accu, 0xf9b9df6fu, 0x8ebeeff9u, 0x17b7be43u, 0x60b08ed5u,
|
||||
0xd6d6a3e8u, 0xa1d1937eu, 0x38d8c2c4u, 0x4fdff252u, 0xd1bb67f1u,
|
||||
0xa6bc5767u, 0x3fb506ddu, 0x48b2364bu, 0xd80d2bdau, 0xaf0a1b4cu,
|
||||
0x36034af6u, 0x41047a60u, 0xdf60efc3u, 0xa867df55u, 0x316e8eefu,
|
||||
0x4669be79u, 0xcb61b38cu, 0xbc66831au, 0x256fd2a0u, 0x5268e236u,
|
||||
0xcc0c7795u, 0xbb0b4703u, 0x220216b9u, 0x5505262fu, 0xc5ba3bbeu,
|
||||
0xb2bd0b28u, 0x2bb45a92u, 0x5cb36a04u, 0xc2d7ffa7u, 0xb5d0cf31u,
|
||||
0x2cd99e8bu, 0x5bdeae1du, 0x9b64c2b0u, 0xec63f226u, 0x756aa39cu,
|
||||
0x026d930au, 0x9c0906a9u, 0xeb0e363fu, 0x72076785u, 0x05005713u,
|
||||
0x95bf4a82u, 0xe2b87a14u, 0x7bb12baeu, 0x0cb61b38u, 0x92d28e9bu,
|
||||
0xe5d5be0du, 0x7cdcefb7u, 0x0bdbdf21u, 0x86d3d2d4u, 0xf1d4e242u,
|
||||
0x68ddb3f8u, 0x1fda836eu, 0x81be16cdu, 0xf6b9265bu, 0x6fb077e1u,
|
||||
0x18b74777u, 0x88085ae6u, 0xff0f6a70u, 0x66063bcau, 0x11010b5cu,
|
||||
0x8f659effu, 0xf862ae69u, 0x616bffd3u, 0x166ccf45u, 0xa00ae278u,
|
||||
0xd70dd2eeu, 0x4e048354u, 0x3903b3c2u, 0xa7672661u, 0xd06016f7u,
|
||||
0x4969474du, 0x3e6e77dbu, 0xaed16a4au, 0xd9d65adcu, 0x40df0b66u,
|
||||
0x37d83bf0u, 0xa9bcae53u, 0xdebb9ec5u, 0x47b2cf7fu, 0x30b5ffe9u,
|
||||
0xbdbdf21cu, 0xcabac28au, 0x53b39330u, 0x24b4a3a6u, 0xbad03605u,
|
||||
0xcdd70693u, 0x54de5729u, 0x23d967bfu, 0xb3667a2eu, 0xc4614ab8u,
|
||||
0x5d681b02u, 0x2a6f2b94u, 0xb40bbe37u, 0xc30c8ea1u, 0x5a05df1bu,
|
||||
0x2d02ef8du,
|
||||
};
|
||||
|
||||
private static readonly uint[] CrcTable1 = new uint[256]
|
||||
{
|
||||
0x00000000u, 0x191B3141u, 0x32366282u, 0x2B2D53C3u, 0x646CC504u,
|
||||
0x7D77F445u, 0x565AA786u, 0x4F4196C7u, 0xC8D98A08u, 0xD1C2BB49u,
|
||||
0xFAEFE88Au, 0xE3F4D9CBu, 0xACB54F0Cu, 0xB5AE7E4Du, 0x9E832D8Eu,
|
||||
0x87981CCFu, 0x4AC21251u, 0x53D92310u, 0x78F470D3u, 0x61EF4192u,
|
||||
0x2EAED755u, 0x37B5E614u, 0x1C98B5D7u, 0x05838496u, 0x821B9859u,
|
||||
0x9B00A918u, 0xB02DFADBu, 0xA936CB9Au, 0xE6775D5Du, 0xFF6C6C1Cu,
|
||||
0xD4413FDFu, 0xCD5A0E9Eu, 0x958424A2u, 0x8C9F15E3u, 0xA7B24620u,
|
||||
0xBEA97761u, 0xF1E8E1A6u, 0xE8F3D0E7u, 0xC3DE8324u, 0xDAC5B265u,
|
||||
0x5D5DAEAAu, 0x44469FEBu, 0x6F6BCC28u, 0x7670FD69u, 0x39316BAEu,
|
||||
0x202A5AEFu, 0x0B07092Cu, 0x121C386Du, 0xDF4636F3u, 0xC65D07B2u,
|
||||
0xED705471u, 0xF46B6530u, 0xBB2AF3F7u, 0xA231C2B6u, 0x891C9175u,
|
||||
0x9007A034u, 0x179FBCFBu, 0x0E848DBAu, 0x25A9DE79u, 0x3CB2EF38u,
|
||||
0x73F379FFu, 0x6AE848BEu, 0x41C51B7Du, 0x58DE2A3Cu, 0xF0794F05u,
|
||||
0xE9627E44u, 0xC24F2D87u, 0xDB541CC6u, 0x94158A01u, 0x8D0EBB40u,
|
||||
0xA623E883u, 0xBF38D9C2u, 0x38A0C50Du, 0x21BBF44Cu, 0x0A96A78Fu,
|
||||
0x138D96CEu, 0x5CCC0009u, 0x45D73148u, 0x6EFA628Bu, 0x77E153CAu,
|
||||
0xBABB5D54u, 0xA3A06C15u, 0x888D3FD6u, 0x91960E97u, 0xDED79850u,
|
||||
0xC7CCA911u, 0xECE1FAD2u, 0xF5FACB93u, 0x7262D75Cu, 0x6B79E61Du,
|
||||
0x4054B5DEu, 0x594F849Fu, 0x160E1258u, 0x0F152319u, 0x243870DAu,
|
||||
0x3D23419Bu, 0x65FD6BA7u, 0x7CE65AE6u, 0x57CB0925u, 0x4ED03864u,
|
||||
0x0191AEA3u, 0x188A9FE2u, 0x33A7CC21u, 0x2ABCFD60u, 0xAD24E1AFu,
|
||||
0xB43FD0EEu, 0x9F12832Du, 0x8609B26Cu, 0xC94824ABu, 0xD05315EAu,
|
||||
0xFB7E4629u, 0xE2657768u, 0x2F3F79F6u, 0x362448B7u, 0x1D091B74u,
|
||||
0x04122A35u, 0x4B53BCF2u, 0x52488DB3u, 0x7965DE70u, 0x607EEF31u,
|
||||
0xE7E6F3FEu, 0xFEFDC2BFu, 0xD5D0917Cu, 0xCCCBA03Du, 0x838A36FAu,
|
||||
0x9A9107BBu, 0xB1BC5478u, 0xA8A76539u, 0x3B83984Bu, 0x2298A90Au,
|
||||
0x09B5FAC9u, 0x10AECB88u, 0x5FEF5D4Fu, 0x46F46C0Eu, 0x6DD93FCDu,
|
||||
0x74C20E8Cu, 0xF35A1243u, 0xEA412302u, 0xC16C70C1u, 0xD8774180u,
|
||||
0x9736D747u, 0x8E2DE606u, 0xA500B5C5u, 0xBC1B8484u, 0x71418A1Au,
|
||||
0x685ABB5Bu, 0x4377E898u, 0x5A6CD9D9u, 0x152D4F1Eu, 0x0C367E5Fu,
|
||||
0x271B2D9Cu, 0x3E001CDDu, 0xB9980012u, 0xA0833153u, 0x8BAE6290u,
|
||||
0x92B553D1u, 0xDDF4C516u, 0xC4EFF457u, 0xEFC2A794u, 0xF6D996D5u,
|
||||
0xAE07BCE9u, 0xB71C8DA8u, 0x9C31DE6Bu, 0x852AEF2Au, 0xCA6B79EDu,
|
||||
0xD37048ACu, 0xF85D1B6Fu, 0xE1462A2Eu, 0x66DE36E1u, 0x7FC507A0u,
|
||||
0x54E85463u, 0x4DF36522u, 0x02B2F3E5u, 0x1BA9C2A4u, 0x30849167u,
|
||||
0x299FA026u, 0xE4C5AEB8u, 0xFDDE9FF9u, 0xD6F3CC3Au, 0xCFE8FD7Bu,
|
||||
0x80A96BBCu, 0x99B25AFDu, 0xB29F093Eu, 0xAB84387Fu, 0x2C1C24B0u,
|
||||
0x350715F1u, 0x1E2A4632u, 0x07317773u, 0x4870E1B4u, 0x516BD0F5u,
|
||||
0x7A468336u, 0x635DB277u, 0xCBFAD74Eu, 0xD2E1E60Fu, 0xF9CCB5CCu,
|
||||
0xE0D7848Du, 0xAF96124Au, 0xB68D230Bu, 0x9DA070C8u, 0x84BB4189u,
|
||||
0x03235D46u, 0x1A386C07u, 0x31153FC4u, 0x280E0E85u, 0x674F9842u,
|
||||
0x7E54A903u, 0x5579FAC0u, 0x4C62CB81u, 0x8138C51Fu, 0x9823F45Eu,
|
||||
0xB30EA79Du, 0xAA1596DCu, 0xE554001Bu, 0xFC4F315Au, 0xD7626299u,
|
||||
0xCE7953D8u, 0x49E14F17u, 0x50FA7E56u, 0x7BD72D95u, 0x62CC1CD4u,
|
||||
0x2D8D8A13u, 0x3496BB52u, 0x1FBBE891u, 0x06A0D9D0u, 0x5E7EF3ECu,
|
||||
0x4765C2ADu, 0x6C48916Eu, 0x7553A02Fu, 0x3A1236E8u, 0x230907A9u,
|
||||
0x0824546Au, 0x113F652Bu, 0x96A779E4u, 0x8FBC48A5u, 0xA4911B66u,
|
||||
0xBD8A2A27u, 0xF2CBBCE0u, 0xEBD08DA1u, 0xC0FDDE62u, 0xD9E6EF23u,
|
||||
0x14BCE1BDu, 0x0DA7D0FCu, 0x268A833Fu, 0x3F91B27Eu, 0x70D024B9u,
|
||||
0x69CB15F8u, 0x42E6463Bu, 0x5BFD777Au, 0xDC656BB5u, 0xC57E5AF4u,
|
||||
0xEE530937u, 0xF7483876u, 0xB809AEB1u, 0xA1129FF0u, 0x8A3FCC33u,
|
||||
0x9324FD72u,
|
||||
};
|
||||
|
||||
private static readonly uint[] CrcTable2 = new uint[256]
|
||||
{
|
||||
0x00000000u, 0x01C26A37u, 0x0384D46Eu, 0x0246BE59u, 0x0709A8DCu,
|
||||
0x06CBC2EBu, 0x048D7CB2u, 0x054F1685u, 0x0E1351B8u, 0x0FD13B8Fu,
|
||||
0x0D9785D6u, 0x0C55EFE1u, 0x091AF964u, 0x08D89353u, 0x0A9E2D0Au,
|
||||
0x0B5C473Du, 0x1C26A370u, 0x1DE4C947u, 0x1FA2771Eu, 0x1E601D29u,
|
||||
0x1B2F0BACu, 0x1AED619Bu, 0x18ABDFC2u, 0x1969B5F5u, 0x1235F2C8u,
|
||||
0x13F798FFu, 0x11B126A6u, 0x10734C91u, 0x153C5A14u, 0x14FE3023u,
|
||||
0x16B88E7Au, 0x177AE44Du, 0x384D46E0u, 0x398F2CD7u, 0x3BC9928Eu,
|
||||
0x3A0BF8B9u, 0x3F44EE3Cu, 0x3E86840Bu, 0x3CC03A52u, 0x3D025065u,
|
||||
0x365E1758u, 0x379C7D6Fu, 0x35DAC336u, 0x3418A901u, 0x3157BF84u,
|
||||
0x3095D5B3u, 0x32D36BEAu, 0x331101DDu, 0x246BE590u, 0x25A98FA7u,
|
||||
0x27EF31FEu, 0x262D5BC9u, 0x23624D4Cu, 0x22A0277Bu, 0x20E69922u,
|
||||
0x2124F315u, 0x2A78B428u, 0x2BBADE1Fu, 0x29FC6046u, 0x283E0A71u,
|
||||
0x2D711CF4u, 0x2CB376C3u, 0x2EF5C89Au, 0x2F37A2ADu, 0x709A8DC0u,
|
||||
0x7158E7F7u, 0x731E59AEu, 0x72DC3399u, 0x7793251Cu, 0x76514F2Bu,
|
||||
0x7417F172u, 0x75D59B45u, 0x7E89DC78u, 0x7F4BB64Fu, 0x7D0D0816u,
|
||||
0x7CCF6221u, 0x798074A4u, 0x78421E93u, 0x7A04A0CAu, 0x7BC6CAFDu,
|
||||
0x6CBC2EB0u, 0x6D7E4487u, 0x6F38FADEu, 0x6EFA90E9u, 0x6BB5866Cu,
|
||||
0x6A77EC5Bu, 0x68315202u, 0x69F33835u, 0x62AF7F08u, 0x636D153Fu,
|
||||
0x612BAB66u, 0x60E9C151u, 0x65A6D7D4u, 0x6464BDE3u, 0x662203BAu,
|
||||
0x67E0698Du, 0x48D7CB20u, 0x4915A117u, 0x4B531F4Eu, 0x4A917579u,
|
||||
0x4FDE63FCu, 0x4E1C09CBu, 0x4C5AB792u, 0x4D98DDA5u, 0x46C49A98u,
|
||||
0x4706F0AFu, 0x45404EF6u, 0x448224C1u, 0x41CD3244u, 0x400F5873u,
|
||||
0x4249E62Au, 0x438B8C1Du, 0x54F16850u, 0x55330267u, 0x5775BC3Eu,
|
||||
0x56B7D609u, 0x53F8C08Cu, 0x523AAABBu, 0x507C14E2u, 0x51BE7ED5u,
|
||||
0x5AE239E8u, 0x5B2053DFu, 0x5966ED86u, 0x58A487B1u, 0x5DEB9134u,
|
||||
0x5C29FB03u, 0x5E6F455Au, 0x5FAD2F6Du, 0xE1351B80u, 0xE0F771B7u,
|
||||
0xE2B1CFEEu, 0xE373A5D9u, 0xE63CB35Cu, 0xE7FED96Bu, 0xE5B86732u,
|
||||
0xE47A0D05u, 0xEF264A38u, 0xEEE4200Fu, 0xECA29E56u, 0xED60F461u,
|
||||
0xE82FE2E4u, 0xE9ED88D3u, 0xEBAB368Au, 0xEA695CBDu, 0xFD13B8F0u,
|
||||
0xFCD1D2C7u, 0xFE976C9Eu, 0xFF5506A9u, 0xFA1A102Cu, 0xFBD87A1Bu,
|
||||
0xF99EC442u, 0xF85CAE75u, 0xF300E948u, 0xF2C2837Fu, 0xF0843D26u,
|
||||
0xF1465711u, 0xF4094194u, 0xF5CB2BA3u, 0xF78D95FAu, 0xF64FFFCDu,
|
||||
0xD9785D60u, 0xD8BA3757u, 0xDAFC890Eu, 0xDB3EE339u, 0xDE71F5BCu,
|
||||
0xDFB39F8Bu, 0xDDF521D2u, 0xDC374BE5u, 0xD76B0CD8u, 0xD6A966EFu,
|
||||
0xD4EFD8B6u, 0xD52DB281u, 0xD062A404u, 0xD1A0CE33u, 0xD3E6706Au,
|
||||
0xD2241A5Du, 0xC55EFE10u, 0xC49C9427u, 0xC6DA2A7Eu, 0xC7184049u,
|
||||
0xC25756CCu, 0xC3953CFBu, 0xC1D382A2u, 0xC011E895u, 0xCB4DAFA8u,
|
||||
0xCA8FC59Fu, 0xC8C97BC6u, 0xC90B11F1u, 0xCC440774u, 0xCD866D43u,
|
||||
0xCFC0D31Au, 0xCE02B92Du, 0x91AF9640u, 0x906DFC77u, 0x922B422Eu,
|
||||
0x93E92819u, 0x96A63E9Cu, 0x976454ABu, 0x9522EAF2u, 0x94E080C5u,
|
||||
0x9FBCC7F8u, 0x9E7EADCFu, 0x9C381396u, 0x9DFA79A1u, 0x98B56F24u,
|
||||
0x99770513u, 0x9B31BB4Au, 0x9AF3D17Du, 0x8D893530u, 0x8C4B5F07u,
|
||||
0x8E0DE15Eu, 0x8FCF8B69u, 0x8A809DECu, 0x8B42F7DBu, 0x89044982u,
|
||||
0x88C623B5u, 0x839A6488u, 0x82580EBFu, 0x801EB0E6u, 0x81DCDAD1u,
|
||||
0x8493CC54u, 0x8551A663u, 0x8717183Au, 0x86D5720Du, 0xA9E2D0A0u,
|
||||
0xA820BA97u, 0xAA6604CEu, 0xABA46EF9u, 0xAEEB787Cu, 0xAF29124Bu,
|
||||
0xAD6FAC12u, 0xACADC625u, 0xA7F18118u, 0xA633EB2Fu, 0xA4755576u,
|
||||
0xA5B73F41u, 0xA0F829C4u, 0xA13A43F3u, 0xA37CFDAAu, 0xA2BE979Du,
|
||||
0xB5C473D0u, 0xB40619E7u, 0xB640A7BEu, 0xB782CD89u, 0xB2CDDB0Cu,
|
||||
0xB30FB13Bu, 0xB1490F62u, 0xB08B6555u, 0xBBD72268u, 0xBA15485Fu,
|
||||
0xB853F606u, 0xB9919C31u, 0xBCDE8AB4u, 0xBD1CE083u, 0xBF5A5EDAu,
|
||||
0xBE9834EDu,
|
||||
};
|
||||
|
||||
private static readonly uint[] CrcTable3 = new uint[256]
|
||||
{
|
||||
0x00000000u, 0xB8BC6765u, 0xAA09C88Bu, 0x12B5AFEEu, 0x8F629757u,
|
||||
0x37DEF032u, 0x256B5FDCu, 0x9DD738B9u, 0xC5B428EFu, 0x7D084F8Au,
|
||||
0x6FBDE064u, 0xD7018701u, 0x4AD6BFB8u, 0xF26AD8DDu, 0xE0DF7733u,
|
||||
0x58631056u, 0x5019579Fu, 0xE8A530FAu, 0xFA109F14u, 0x42ACF871u,
|
||||
0xDF7BC0C8u, 0x67C7A7ADu, 0x75720843u, 0xCDCE6F26u, 0x95AD7F70u,
|
||||
0x2D111815u, 0x3FA4B7FBu, 0x8718D09Eu, 0x1ACFE827u, 0xA2738F42u,
|
||||
0xB0C620ACu, 0x087A47C9u, 0xA032AF3Eu, 0x188EC85Bu, 0x0A3B67B5u,
|
||||
0xB28700D0u, 0x2F503869u, 0x97EC5F0Cu, 0x8559F0E2u, 0x3DE59787u,
|
||||
0x658687D1u, 0xDD3AE0B4u, 0xCF8F4F5Au, 0x7733283Fu, 0xEAE41086u,
|
||||
0x525877E3u, 0x40EDD80Du, 0xF851BF68u, 0xF02BF8A1u, 0x48979FC4u,
|
||||
0x5A22302Au, 0xE29E574Fu, 0x7F496FF6u, 0xC7F50893u, 0xD540A77Du,
|
||||
0x6DFCC018u, 0x359FD04Eu, 0x8D23B72Bu, 0x9F9618C5u, 0x272A7FA0u,
|
||||
0xBAFD4719u, 0x0241207Cu, 0x10F48F92u, 0xA848E8F7u, 0x9B14583Du,
|
||||
0x23A83F58u, 0x311D90B6u, 0x89A1F7D3u, 0x1476CF6Au, 0xACCAA80Fu,
|
||||
0xBE7F07E1u, 0x06C36084u, 0x5EA070D2u, 0xE61C17B7u, 0xF4A9B859u,
|
||||
0x4C15DF3Cu, 0xD1C2E785u, 0x697E80E0u, 0x7BCB2F0Eu, 0xC377486Bu,
|
||||
0xCB0D0FA2u, 0x73B168C7u, 0x6104C729u, 0xD9B8A04Cu, 0x446F98F5u,
|
||||
0xFCD3FF90u, 0xEE66507Eu, 0x56DA371Bu, 0x0EB9274Du, 0xB6054028u,
|
||||
0xA4B0EFC6u, 0x1C0C88A3u, 0x81DBB01Au, 0x3967D77Fu, 0x2BD27891u,
|
||||
0x936E1FF4u, 0x3B26F703u, 0x839A9066u, 0x912F3F88u, 0x299358EDu,
|
||||
0xB4446054u, 0x0CF80731u, 0x1E4DA8DFu, 0xA6F1CFBAu, 0xFE92DFECu,
|
||||
0x462EB889u, 0x549B1767u, 0xEC277002u, 0x71F048BBu, 0xC94C2FDEu,
|
||||
0xDBF98030u, 0x6345E755u, 0x6B3FA09Cu, 0xD383C7F9u, 0xC1366817u,
|
||||
0x798A0F72u, 0xE45D37CBu, 0x5CE150AEu, 0x4E54FF40u, 0xF6E89825u,
|
||||
0xAE8B8873u, 0x1637EF16u, 0x048240F8u, 0xBC3E279Du, 0x21E91F24u,
|
||||
0x99557841u, 0x8BE0D7AFu, 0x335CB0CAu, 0xED59B63Bu, 0x55E5D15Eu,
|
||||
0x47507EB0u, 0xFFEC19D5u, 0x623B216Cu, 0xDA874609u, 0xC832E9E7u,
|
||||
0x708E8E82u, 0x28ED9ED4u, 0x9051F9B1u, 0x82E4565Fu, 0x3A58313Au,
|
||||
0xA78F0983u, 0x1F336EE6u, 0x0D86C108u, 0xB53AA66Du, 0xBD40E1A4u,
|
||||
0x05FC86C1u, 0x1749292Fu, 0xAFF54E4Au, 0x322276F3u, 0x8A9E1196u,
|
||||
0x982BBE78u, 0x2097D91Du, 0x78F4C94Bu, 0xC048AE2Eu, 0xD2FD01C0u,
|
||||
0x6A4166A5u, 0xF7965E1Cu, 0x4F2A3979u, 0x5D9F9697u, 0xE523F1F2u,
|
||||
0x4D6B1905u, 0xF5D77E60u, 0xE762D18Eu, 0x5FDEB6EBu, 0xC2098E52u,
|
||||
0x7AB5E937u, 0x680046D9u, 0xD0BC21BCu, 0x88DF31EAu, 0x3063568Fu,
|
||||
0x22D6F961u, 0x9A6A9E04u, 0x07BDA6BDu, 0xBF01C1D8u, 0xADB46E36u,
|
||||
0x15080953u, 0x1D724E9Au, 0xA5CE29FFu, 0xB77B8611u, 0x0FC7E174u,
|
||||
0x9210D9CDu, 0x2AACBEA8u, 0x38191146u, 0x80A57623u, 0xD8C66675u,
|
||||
0x607A0110u, 0x72CFAEFEu, 0xCA73C99Bu, 0x57A4F122u, 0xEF189647u,
|
||||
0xFDAD39A9u, 0x45115ECCu, 0x764DEE06u, 0xCEF18963u, 0xDC44268Du,
|
||||
0x64F841E8u, 0xF92F7951u, 0x41931E34u, 0x5326B1DAu, 0xEB9AD6BFu,
|
||||
0xB3F9C6E9u, 0x0B45A18Cu, 0x19F00E62u, 0xA14C6907u, 0x3C9B51BEu,
|
||||
0x842736DBu, 0x96929935u, 0x2E2EFE50u, 0x2654B999u, 0x9EE8DEFCu,
|
||||
0x8C5D7112u, 0x34E11677u, 0xA9362ECEu, 0x118A49ABu, 0x033FE645u,
|
||||
0xBB838120u, 0xE3E09176u, 0x5B5CF613u, 0x49E959FDu, 0xF1553E98u,
|
||||
0x6C820621u, 0xD43E6144u, 0xC68BCEAAu, 0x7E37A9CFu, 0xD67F4138u,
|
||||
0x6EC3265Du, 0x7C7689B3u, 0xC4CAEED6u, 0x591DD66Fu, 0xE1A1B10Au,
|
||||
0xF3141EE4u, 0x4BA87981u, 0x13CB69D7u, 0xAB770EB2u, 0xB9C2A15Cu,
|
||||
0x017EC639u, 0x9CA9FE80u, 0x241599E5u, 0x36A0360Bu, 0x8E1C516Eu,
|
||||
0x866616A7u, 0x3EDA71C2u, 0x2C6FDE2Cu, 0x94D3B949u, 0x090481F0u,
|
||||
0xB1B8E695u, 0xA30D497Bu, 0x1BB12E1Eu, 0x43D23E48u, 0xFB6E592Du,
|
||||
0xE9DBF6C3u, 0x516791A6u, 0xCCB0A91Fu, 0x740CCE7Au, 0x66B96194u,
|
||||
0xDE0506F1u,
|
||||
};
|
||||
|
||||
private static readonly uint[] CrcTable4 = new uint[256]
|
||||
{
|
||||
0x00000000u, 0x3D6029B0u, 0x7AC05360u, 0x47A07AD0u, 0xF580A6C0u,
|
||||
0xC8E08F70u, 0x8F40F5A0u, 0xB220DC10u, 0x30704BC1u, 0x0D106271u,
|
||||
0x4AB018A1u, 0x77D03111u, 0xC5F0ED01u, 0xF890C4B1u, 0xBF30BE61u,
|
||||
0x825097D1u, 0x60E09782u, 0x5D80BE32u, 0x1A20C4E2u, 0x2740ED52u,
|
||||
0x95603142u, 0xA80018F2u, 0xEFA06222u, 0xD2C04B92u, 0x5090DC43u,
|
||||
0x6DF0F5F3u, 0x2A508F23u, 0x1730A693u, 0xA5107A83u, 0x98705333u,
|
||||
0xDFD029E3u, 0xE2B00053u, 0xC1C12F04u, 0xFCA106B4u, 0xBB017C64u,
|
||||
0x866155D4u, 0x344189C4u, 0x0921A074u, 0x4E81DAA4u, 0x73E1F314u,
|
||||
0xF1B164C5u, 0xCCD14D75u, 0x8B7137A5u, 0xB6111E15u, 0x0431C205u,
|
||||
0x3951EBB5u, 0x7EF19165u, 0x4391B8D5u, 0xA121B886u, 0x9C419136u,
|
||||
0xDBE1EBE6u, 0xE681C256u, 0x54A11E46u, 0x69C137F6u, 0x2E614D26u,
|
||||
0x13016496u, 0x9151F347u, 0xAC31DAF7u, 0xEB91A027u, 0xD6F18997u,
|
||||
0x64D15587u, 0x59B17C37u, 0x1E1106E7u, 0x23712F57u, 0x58F35849u,
|
||||
0x659371F9u, 0x22330B29u, 0x1F532299u, 0xAD73FE89u, 0x9013D739u,
|
||||
0xD7B3ADE9u, 0xEAD38459u, 0x68831388u, 0x55E33A38u, 0x124340E8u,
|
||||
0x2F236958u, 0x9D03B548u, 0xA0639CF8u, 0xE7C3E628u, 0xDAA3CF98u,
|
||||
0x3813CFCBu, 0x0573E67Bu, 0x42D39CABu, 0x7FB3B51Bu, 0xCD93690Bu,
|
||||
0xF0F340BBu, 0xB7533A6Bu, 0x8A3313DBu, 0x0863840Au, 0x3503ADBAu,
|
||||
0x72A3D76Au, 0x4FC3FEDAu, 0xFDE322CAu, 0xC0830B7Au, 0x872371AAu,
|
||||
0xBA43581Au, 0x9932774Du, 0xA4525EFDu, 0xE3F2242Du, 0xDE920D9Du,
|
||||
0x6CB2D18Du, 0x51D2F83Du, 0x167282EDu, 0x2B12AB5Du, 0xA9423C8Cu,
|
||||
0x9422153Cu, 0xD3826FECu, 0xEEE2465Cu, 0x5CC29A4Cu, 0x61A2B3FCu,
|
||||
0x2602C92Cu, 0x1B62E09Cu, 0xF9D2E0CFu, 0xC4B2C97Fu, 0x8312B3AFu,
|
||||
0xBE729A1Fu, 0x0C52460Fu, 0x31326FBFu, 0x7692156Fu, 0x4BF23CDFu,
|
||||
0xC9A2AB0Eu, 0xF4C282BEu, 0xB362F86Eu, 0x8E02D1DEu, 0x3C220DCEu,
|
||||
0x0142247Eu, 0x46E25EAEu, 0x7B82771Eu, 0xB1E6B092u, 0x8C869922u,
|
||||
0xCB26E3F2u, 0xF646CA42u, 0x44661652u, 0x79063FE2u, 0x3EA64532u,
|
||||
0x03C66C82u, 0x8196FB53u, 0xBCF6D2E3u, 0xFB56A833u, 0xC6368183u,
|
||||
0x74165D93u, 0x49767423u, 0x0ED60EF3u, 0x33B62743u, 0xD1062710u,
|
||||
0xEC660EA0u, 0xABC67470u, 0x96A65DC0u, 0x248681D0u, 0x19E6A860u,
|
||||
0x5E46D2B0u, 0x6326FB00u, 0xE1766CD1u, 0xDC164561u, 0x9BB63FB1u,
|
||||
0xA6D61601u, 0x14F6CA11u, 0x2996E3A1u, 0x6E369971u, 0x5356B0C1u,
|
||||
0x70279F96u, 0x4D47B626u, 0x0AE7CCF6u, 0x3787E546u, 0x85A73956u,
|
||||
0xB8C710E6u, 0xFF676A36u, 0xC2074386u, 0x4057D457u, 0x7D37FDE7u,
|
||||
0x3A978737u, 0x07F7AE87u, 0xB5D77297u, 0x88B75B27u, 0xCF1721F7u,
|
||||
0xF2770847u, 0x10C70814u, 0x2DA721A4u, 0x6A075B74u, 0x576772C4u,
|
||||
0xE547AED4u, 0xD8278764u, 0x9F87FDB4u, 0xA2E7D404u, 0x20B743D5u,
|
||||
0x1DD76A65u, 0x5A7710B5u, 0x67173905u, 0xD537E515u, 0xE857CCA5u,
|
||||
0xAFF7B675u, 0x92979FC5u, 0xE915E8DBu, 0xD475C16Bu, 0x93D5BBBBu,
|
||||
0xAEB5920Bu, 0x1C954E1Bu, 0x21F567ABu, 0x66551D7Bu, 0x5B3534CBu,
|
||||
0xD965A31Au, 0xE4058AAAu, 0xA3A5F07Au, 0x9EC5D9CAu, 0x2CE505DAu,
|
||||
0x11852C6Au, 0x562556BAu, 0x6B457F0Au, 0x89F57F59u, 0xB49556E9u,
|
||||
0xF3352C39u, 0xCE550589u, 0x7C75D999u, 0x4115F029u, 0x06B58AF9u,
|
||||
0x3BD5A349u, 0xB9853498u, 0x84E51D28u, 0xC34567F8u, 0xFE254E48u,
|
||||
0x4C059258u, 0x7165BBE8u, 0x36C5C138u, 0x0BA5E888u, 0x28D4C7DFu,
|
||||
0x15B4EE6Fu, 0x521494BFu, 0x6F74BD0Fu, 0xDD54611Fu, 0xE03448AFu,
|
||||
0xA794327Fu, 0x9AF41BCFu, 0x18A48C1Eu, 0x25C4A5AEu, 0x6264DF7Eu,
|
||||
0x5F04F6CEu, 0xED242ADEu, 0xD044036Eu, 0x97E479BEu, 0xAA84500Eu,
|
||||
0x4834505Du, 0x755479EDu, 0x32F4033Du, 0x0F942A8Du, 0xBDB4F69Du,
|
||||
0x80D4DF2Du, 0xC774A5FDu, 0xFA148C4Du, 0x78441B9Cu, 0x4524322Cu,
|
||||
0x028448FCu, 0x3FE4614Cu, 0x8DC4BD5Cu, 0xB0A494ECu, 0xF704EE3Cu,
|
||||
0xCA64C78Cu,
|
||||
};
|
||||
|
||||
private static readonly uint[] CrcTable5 = new uint[256]
|
||||
{
|
||||
0x00000000u, 0xCB5CD3A5u, 0x4DC8A10Bu, 0x869472AEu, 0x9B914216u,
|
||||
0x50CD91B3u, 0xD659E31Du, 0x1D0530B8u, 0xEC53826Du, 0x270F51C8u,
|
||||
0xA19B2366u, 0x6AC7F0C3u, 0x77C2C07Bu, 0xBC9E13DEu, 0x3A0A6170u,
|
||||
0xF156B2D5u, 0x03D6029Bu, 0xC88AD13Eu, 0x4E1EA390u, 0x85427035u,
|
||||
0x9847408Du, 0x531B9328u, 0xD58FE186u, 0x1ED33223u, 0xEF8580F6u,
|
||||
0x24D95353u, 0xA24D21FDu, 0x6911F258u, 0x7414C2E0u, 0xBF481145u,
|
||||
0x39DC63EBu, 0xF280B04Eu, 0x07AC0536u, 0xCCF0D693u, 0x4A64A43Du,
|
||||
0x81387798u, 0x9C3D4720u, 0x57619485u, 0xD1F5E62Bu, 0x1AA9358Eu,
|
||||
0xEBFF875Bu, 0x20A354FEu, 0xA6372650u, 0x6D6BF5F5u, 0x706EC54Du,
|
||||
0xBB3216E8u, 0x3DA66446u, 0xF6FAB7E3u, 0x047A07ADu, 0xCF26D408u,
|
||||
0x49B2A6A6u, 0x82EE7503u, 0x9FEB45BBu, 0x54B7961Eu, 0xD223E4B0u,
|
||||
0x197F3715u, 0xE82985C0u, 0x23755665u, 0xA5E124CBu, 0x6EBDF76Eu,
|
||||
0x73B8C7D6u, 0xB8E41473u, 0x3E7066DDu, 0xF52CB578u, 0x0F580A6Cu,
|
||||
0xC404D9C9u, 0x4290AB67u, 0x89CC78C2u, 0x94C9487Au, 0x5F959BDFu,
|
||||
0xD901E971u, 0x125D3AD4u, 0xE30B8801u, 0x28575BA4u, 0xAEC3290Au,
|
||||
0x659FFAAFu, 0x789ACA17u, 0xB3C619B2u, 0x35526B1Cu, 0xFE0EB8B9u,
|
||||
0x0C8E08F7u, 0xC7D2DB52u, 0x4146A9FCu, 0x8A1A7A59u, 0x971F4AE1u,
|
||||
0x5C439944u, 0xDAD7EBEAu, 0x118B384Fu, 0xE0DD8A9Au, 0x2B81593Fu,
|
||||
0xAD152B91u, 0x6649F834u, 0x7B4CC88Cu, 0xB0101B29u, 0x36846987u,
|
||||
0xFDD8BA22u, 0x08F40F5Au, 0xC3A8DCFFu, 0x453CAE51u, 0x8E607DF4u,
|
||||
0x93654D4Cu, 0x58399EE9u, 0xDEADEC47u, 0x15F13FE2u, 0xE4A78D37u,
|
||||
0x2FFB5E92u, 0xA96F2C3Cu, 0x6233FF99u, 0x7F36CF21u, 0xB46A1C84u,
|
||||
0x32FE6E2Au, 0xF9A2BD8Fu, 0x0B220DC1u, 0xC07EDE64u, 0x46EAACCAu,
|
||||
0x8DB67F6Fu, 0x90B34FD7u, 0x5BEF9C72u, 0xDD7BEEDCu, 0x16273D79u,
|
||||
0xE7718FACu, 0x2C2D5C09u, 0xAAB92EA7u, 0x61E5FD02u, 0x7CE0CDBAu,
|
||||
0xB7BC1E1Fu, 0x31286CB1u, 0xFA74BF14u, 0x1EB014D8u, 0xD5ECC77Du,
|
||||
0x5378B5D3u, 0x98246676u, 0x852156CEu, 0x4E7D856Bu, 0xC8E9F7C5u,
|
||||
0x03B52460u, 0xF2E396B5u, 0x39BF4510u, 0xBF2B37BEu, 0x7477E41Bu,
|
||||
0x6972D4A3u, 0xA22E0706u, 0x24BA75A8u, 0xEFE6A60Du, 0x1D661643u,
|
||||
0xD63AC5E6u, 0x50AEB748u, 0x9BF264EDu, 0x86F75455u, 0x4DAB87F0u,
|
||||
0xCB3FF55Eu, 0x006326FBu, 0xF135942Eu, 0x3A69478Bu, 0xBCFD3525u,
|
||||
0x77A1E680u, 0x6AA4D638u, 0xA1F8059Du, 0x276C7733u, 0xEC30A496u,
|
||||
0x191C11EEu, 0xD240C24Bu, 0x54D4B0E5u, 0x9F886340u, 0x828D53F8u,
|
||||
0x49D1805Du, 0xCF45F2F3u, 0x04192156u, 0xF54F9383u, 0x3E134026u,
|
||||
0xB8873288u, 0x73DBE12Du, 0x6EDED195u, 0xA5820230u, 0x2316709Eu,
|
||||
0xE84AA33Bu, 0x1ACA1375u, 0xD196C0D0u, 0x5702B27Eu, 0x9C5E61DBu,
|
||||
0x815B5163u, 0x4A0782C6u, 0xCC93F068u, 0x07CF23CDu, 0xF6999118u,
|
||||
0x3DC542BDu, 0xBB513013u, 0x700DE3B6u, 0x6D08D30Eu, 0xA65400ABu,
|
||||
0x20C07205u, 0xEB9CA1A0u, 0x11E81EB4u, 0xDAB4CD11u, 0x5C20BFBFu,
|
||||
0x977C6C1Au, 0x8A795CA2u, 0x41258F07u, 0xC7B1FDA9u, 0x0CED2E0Cu,
|
||||
0xFDBB9CD9u, 0x36E74F7Cu, 0xB0733DD2u, 0x7B2FEE77u, 0x662ADECFu,
|
||||
0xAD760D6Au, 0x2BE27FC4u, 0xE0BEAC61u, 0x123E1C2Fu, 0xD962CF8Au,
|
||||
0x5FF6BD24u, 0x94AA6E81u, 0x89AF5E39u, 0x42F38D9Cu, 0xC467FF32u,
|
||||
0x0F3B2C97u, 0xFE6D9E42u, 0x35314DE7u, 0xB3A53F49u, 0x78F9ECECu,
|
||||
0x65FCDC54u, 0xAEA00FF1u, 0x28347D5Fu, 0xE368AEFAu, 0x16441B82u,
|
||||
0xDD18C827u, 0x5B8CBA89u, 0x90D0692Cu, 0x8DD55994u, 0x46898A31u,
|
||||
0xC01DF89Fu, 0x0B412B3Au, 0xFA1799EFu, 0x314B4A4Au, 0xB7DF38E4u,
|
||||
0x7C83EB41u, 0x6186DBF9u, 0xAADA085Cu, 0x2C4E7AF2u, 0xE712A957u,
|
||||
0x15921919u, 0xDECECABCu, 0x585AB812u, 0x93066BB7u, 0x8E035B0Fu,
|
||||
0x455F88AAu, 0xC3CBFA04u, 0x089729A1u, 0xF9C19B74u, 0x329D48D1u,
|
||||
0xB4093A7Fu, 0x7F55E9DAu, 0x6250D962u, 0xA90C0AC7u, 0x2F987869u,
|
||||
0xE4C4ABCCu,
|
||||
};
|
||||
|
||||
private static readonly uint[] CrcTable6 = new uint[256]
|
||||
{
|
||||
0x00000000u, 0xA6770BB4u, 0x979F1129u, 0x31E81A9Du, 0xF44F2413u,
|
||||
0x52382FA7u, 0x63D0353Au, 0xC5A73E8Eu, 0x33EF4E67u, 0x959845D3u,
|
||||
0xA4705F4Eu, 0x020754FAu, 0xC7A06A74u, 0x61D761C0u, 0x503F7B5Du,
|
||||
0xF64870E9u, 0x67DE9CCEu, 0xC1A9977Au, 0xF0418DE7u, 0x56368653u,
|
||||
0x9391B8DDu, 0x35E6B369u, 0x040EA9F4u, 0xA279A240u, 0x5431D2A9u,
|
||||
0xF246D91Du, 0xC3AEC380u, 0x65D9C834u, 0xA07EF6BAu, 0x0609FD0Eu,
|
||||
0x37E1E793u, 0x9196EC27u, 0xCFBD399Cu, 0x69CA3228u, 0x582228B5u,
|
||||
0xFE552301u, 0x3BF21D8Fu, 0x9D85163Bu, 0xAC6D0CA6u, 0x0A1A0712u,
|
||||
0xFC5277FBu, 0x5A257C4Fu, 0x6BCD66D2u, 0xCDBA6D66u, 0x081D53E8u,
|
||||
0xAE6A585Cu, 0x9F8242C1u, 0x39F54975u, 0xA863A552u, 0x0E14AEE6u,
|
||||
0x3FFCB47Bu, 0x998BBFCFu, 0x5C2C8141u, 0xFA5B8AF5u, 0xCBB39068u,
|
||||
0x6DC49BDCu, 0x9B8CEB35u, 0x3DFBE081u, 0x0C13FA1Cu, 0xAA64F1A8u,
|
||||
0x6FC3CF26u, 0xC9B4C492u, 0xF85CDE0Fu, 0x5E2BD5BBu, 0x440B7579u,
|
||||
0xE27C7ECDu, 0xD3946450u, 0x75E36FE4u, 0xB044516Au, 0x16335ADEu,
|
||||
0x27DB4043u, 0x81AC4BF7u, 0x77E43B1Eu, 0xD19330AAu, 0xE07B2A37u,
|
||||
0x460C2183u, 0x83AB1F0Du, 0x25DC14B9u, 0x14340E24u, 0xB2430590u,
|
||||
0x23D5E9B7u, 0x85A2E203u, 0xB44AF89Eu, 0x123DF32Au, 0xD79ACDA4u,
|
||||
0x71EDC610u, 0x4005DC8Du, 0xE672D739u, 0x103AA7D0u, 0xB64DAC64u,
|
||||
0x87A5B6F9u, 0x21D2BD4Du, 0xE47583C3u, 0x42028877u, 0x73EA92EAu,
|
||||
0xD59D995Eu, 0x8BB64CE5u, 0x2DC14751u, 0x1C295DCCu, 0xBA5E5678u,
|
||||
0x7FF968F6u, 0xD98E6342u, 0xE86679DFu, 0x4E11726Bu, 0xB8590282u,
|
||||
0x1E2E0936u, 0x2FC613ABu, 0x89B1181Fu, 0x4C162691u, 0xEA612D25u,
|
||||
0xDB8937B8u, 0x7DFE3C0Cu, 0xEC68D02Bu, 0x4A1FDB9Fu, 0x7BF7C102u,
|
||||
0xDD80CAB6u, 0x1827F438u, 0xBE50FF8Cu, 0x8FB8E511u, 0x29CFEEA5u,
|
||||
0xDF879E4Cu, 0x79F095F8u, 0x48188F65u, 0xEE6F84D1u, 0x2BC8BA5Fu,
|
||||
0x8DBFB1EBu, 0xBC57AB76u, 0x1A20A0C2u, 0x8816EAF2u, 0x2E61E146u,
|
||||
0x1F89FBDBu, 0xB9FEF06Fu, 0x7C59CEE1u, 0xDA2EC555u, 0xEBC6DFC8u,
|
||||
0x4DB1D47Cu, 0xBBF9A495u, 0x1D8EAF21u, 0x2C66B5BCu, 0x8A11BE08u,
|
||||
0x4FB68086u, 0xE9C18B32u, 0xD82991AFu, 0x7E5E9A1Bu, 0xEFC8763Cu,
|
||||
0x49BF7D88u, 0x78576715u, 0xDE206CA1u, 0x1B87522Fu, 0xBDF0599Bu,
|
||||
0x8C184306u, 0x2A6F48B2u, 0xDC27385Bu, 0x7A5033EFu, 0x4BB82972u,
|
||||
0xEDCF22C6u, 0x28681C48u, 0x8E1F17FCu, 0xBFF70D61u, 0x198006D5u,
|
||||
0x47ABD36Eu, 0xE1DCD8DAu, 0xD034C247u, 0x7643C9F3u, 0xB3E4F77Du,
|
||||
0x1593FCC9u, 0x247BE654u, 0x820CEDE0u, 0x74449D09u, 0xD23396BDu,
|
||||
0xE3DB8C20u, 0x45AC8794u, 0x800BB91Au, 0x267CB2AEu, 0x1794A833u,
|
||||
0xB1E3A387u, 0x20754FA0u, 0x86024414u, 0xB7EA5E89u, 0x119D553Du,
|
||||
0xD43A6BB3u, 0x724D6007u, 0x43A57A9Au, 0xE5D2712Eu, 0x139A01C7u,
|
||||
0xB5ED0A73u, 0x840510EEu, 0x22721B5Au, 0xE7D525D4u, 0x41A22E60u,
|
||||
0x704A34FDu, 0xD63D3F49u, 0xCC1D9F8Bu, 0x6A6A943Fu, 0x5B828EA2u,
|
||||
0xFDF58516u, 0x3852BB98u, 0x9E25B02Cu, 0xAFCDAAB1u, 0x09BAA105u,
|
||||
0xFFF2D1ECu, 0x5985DA58u, 0x686DC0C5u, 0xCE1ACB71u, 0x0BBDF5FFu,
|
||||
0xADCAFE4Bu, 0x9C22E4D6u, 0x3A55EF62u, 0xABC30345u, 0x0DB408F1u,
|
||||
0x3C5C126Cu, 0x9A2B19D8u, 0x5F8C2756u, 0xF9FB2CE2u, 0xC813367Fu,
|
||||
0x6E643DCBu, 0x982C4D22u, 0x3E5B4696u, 0x0FB35C0Bu, 0xA9C457BFu,
|
||||
0x6C636931u, 0xCA146285u, 0xFBFC7818u, 0x5D8B73ACu, 0x03A0A617u,
|
||||
0xA5D7ADA3u, 0x943FB73Eu, 0x3248BC8Au, 0xF7EF8204u, 0x519889B0u,
|
||||
0x6070932Du, 0xC6079899u, 0x304FE870u, 0x9638E3C4u, 0xA7D0F959u,
|
||||
0x01A7F2EDu, 0xC400CC63u, 0x6277C7D7u, 0x539FDD4Au, 0xF5E8D6FEu,
|
||||
0x647E3AD9u, 0xC209316Du, 0xF3E12BF0u, 0x55962044u, 0x90311ECAu,
|
||||
0x3646157Eu, 0x07AE0FE3u, 0xA1D90457u, 0x579174BEu, 0xF1E67F0Au,
|
||||
0xC00E6597u, 0x66796E23u, 0xA3DE50ADu, 0x05A95B19u, 0x34414184u,
|
||||
0x92364A30u,
|
||||
};
|
||||
|
||||
private static readonly uint[] CrcTable7 = new uint[256]
|
||||
{
|
||||
0x00000000u, 0xCCAA009Eu, 0x4225077Du, 0x8E8F07E3u, 0x844A0EFAu,
|
||||
0x48E00E64u, 0xC66F0987u, 0x0AC50919u, 0xD3E51BB5u, 0x1F4F1B2Bu,
|
||||
0x91C01CC8u, 0x5D6A1C56u, 0x57AF154Fu, 0x9B0515D1u, 0x158A1232u,
|
||||
0xD92012ACu, 0x7CBB312Bu, 0xB01131B5u, 0x3E9E3656u, 0xF23436C8u,
|
||||
0xF8F13FD1u, 0x345B3F4Fu, 0xBAD438ACu, 0x767E3832u, 0xAF5E2A9Eu,
|
||||
0x63F42A00u, 0xED7B2DE3u, 0x21D12D7Du, 0x2B142464u, 0xE7BE24FAu,
|
||||
0x69312319u, 0xA59B2387u, 0xF9766256u, 0x35DC62C8u, 0xBB53652Bu,
|
||||
0x77F965B5u, 0x7D3C6CACu, 0xB1966C32u, 0x3F196BD1u, 0xF3B36B4Fu,
|
||||
0x2A9379E3u, 0xE639797Du, 0x68B67E9Eu, 0xA41C7E00u, 0xAED97719u,
|
||||
0x62737787u, 0xECFC7064u, 0x205670FAu, 0x85CD537Du, 0x496753E3u,
|
||||
0xC7E85400u, 0x0B42549Eu, 0x01875D87u, 0xCD2D5D19u, 0x43A25AFAu,
|
||||
0x8F085A64u, 0x562848C8u, 0x9A824856u, 0x140D4FB5u, 0xD8A74F2Bu,
|
||||
0xD2624632u, 0x1EC846ACu, 0x9047414Fu, 0x5CED41D1u, 0x299DC2EDu,
|
||||
0xE537C273u, 0x6BB8C590u, 0xA712C50Eu, 0xADD7CC17u, 0x617DCC89u,
|
||||
0xEFF2CB6Au, 0x2358CBF4u, 0xFA78D958u, 0x36D2D9C6u, 0xB85DDE25u,
|
||||
0x74F7DEBBu, 0x7E32D7A2u, 0xB298D73Cu, 0x3C17D0DFu, 0xF0BDD041u,
|
||||
0x5526F3C6u, 0x998CF358u, 0x1703F4BBu, 0xDBA9F425u, 0xD16CFD3Cu,
|
||||
0x1DC6FDA2u, 0x9349FA41u, 0x5FE3FADFu, 0x86C3E873u, 0x4A69E8EDu,
|
||||
0xC4E6EF0Eu, 0x084CEF90u, 0x0289E689u, 0xCE23E617u, 0x40ACE1F4u,
|
||||
0x8C06E16Au, 0xD0EBA0BBu, 0x1C41A025u, 0x92CEA7C6u, 0x5E64A758u,
|
||||
0x54A1AE41u, 0x980BAEDFu, 0x1684A93Cu, 0xDA2EA9A2u, 0x030EBB0Eu,
|
||||
0xCFA4BB90u, 0x412BBC73u, 0x8D81BCEDu, 0x8744B5F4u, 0x4BEEB56Au,
|
||||
0xC561B289u, 0x09CBB217u, 0xAC509190u, 0x60FA910Eu, 0xEE7596EDu,
|
||||
0x22DF9673u, 0x281A9F6Au, 0xE4B09FF4u, 0x6A3F9817u, 0xA6959889u,
|
||||
0x7FB58A25u, 0xB31F8ABBu, 0x3D908D58u, 0xF13A8DC6u, 0xFBFF84DFu,
|
||||
0x37558441u, 0xB9DA83A2u, 0x7570833Cu, 0x533B85DAu, 0x9F918544u,
|
||||
0x111E82A7u, 0xDDB48239u, 0xD7718B20u, 0x1BDB8BBEu, 0x95548C5Du,
|
||||
0x59FE8CC3u, 0x80DE9E6Fu, 0x4C749EF1u, 0xC2FB9912u, 0x0E51998Cu,
|
||||
0x04949095u, 0xC83E900Bu, 0x46B197E8u, 0x8A1B9776u, 0x2F80B4F1u,
|
||||
0xE32AB46Fu, 0x6DA5B38Cu, 0xA10FB312u, 0xABCABA0Bu, 0x6760BA95u,
|
||||
0xE9EFBD76u, 0x2545BDE8u, 0xFC65AF44u, 0x30CFAFDAu, 0xBE40A839u,
|
||||
0x72EAA8A7u, 0x782FA1BEu, 0xB485A120u, 0x3A0AA6C3u, 0xF6A0A65Du,
|
||||
0xAA4DE78Cu, 0x66E7E712u, 0xE868E0F1u, 0x24C2E06Fu, 0x2E07E976u,
|
||||
0xE2ADE9E8u, 0x6C22EE0Bu, 0xA088EE95u, 0x79A8FC39u, 0xB502FCA7u,
|
||||
0x3B8DFB44u, 0xF727FBDAu, 0xFDE2F2C3u, 0x3148F25Du, 0xBFC7F5BEu,
|
||||
0x736DF520u, 0xD6F6D6A7u, 0x1A5CD639u, 0x94D3D1DAu, 0x5879D144u,
|
||||
0x52BCD85Du, 0x9E16D8C3u, 0x1099DF20u, 0xDC33DFBEu, 0x0513CD12u,
|
||||
0xC9B9CD8Cu, 0x4736CA6Fu, 0x8B9CCAF1u, 0x8159C3E8u, 0x4DF3C376u,
|
||||
0xC37CC495u, 0x0FD6C40Bu, 0x7AA64737u, 0xB60C47A9u, 0x3883404Au,
|
||||
0xF42940D4u, 0xFEEC49CDu, 0x32464953u, 0xBCC94EB0u, 0x70634E2Eu,
|
||||
0xA9435C82u, 0x65E95C1Cu, 0xEB665BFFu, 0x27CC5B61u, 0x2D095278u,
|
||||
0xE1A352E6u, 0x6F2C5505u, 0xA386559Bu, 0x061D761Cu, 0xCAB77682u,
|
||||
0x44387161u, 0x889271FFu, 0x825778E6u, 0x4EFD7878u, 0xC0727F9Bu,
|
||||
0x0CD87F05u, 0xD5F86DA9u, 0x19526D37u, 0x97DD6AD4u, 0x5B776A4Au,
|
||||
0x51B26353u, 0x9D1863CDu, 0x1397642Eu, 0xDF3D64B0u, 0x83D02561u,
|
||||
0x4F7A25FFu, 0xC1F5221Cu, 0x0D5F2282u, 0x079A2B9Bu, 0xCB302B05u,
|
||||
0x45BF2CE6u, 0x89152C78u, 0x50353ED4u, 0x9C9F3E4Au, 0x121039A9u,
|
||||
0xDEBA3937u, 0xD47F302Eu, 0x18D530B0u, 0x965A3753u, 0x5AF037CDu,
|
||||
0xFF6B144Au, 0x33C114D4u, 0xBD4E1337u, 0x71E413A9u, 0x7B211AB0u,
|
||||
0xB78B1A2Eu, 0x39041DCDu, 0xF5AE1D53u, 0x2C8E0FFFu, 0xE0240F61u,
|
||||
0x6EAB0882u, 0xA201081Cu, 0xA8C40105u, 0x646E019Bu, 0xEAE10678u,
|
||||
0x264B06E6u,
|
||||
};
|
||||
|
||||
public static unsafe uint Update(uint crc32, ReadOnlySpan<byte> span)
|
||||
{
|
||||
Contract.Assert(BitConverter.IsLittleEndian, "Little Endian expected");
|
||||
|
||||
crc32 ^= 0xFFFFFFFFU;
|
||||
int offset = 0;
|
||||
int runningLength = (span.Length / 8) * 8;
|
||||
int endBytes = span.Length - runningLength;
|
||||
|
||||
fixed (uint* words = MemoryMarshal.Cast<byte, uint>(span))
|
||||
{
|
||||
for (int i = 0; i < runningLength / 8; i++)
|
||||
{
|
||||
crc32 ^= words[offset];
|
||||
offset += 1;
|
||||
uint term1 = Crc32.CrcTable7[crc32 & 0x000000FF] ^
|
||||
Crc32.CrcTable6[(crc32 >> 8) & 0x000000FF];
|
||||
|
||||
uint term2 = crc32 >> 16;
|
||||
crc32 = term1 ^
|
||||
Crc32.CrcTable5[term2 & 0x000000FF] ^
|
||||
Crc32.CrcTable4[(term2 >> 8) & 0x000000FF];
|
||||
|
||||
uint term3 = words[offset];
|
||||
offset += 1;
|
||||
term1 = Crc32.CrcTable3[term3 & 0x000000FF] ^
|
||||
Crc32.CrcTable2[(term3 >> 8) & 0x000000FF];
|
||||
|
||||
term2 = term3 >> 16;
|
||||
crc32 ^= term1 ^
|
||||
Crc32.CrcTable1[term2 & 0x000000FF] ^
|
||||
Crc32.CrcTable0[(term2 >> 8) & 0x000000FF];
|
||||
}
|
||||
}
|
||||
|
||||
offset = runningLength;
|
||||
for (int i = 0; i < endBytes; i++)
|
||||
{
|
||||
crc32 = Crc32.CrcTable0[(crc32 ^ span[offset++]) & 0x000000FF] ^ (crc32 >> 8);
|
||||
}
|
||||
|
||||
crc32 ^= 0xFFFFFFFFU;
|
||||
return crc32;
|
||||
}
|
||||
}
|
||||
}
|
||||
31
src/Core/Core/Linear.cs
Normal file
31
src/Core/Core/Linear.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Azure.Cosmos.Core
|
||||
{
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
public static class Linear
|
||||
{
|
||||
/// <summary>
|
||||
/// Perform the managed equivalent of std::move by returning the value at
|
||||
/// <paramref name="src" /> while simultaneously assigning <see cref="T:default" /> to
|
||||
/// <paramref name="src" />.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the value to transfer.</typeparam>
|
||||
/// <param name="src">A reference to the field whose value should be transferred.</param>
|
||||
/// <returns>The value transferred.</returns>
|
||||
/// <remarks>
|
||||
/// The value of <paramref name="src" /> after the transfer is always <see cref="T:default" />
|
||||
/// . The value is considered "consumed".
|
||||
/// </remarks>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static T Move<T>(ref T src)
|
||||
{
|
||||
T retval = src;
|
||||
src = default;
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
}
|
||||
12
src/Core/Core/Microsoft.Azure.Cosmos.Core.csproj
Normal file
12
src/Core/Core/Microsoft.Azure.Cosmos.Core.csproj
Normal file
@@ -0,0 +1,12 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.7.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
136
src/Core/Core/SpanHexExtensions.cs
Normal file
136
src/Core/Core/SpanHexExtensions.cs
Normal file
@@ -0,0 +1,136 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Azure.Cosmos.Core
|
||||
{
|
||||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
public static class SpanHexExtensions
|
||||
{
|
||||
private static readonly byte[] DecodeTable =
|
||||
{
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
|
||||
};
|
||||
|
||||
private static readonly uint[] EncodeTable = SpanHexExtensions.Initialize();
|
||||
|
||||
public static unsafe string ToHexString(this ReadOnlySpan<byte> bytes)
|
||||
{
|
||||
int len = bytes.Length;
|
||||
string result = new string((char)0, len * 2);
|
||||
fixed (uint* lp = SpanHexExtensions.EncodeTable)
|
||||
{
|
||||
fixed (byte* bp = bytes)
|
||||
{
|
||||
fixed (char* rp = result)
|
||||
{
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
((uint*)rp)[i] = lp[bp[i]];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static bool TryParseHexString(this ReadOnlySpan<char> hexChars, out byte[] result)
|
||||
{
|
||||
Contract.Requires(hexChars.Length % 2 == 0);
|
||||
|
||||
int len = hexChars.Length;
|
||||
result = new byte[len / 2];
|
||||
if (!hexChars.TryParseHexString(result.AsSpan()))
|
||||
{
|
||||
result = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static unsafe bool TryParseHexString(this ReadOnlySpan<char> hexChars, Span<byte> result)
|
||||
{
|
||||
Contract.Requires(hexChars.Length % 2 == 0);
|
||||
Contract.Requires(result.Length == hexChars.Length / 2);
|
||||
|
||||
int len = hexChars.Length;
|
||||
fixed (byte* lp = SpanHexExtensions.DecodeTable)
|
||||
{
|
||||
fixed (char* cp = hexChars)
|
||||
{
|
||||
fixed (byte* rp = result)
|
||||
{
|
||||
for (int i = 0; i < len; i += 2)
|
||||
{
|
||||
int c1 = cp[i];
|
||||
if (c1 < 0 || c1 > 255)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
byte b1 = lp[c1];
|
||||
if (b1 == 255)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int c2 = cp[i + 1];
|
||||
if (c2 < 0 || c2 > 255)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
byte b2 = lp[c2];
|
||||
if (b2 == 255)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
rp[i / 2] = (byte)((b1 << 4) | b2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static uint[] Initialize()
|
||||
{
|
||||
uint[] result = new uint[256];
|
||||
for (int i = 0; i < 256; i++)
|
||||
{
|
||||
string s = i.ToString("X2", CultureInfo.InvariantCulture);
|
||||
if (BitConverter.IsLittleEndian)
|
||||
{
|
||||
result[i] = s[0] + ((uint)s[1] << 16);
|
||||
}
|
||||
else
|
||||
{
|
||||
result[i] = s[1] + ((uint)s[0] << 16);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
34
src/Core/Core/Utf8/Readme.md
Normal file
34
src/Core/Core/Utf8/Readme.md
Normal file
@@ -0,0 +1,34 @@
|
||||
This directory contains types derived from
|
||||
[dotnet/corefxlab](https://github.com/dotnet/corefxlab) repo. This repo contains designs
|
||||
proposed by the CLR team but not yet committed for inclusion in either the C# language
|
||||
or the standard .NET Framework. The types included here (e.g. Utf8Span) may **never**
|
||||
appear in the official standard. Including the types here lays a foundation for adopting
|
||||
these types **if** they do become standard in the future.
|
||||
|
||||
[[_TOC_]]
|
||||
|
||||
|
||||
## Utf8Span
|
||||
A readonly struct wrapping a sequence of bytes that are guaranteed to be a valid UTF8
|
||||
encoded string.
|
||||
|
||||
A `Utf8Span` can be created over a `ReadOnlySpan<byte>` at the cost of validating the
|
||||
byte sequence. Once the byte sequence has been validated then a `Utf8Span` can be passed
|
||||
around safely without re-validating the content as UTF8. The type system is used to
|
||||
enforce the correctness.
|
||||
|
||||
## Utf8String
|
||||
A readonly class wrapping a sequence of bytes that are guaranteed to be a valid UTF8
|
||||
encoded string.
|
||||
|
||||
`Utf8String` is the heap equivalent of `Utf8Span` and provides the same capabilities.
|
||||
`Utf8String` can be implicitly converted to `Utf8Span`. This conversion is guaranteed
|
||||
to be cheap and non-allocating. Converting from a `Utf8Span` to a `Utf8String`, however,
|
||||
requires a blittable copy of the content (but not re-validation). Additionally, `Utf8String`
|
||||
can be converted to a `string` object via the expected transcode process. This operation is expensive.
|
||||
|
||||
## UtfAnyString
|
||||
A readonly struct wrapping either a `Utf8String` or a `string` object. The `UtfAnyString`
|
||||
enables API's to accept both UTF8 and UTF16 encoded strings without requiring overloads.
|
||||
UtfAnyString provides implicit conversion **from** either type, but explicit convert **to**
|
||||
either type (because such a conversion *may* require a transcoding copy).
|
||||
51
src/Core/Core/Utf8/Utf16LittleEndianCodePointEnumerator.cs
Normal file
51
src/Core/Core/Utf8/Utf16LittleEndianCodePointEnumerator.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Azure.Cosmos.Core.Utf8
|
||||
{
|
||||
using System;
|
||||
|
||||
internal struct Utf16LittleEndianCodePointEnumerator
|
||||
{
|
||||
private readonly string str;
|
||||
private int index;
|
||||
private uint codePoint;
|
||||
private bool hasValue;
|
||||
|
||||
public Utf16LittleEndianCodePointEnumerator(string str)
|
||||
{
|
||||
Contract.Assert(BitConverter.IsLittleEndian);
|
||||
|
||||
this.str = str;
|
||||
this.index = 0;
|
||||
this.codePoint = 0;
|
||||
this.hasValue = false;
|
||||
}
|
||||
|
||||
public uint Current
|
||||
{
|
||||
get
|
||||
{
|
||||
Contract.Requires(this.hasValue);
|
||||
return this.codePoint;
|
||||
}
|
||||
}
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
if (this.index >= this.str.Length)
|
||||
{
|
||||
this.hasValue = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.hasValue = Utf8Helper.TryDecodeCodePointFromString(this.str, this.index, out this.codePoint, out int charsConsumed);
|
||||
Contract.Invariant(this.hasValue && charsConsumed > 0, "Invalid code point!");
|
||||
this.index += charsConsumed;
|
||||
}
|
||||
|
||||
return this.hasValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
49
src/Core/Core/Utf8/Utf8CodePointEnumerator.cs
Normal file
49
src/Core/Core/Utf8/Utf8CodePointEnumerator.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Azure.Cosmos.Core.Utf8
|
||||
{
|
||||
using System;
|
||||
|
||||
public ref struct Utf8CodePointEnumerator
|
||||
{
|
||||
private readonly ReadOnlySpan<byte> utf8Bytes;
|
||||
private int index;
|
||||
private uint codePoint;
|
||||
private bool hasValue;
|
||||
|
||||
public Utf8CodePointEnumerator(ReadOnlySpan<byte> utf8Bytes)
|
||||
{
|
||||
this.utf8Bytes = utf8Bytes;
|
||||
this.index = 0;
|
||||
this.codePoint = 0;
|
||||
this.hasValue = false;
|
||||
}
|
||||
|
||||
public uint Current
|
||||
{
|
||||
get
|
||||
{
|
||||
Contract.Requires(this.hasValue);
|
||||
return this.codePoint;
|
||||
}
|
||||
}
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
if (this.index >= this.utf8Bytes.Length)
|
||||
{
|
||||
this.hasValue = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.hasValue = Utf8Helper.TryDecodeCodePoint(this.utf8Bytes, this.index, out this.codePoint, out int bytesConsumed);
|
||||
Contract.Invariant(this.hasValue && bytesConsumed > 0, "Invalid code point!");
|
||||
this.index += bytesConsumed;
|
||||
}
|
||||
|
||||
return this.hasValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
134
src/Core/Core/Utf8/Utf8Helper.cs
Normal file
134
src/Core/Core/Utf8/Utf8Helper.cs
Normal file
@@ -0,0 +1,134 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Azure.Cosmos.Core.Utf8
|
||||
{
|
||||
using System;
|
||||
|
||||
internal static class Utf8Helper
|
||||
{
|
||||
public const int MaxCodeUnitsPerCodePoint = 4;
|
||||
|
||||
public static bool TryDecodeCodePoint(ReadOnlySpan<byte> utf8, int index, out uint codePoint, out int bytesConsumed)
|
||||
{
|
||||
if (index >= utf8.Length)
|
||||
{
|
||||
codePoint = default;
|
||||
bytesConsumed = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
byte first = utf8[index];
|
||||
|
||||
bytesConsumed = Utf8Helper.GetEncodedBytes(first);
|
||||
if (bytesConsumed == 0 || utf8.Length - index < bytesConsumed)
|
||||
{
|
||||
bytesConsumed = 0;
|
||||
codePoint = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (bytesConsumed)
|
||||
{
|
||||
case 1:
|
||||
codePoint = first;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
codePoint = (uint)(first & Utf8Helper.B0001_1111U);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
codePoint = (uint)(first & Utf8Helper.B0000_1111U);
|
||||
break;
|
||||
|
||||
case 4:
|
||||
codePoint = (uint)(first & Utf8Helper.B0000_0111U);
|
||||
break;
|
||||
|
||||
default:
|
||||
codePoint = default;
|
||||
bytesConsumed = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 1; i < bytesConsumed; i++)
|
||||
{
|
||||
uint current = utf8[index + i];
|
||||
if ((current & Utf8Helper.B1100_0000U) != Utf8Helper.B1000_0000U)
|
||||
{
|
||||
bytesConsumed = 0;
|
||||
codePoint = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
codePoint = (codePoint << 6) | (Utf8Helper.B0011_1111U & current);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool TryDecodeCodePointFromString(string s, int index, out uint codePoint, out int encodedChars)
|
||||
{
|
||||
if (index < 0 || index >= s.Length)
|
||||
{
|
||||
codePoint = default;
|
||||
encodedChars = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (index == s.Length - 1 && char.IsSurrogate(s[index]))
|
||||
{
|
||||
codePoint = default;
|
||||
encodedChars = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
encodedChars = char.IsHighSurrogate(s[index]) ? 2 : 1;
|
||||
codePoint = unchecked((uint)char.ConvertToUtf32(s, index));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static int GetEncodedBytes(byte b)
|
||||
{
|
||||
if ((b & Utf8Helper.B1000_0000U) == 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ((b & Utf8Helper.B1110_0000U) == Utf8Helper.B1100_0000U)
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
if ((b & Utf8Helper.B1111_0000U) == Utf8Helper.B1110_0000U)
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
|
||||
if ((b & Utf8Helper.B1111_1000U) == Utf8Helper.B1111_0000U)
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
#pragma warning disable SA1310 // Field names should not contain underscore
|
||||
private const byte B0000_0111U = 0x07; //7
|
||||
private const byte B0000_1111U = 0x0F; //15
|
||||
private const byte B0001_1111U = 0x1F; //31
|
||||
private const byte B0011_1111U = 0x3F; //63
|
||||
private const byte B1000_0000U = 0x80; //128
|
||||
private const byte B1100_0000U = 0xC0; //192
|
||||
private const byte B1110_0000U = 0xE0; //224
|
||||
private const byte B1111_0000U = 0xF0; //240
|
||||
private const byte B1111_1000U = 0xF8; //248
|
||||
|
||||
// ReSharper restore InconsistentNaming
|
||||
#pragma warning restore SA1310 // Field names should not contain underscore
|
||||
}
|
||||
}
|
||||
397
src/Core/Core/Utf8/Utf8Span.cs
Normal file
397
src/Core/Core/Utf8/Utf8Span.cs
Normal file
@@ -0,0 +1,397 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma warning disable CA1066 // Type {0} should implement IEquatable<T> because it overrides Equals
|
||||
|
||||
namespace Microsoft.Azure.Cosmos.Core.Utf8
|
||||
{
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
|
||||
// ReSharper disable once UseNameofExpression
|
||||
[DebuggerDisplay("{ToString()}")]
|
||||
public readonly ref struct Utf8Span
|
||||
{
|
||||
public static Utf8Span Empty => default;
|
||||
|
||||
private readonly ReadOnlySpan<byte> buffer;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private Utf8Span(ReadOnlySpan<byte> utf8Bytes)
|
||||
{
|
||||
this.buffer = utf8Bytes;
|
||||
}
|
||||
|
||||
/// <summary>Parses the sequence of bytes to prove it is valid UTF8.</summary>
|
||||
/// <param name="utf8Bytes">The bytes to validate.</param>
|
||||
/// <param name="span">
|
||||
/// If the sequence validates a <see cref="Utf8Span" /> that wraps the bytes in
|
||||
/// <paramref name="utf8Bytes" />, otherwise <see cref="t:default" />.
|
||||
/// </param>
|
||||
/// <returns>True if the sequence validates, false otherwise.</returns>
|
||||
public static bool TryParseUtf8Bytes(ReadOnlySpan<byte> utf8Bytes, out Utf8Span span)
|
||||
{
|
||||
int invalidIndex = Utf8Util.GetIndexOfFirstInvalidUtf8Sequence(utf8Bytes, out int _, out int _);
|
||||
if (invalidIndex != -1)
|
||||
{
|
||||
span = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
span = new Utf8Span(utf8Bytes);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>Creates a <see cref="Utf8Span" /> without validating the underlying bytes.</summary>
|
||||
/// <param name="utf8Bytes">The bytes claiming to be UTF8.</param>
|
||||
/// <returns>A <see cref="Utf8Span" /> wrapping <paramref name="utf8Bytes" />.</returns>
|
||||
/// <remarks>
|
||||
/// This method is dangerous as consumers of the <see cref="Utf8Span" /> must assume the
|
||||
/// underlying bytes are indeed valid UTF8. The method should <bold>only</bold> be used when the UTF8
|
||||
/// sequence has already been externally valid or is known to be valid by construction.
|
||||
/// </remarks>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Utf8Span UnsafeFromUtf8BytesNoValidation(ReadOnlySpan<byte> utf8Bytes)
|
||||
{
|
||||
return new Utf8Span(utf8Bytes);
|
||||
}
|
||||
|
||||
/// <summary>Creates a <see cref="Utf8Span" /> from a UTF16 encoding string.</summary>
|
||||
/// <param name="utf16String">The UTF16 encoding string.</param>
|
||||
/// <returns>A new <see cref="Utf8Span" />.</returns>
|
||||
/// <remarks>
|
||||
/// This method must transcode the UTF16 into UTF8 which both requires allocation and is a
|
||||
/// size of data operation.
|
||||
/// </remarks>
|
||||
public static Utf8Span TranscodeUtf16(string utf16String)
|
||||
{
|
||||
Contract.Requires(utf16String != null);
|
||||
|
||||
if (string.IsNullOrEmpty(utf16String))
|
||||
{
|
||||
return new Utf8Span(ReadOnlySpan<byte>.Empty);
|
||||
}
|
||||
|
||||
return new Utf8Span(Encoding.UTF8.GetBytes(utf16String));
|
||||
}
|
||||
|
||||
/// <summary>The UTF8 byte sequence.</summary>
|
||||
public ReadOnlySpan<byte> Span
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => this.buffer;
|
||||
}
|
||||
|
||||
/// <summary>The length in bytes of the UTF8 encoding.</summary>
|
||||
public int Length => this.Span.Length;
|
||||
|
||||
/// <summary>True if the length is empty.</summary>
|
||||
public bool IsEmpty => this.Span.Length == 0;
|
||||
|
||||
/// <summary>Non-allocating enumeration of each code point in the UTF8 stream.</summary>
|
||||
public Utf8CodePointEnumerator GetEnumerator()
|
||||
{
|
||||
return new Utf8CodePointEnumerator(this.buffer);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (this.buffer.IsEmpty)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
unsafe
|
||||
{
|
||||
// ReSharper disable once ImpureMethodCallOnReadonlyValueField
|
||||
fixed (byte* bytes = &this.buffer.GetPinnableReference())
|
||||
{
|
||||
return Encoding.UTF8.GetString(bytes, this.buffer.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool ReferenceEquals(Utf8Span other)
|
||||
{
|
||||
return this.buffer == other.buffer;
|
||||
}
|
||||
|
||||
public bool Equals(Utf8Span other)
|
||||
{
|
||||
return this.buffer.SequenceEqual(other.buffer);
|
||||
}
|
||||
|
||||
public bool Equals(string other)
|
||||
{
|
||||
Contract.Requires(other != null);
|
||||
|
||||
Utf8CodePointEnumerator thisEnumerator = this.GetEnumerator();
|
||||
Utf16LittleEndianCodePointEnumerator otherEnumerator = new Utf16LittleEndianCodePointEnumerator(other);
|
||||
|
||||
while (true)
|
||||
{
|
||||
bool hasNext = thisEnumerator.MoveNext();
|
||||
if (hasNext != otherEnumerator.MoveNext())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!hasNext)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (thisEnumerator.Current != otherEnumerator.Current)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
switch (obj)
|
||||
{
|
||||
case string s:
|
||||
return this.Equals(s);
|
||||
case Utf8String s:
|
||||
return this.Equals(s.Span);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
uint hash1 = 5381;
|
||||
uint hash2 = hash1;
|
||||
|
||||
Utf8CodePointEnumerator thisEnumerator = this.GetEnumerator();
|
||||
for (int i = 0; thisEnumerator.MoveNext(); i++)
|
||||
{
|
||||
uint c = thisEnumerator.Current;
|
||||
if (i % 2 == 0)
|
||||
{
|
||||
hash1 = ((hash1 << 5) + hash1) ^ c;
|
||||
}
|
||||
else
|
||||
{
|
||||
hash2 = ((hash2 << 5) + hash2) ^ c;
|
||||
}
|
||||
}
|
||||
|
||||
return (int)(hash1 + (hash2 * 1566083941));
|
||||
}
|
||||
}
|
||||
|
||||
public static bool operator ==(Utf8Span left, Utf8Span right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator !=(Utf8Span left, Utf8Span right)
|
||||
{
|
||||
return !left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator ==(Utf8Span left, string right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator !=(Utf8Span left, string right)
|
||||
{
|
||||
return !left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator ==(string left, Utf8Span right)
|
||||
{
|
||||
return right.Equals(left);
|
||||
}
|
||||
|
||||
public static bool operator !=(string left, Utf8Span right)
|
||||
{
|
||||
return !right.Equals(left);
|
||||
}
|
||||
|
||||
public static bool operator <(Utf8Span left, Utf8Span right)
|
||||
{
|
||||
return left.CompareTo(right) < 0;
|
||||
}
|
||||
|
||||
public static bool operator <=(Utf8Span left, Utf8Span right)
|
||||
{
|
||||
return left.CompareTo(right) <= 0;
|
||||
}
|
||||
|
||||
public static bool operator >(Utf8Span left, Utf8Span right)
|
||||
{
|
||||
return left.CompareTo(right) > 0;
|
||||
}
|
||||
|
||||
public static bool operator >=(Utf8Span left, Utf8Span right)
|
||||
{
|
||||
return left.CompareTo(right) >= 0;
|
||||
}
|
||||
|
||||
public static bool operator <(Utf8Span left, string right)
|
||||
{
|
||||
return left.CompareTo(right) < 0;
|
||||
}
|
||||
|
||||
public static bool operator <=(Utf8Span left, string right)
|
||||
{
|
||||
return left.CompareTo(right) <= 0;
|
||||
}
|
||||
|
||||
public static bool operator >(Utf8Span left, string right)
|
||||
{
|
||||
return left.CompareTo(right) > 0;
|
||||
}
|
||||
|
||||
public static bool operator >=(Utf8Span left, string right)
|
||||
{
|
||||
return left.CompareTo(right) >= 0;
|
||||
}
|
||||
|
||||
public static bool operator <(string left, Utf8Span right)
|
||||
{
|
||||
return right.CompareTo(left) >= 0;
|
||||
}
|
||||
|
||||
public static bool operator <=(string left, Utf8Span right)
|
||||
{
|
||||
return right.CompareTo(left) > 0;
|
||||
}
|
||||
|
||||
public static bool operator >(string left, Utf8Span right)
|
||||
{
|
||||
return right.CompareTo(left) <= 0;
|
||||
}
|
||||
|
||||
public static bool operator >=(string left, Utf8Span right)
|
||||
{
|
||||
return right.CompareTo(left) < 0;
|
||||
}
|
||||
|
||||
public int CompareTo(Utf8Span other)
|
||||
{
|
||||
ReadOnlySpan<byte> left = this.Span;
|
||||
ReadOnlySpan<byte> right = other.Span;
|
||||
int minLength = left.Length;
|
||||
if (minLength > right.Length)
|
||||
{
|
||||
minLength = right.Length;
|
||||
}
|
||||
|
||||
for (int i = 0; i < minLength; i++)
|
||||
{
|
||||
int result = left[i].CompareTo(right[i]);
|
||||
if (result != 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return left.Length.CompareTo(right.Length);
|
||||
}
|
||||
|
||||
public int CompareTo(string other)
|
||||
{
|
||||
Contract.Requires(other != null);
|
||||
|
||||
Utf8CodePointEnumerator thisEnumerator = this.GetEnumerator();
|
||||
Utf16LittleEndianCodePointEnumerator otherEnumerator = new Utf16LittleEndianCodePointEnumerator(other);
|
||||
|
||||
while (true)
|
||||
{
|
||||
bool thisHasNext = thisEnumerator.MoveNext();
|
||||
bool otherHasNext = otherEnumerator.MoveNext();
|
||||
if (!thisHasNext && !otherHasNext)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!thisHasNext)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!otherHasNext)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint thisCurrent = thisEnumerator.Current;
|
||||
uint otherCurrent = otherEnumerator.Current;
|
||||
|
||||
if (thisCurrent == otherCurrent)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
return thisCurrent.CompareTo(otherCurrent);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if this <see cref="Utf8Span" /> starts with (or equals) the second.
|
||||
/// </summary>
|
||||
/// <param name="pattern">The <see cref="Utf8Span" /> to compare.</param>
|
||||
/// <returns>If starts with.</returns>
|
||||
public bool StartsWith(Utf8Span pattern)
|
||||
{
|
||||
return this.Span.StartsWith(pattern.Span);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if this <see cref="Utf8Span" /> ends with (or equals) the second.
|
||||
/// </summary>
|
||||
/// <param name="pattern">The <see cref="Utf8Span" /> to compare.</param>
|
||||
/// <returns>If starts with.</returns>
|
||||
public bool EndsWith(Utf8Span pattern)
|
||||
{
|
||||
return this.Span.EndsWith(pattern.Span);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if a specified <see cref="Utf8Span" /> occurs within this <see cref="Utf8Span" />.
|
||||
/// </summary>
|
||||
/// <param name="pattern">The <see cref="Utf8Span" /> to compare.</param>
|
||||
/// <returns>If contains.</returns>
|
||||
public bool Contains(Utf8Span pattern)
|
||||
{
|
||||
return this.Span.IndexOf(pattern.Span) >= 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Splits a <see cref="Utf8Span" /> around first occurrence of <paramref name="pattern"/> into the left and right segments.
|
||||
/// The pattern is not included in either left or right results.
|
||||
/// </summary>
|
||||
/// <param name="pattern">The <see cref="Utf8Span" /> split around.</param>
|
||||
/// <param name="left">The <see cref="Utf8Span" /> before the pattern.</param>
|
||||
/// <param name="right">The <see cref="Utf8Span" /> after the pattern.</param>
|
||||
/// <returns>True if success, false if does not contain pattern.</returns>
|
||||
public bool TrySplitFirst(Utf8Span pattern, out Utf8Span left, out Utf8Span right)
|
||||
{
|
||||
int indexOfPattern = this.Span.IndexOf(pattern.Span);
|
||||
|
||||
if (indexOfPattern < 0)
|
||||
{
|
||||
left = default;
|
||||
right = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
left = new Utf8Span(this.Span.Slice(0, indexOfPattern));
|
||||
right = new Utf8Span(this.Span.Slice(indexOfPattern + pattern.Length));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
382
src/Core/Core/Utf8/Utf8String.cs
Normal file
382
src/Core/Core/Utf8/Utf8String.cs
Normal file
@@ -0,0 +1,382 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma warning disable CA1066 // Type {0} should implement IEquatable<T> because it overrides Equals
|
||||
#pragma warning disable CA2225 // Operator overloads have named alternates
|
||||
#pragma warning disable IDE0041 // Use 'is null' check
|
||||
|
||||
namespace Microsoft.Azure.Cosmos.Core.Utf8
|
||||
{
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
|
||||
// ReSharper disable once UseNameofExpression
|
||||
[DebuggerDisplay("{ToString()}")]
|
||||
public class Utf8String : IEquatable<Utf8String>, IComparable<Utf8String>, IEquatable<string>, IComparable<string>
|
||||
{
|
||||
private readonly ReadOnlyMemory<byte> buffer;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private Utf8String(ReadOnlyMemory<byte> utf8Bytes)
|
||||
{
|
||||
this.buffer = utf8Bytes;
|
||||
}
|
||||
|
||||
public static readonly Utf8String Empty = new Utf8String(default);
|
||||
|
||||
/// <summary>The UTF8 byte sequence.</summary>
|
||||
public Utf8Span Span
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => Utf8Span.UnsafeFromUtf8BytesNoValidation(this.buffer.Span);
|
||||
}
|
||||
|
||||
/// <summary>The length in bytes of the UTF8 encoding.</summary>
|
||||
public int Length => this.buffer.Length;
|
||||
|
||||
/// <summary>True if the length is empty.</summary>
|
||||
public bool IsEmpty => this.buffer.Length == 0;
|
||||
|
||||
/// <summary>Parses the sequence of bytes to prove it is valid UTF8.</summary>
|
||||
/// <param name="utf8Bytes">The bytes to validate.</param>
|
||||
/// <param name="str">
|
||||
/// If the sequence validates a <see cref="Utf8String" /> that wraps the bytes in
|
||||
/// <paramref name="utf8Bytes" />, otherwise <see cref="t:default" />.
|
||||
/// </param>
|
||||
/// <remarks>The new <see cref="Utf8String"/> takes ownership of <paramref name="utf8Bytes"/>.</remarks>
|
||||
/// <returns>True if the sequence validates, false otherwise.</returns>
|
||||
public static bool TryParseUtf8Bytes(ReadOnlyMemory<byte> utf8Bytes, out Utf8String str)
|
||||
{
|
||||
int invalidIndex = Utf8Util.GetIndexOfFirstInvalidUtf8Sequence(utf8Bytes.Span, out int _, out int _);
|
||||
if (invalidIndex != -1)
|
||||
{
|
||||
str = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
str = new Utf8String(utf8Bytes);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>Creates a <see cref="Utf8String" /> without validating the underlying bytes.</summary>
|
||||
/// <param name="utf8Bytes">The bytes claiming to be UTF8.</param>
|
||||
/// <returns>A <see cref="Utf8String" /> wrapping <paramref name="utf8Bytes" />.</returns>
|
||||
/// <remarks>
|
||||
/// This method is dangerous as consumers of the <see cref="Utf8String" /> must assume the
|
||||
/// underlying bytes are indeed valid UTF8. The method should <bold>only</bold> be used when the UTF8
|
||||
/// sequence has already been externally valid or is known to be valid by construction.
|
||||
/// </remarks>
|
||||
/// <remarks>The new <see cref="Utf8String"/> takes ownership of <paramref name="utf8Bytes"/>.</remarks>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Utf8String UnsafeFromUtf8BytesNoValidation(ReadOnlyMemory<byte> utf8Bytes)
|
||||
{
|
||||
Contract.Assert(Utf8Util.GetIndexOfFirstInvalidUtf8Sequence(utf8Bytes.Span, out int _, out int _) == -1);
|
||||
return new Utf8String(utf8Bytes);
|
||||
}
|
||||
|
||||
/// <summary>Creates a <see cref="Utf8String" /> from a <see cref="Utf8Span"/>.</summary>
|
||||
/// <param name="span">The bytes that are UTF8.</param>
|
||||
/// <returns>A <see cref="Utf8String" /> with contents from <paramref name="span" />.</returns>
|
||||
public static Utf8String CopyFrom(Utf8Span span)
|
||||
{
|
||||
Contract.Assert(Utf8Util.GetIndexOfFirstInvalidUtf8Sequence(span.Span, out int _, out int _) == -1);
|
||||
return new Utf8String(span.Span.ToArray());
|
||||
}
|
||||
|
||||
/// <summary>Creates a <see cref="Utf8String" /> from a UTF16 encoding string.</summary>
|
||||
/// <param name="utf16String">The UTF16 encoding string.</param>
|
||||
/// <returns>A new <see cref="Utf8String" />.</returns>
|
||||
/// <remarks>
|
||||
/// This method must transcode the UTF16 into UTF8 which both requires allocation and is a
|
||||
/// size of data operation.
|
||||
/// </remarks>
|
||||
public static Utf8String TranscodeUtf16(string utf16String)
|
||||
{
|
||||
if (object.ReferenceEquals(utf16String, null))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(utf16String))
|
||||
{
|
||||
return new Utf8String(ReadOnlyMemory<byte>.Empty);
|
||||
}
|
||||
|
||||
return new Utf8String(Encoding.UTF8.GetBytes(utf16String));
|
||||
}
|
||||
|
||||
/// <summary><see cref="Utf8Span" /> over the string's content.</summary>
|
||||
/// <param name="utf8String">The string whose content is returned.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Utf8Span(Utf8String utf8String)
|
||||
{
|
||||
return (utf8String == null) ? default : utf8String.Span;
|
||||
}
|
||||
|
||||
public static implicit operator UtfAnyString(Utf8String utf8String)
|
||||
{
|
||||
return new UtfAnyString(utf8String);
|
||||
}
|
||||
|
||||
public static bool operator ==(Utf8String left, Utf8String right)
|
||||
{
|
||||
if (object.ReferenceEquals(null, left))
|
||||
{
|
||||
return object.ReferenceEquals(null, right);
|
||||
}
|
||||
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator !=(Utf8String left, Utf8String right)
|
||||
{
|
||||
if (object.ReferenceEquals(null, left))
|
||||
{
|
||||
return !object.ReferenceEquals(null, right);
|
||||
}
|
||||
|
||||
return !left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator <(Utf8String left, Utf8String right)
|
||||
{
|
||||
return object.ReferenceEquals(left, null) ? !object.ReferenceEquals(right, null) : left.CompareTo(right) < 0;
|
||||
}
|
||||
|
||||
public static bool operator <=(Utf8String left, Utf8String right)
|
||||
{
|
||||
return object.ReferenceEquals(left, null) || left.CompareTo(right) <= 0;
|
||||
}
|
||||
|
||||
public static bool operator >(Utf8String left, Utf8String right)
|
||||
{
|
||||
return !object.ReferenceEquals(left, null) && left.CompareTo(right) > 0;
|
||||
}
|
||||
|
||||
public static bool operator >=(Utf8String left, Utf8String right)
|
||||
{
|
||||
return object.ReferenceEquals(left, null) ? object.ReferenceEquals(right, null) : left.CompareTo(right) >= 0;
|
||||
}
|
||||
|
||||
public static bool operator <(Utf8String left, string right)
|
||||
{
|
||||
return object.ReferenceEquals(left, null) ? !object.ReferenceEquals(right, null) : left.CompareTo(right) < 0;
|
||||
}
|
||||
|
||||
public static bool operator <=(Utf8String left, string right)
|
||||
{
|
||||
return object.ReferenceEquals(left, null) || left.CompareTo(right) <= 0;
|
||||
}
|
||||
|
||||
public static bool operator >(Utf8String left, string right)
|
||||
{
|
||||
return !object.ReferenceEquals(left, null) && left.CompareTo(right) > 0;
|
||||
}
|
||||
|
||||
public static bool operator >=(Utf8String left, string right)
|
||||
{
|
||||
return object.ReferenceEquals(left, null) ? object.ReferenceEquals(right, null) : left.CompareTo(right) >= 0;
|
||||
}
|
||||
|
||||
public static bool operator <(string left, Utf8String right)
|
||||
{
|
||||
return object.ReferenceEquals(right, null) ? object.ReferenceEquals(left, null) : right.CompareTo(left) >= 0;
|
||||
}
|
||||
|
||||
public static bool operator <=(string left, Utf8String right)
|
||||
{
|
||||
return !object.ReferenceEquals(right, null) && right.CompareTo(left) > 0;
|
||||
}
|
||||
|
||||
public static bool operator >(string left, Utf8String right)
|
||||
{
|
||||
return object.ReferenceEquals(right, null) || right.CompareTo(left) <= 0;
|
||||
}
|
||||
|
||||
public static bool operator >=(string left, Utf8String right)
|
||||
{
|
||||
return object.ReferenceEquals(right, null) ? !object.ReferenceEquals(left, null) : right.CompareTo(left) < 0;
|
||||
}
|
||||
|
||||
/// <summary>Non-allocating enumeration of each code point in the UTF8 stream.</summary>
|
||||
public Utf8CodePointEnumerator GetEnumerator()
|
||||
{
|
||||
return this.Span.GetEnumerator();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return this.Span.ToString();
|
||||
}
|
||||
|
||||
public bool Equals(Utf8Span other)
|
||||
{
|
||||
return this.Span.Equals(other);
|
||||
}
|
||||
|
||||
public bool Equals(Utf8String other)
|
||||
{
|
||||
if (object.ReferenceEquals(null, other))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (object.ReferenceEquals(this, other))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return this.Span.Equals(other.Span);
|
||||
}
|
||||
|
||||
public bool Equals(string other)
|
||||
{
|
||||
if (object.ReferenceEquals(null, other))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.Span.Equals(other);
|
||||
}
|
||||
|
||||
public override bool Equals(object other)
|
||||
{
|
||||
if (object.ReferenceEquals(null, other))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (object.ReferenceEquals(this, other))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return this.Span.Equals(other);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return this.Span.GetHashCode();
|
||||
}
|
||||
|
||||
public int CompareTo(Utf8String other)
|
||||
{
|
||||
if (other == null)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
return this.Span.CompareTo(other.Span);
|
||||
}
|
||||
|
||||
public int CompareTo(string other)
|
||||
{
|
||||
if (other == null)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
return this.Span.CompareTo(other);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Splits a <see cref="Utf8String" /> around first occurrence of <paramref name="pattern"/> into the left and right segments.
|
||||
/// The pattern is not included in either left or right results.
|
||||
/// </summary>
|
||||
/// <param name="pattern">The <see cref="Utf8Span" /> split around.</param>
|
||||
/// <param name="left">The <see cref="Utf8String" /> before the pattern.</param>
|
||||
/// <param name="right">The <see cref="Utf8String" /> after the pattern.</param>
|
||||
/// <returns>True if success, false if does not contain pattern.</returns>
|
||||
public bool TrySplitFirst(Utf8Span pattern, out Utf8String left, out Utf8String right)
|
||||
{
|
||||
int indexOfPattern = this.buffer.Span.IndexOf(pattern.Span);
|
||||
|
||||
if (indexOfPattern < 0)
|
||||
{
|
||||
left = default;
|
||||
right = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
left = new Utf8String(this.buffer.Slice(0, indexOfPattern));
|
||||
right = new Utf8String(this.buffer.Slice(indexOfPattern + pattern.Length));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes given <see cref="Utf8Span" /> from start and outputs resultant <see cref="Utf8String" />.
|
||||
/// </summary>
|
||||
/// <param name="pattern">The <see cref="Utf8Span" /> to remove.</param>
|
||||
/// <param name="output">The <see cref="Utf8String" /> with value removed from start.</param>
|
||||
/// <returns>Is success.</returns>
|
||||
public bool TryTrimLeft(Utf8Span pattern, out Utf8String output)
|
||||
{
|
||||
if (!this.Span.StartsWith(pattern))
|
||||
{
|
||||
output = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
output = new Utf8String(this.buffer.Slice(pattern.Length));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes given <see cref="Utf8Span" /> from end and outputs resultant <see cref="Utf8String" />.
|
||||
/// </summary>
|
||||
/// <param name="pattern">The <see cref="Utf8Span" /> to remove.</param>
|
||||
/// <param name="output">The <see cref="Utf8String" /> with value removed from end.</param>
|
||||
/// <returns>Is success.</returns>
|
||||
public bool TryTrimRight(Utf8Span pattern, out Utf8String output)
|
||||
{
|
||||
if (!this.Span.EndsWith(pattern))
|
||||
{
|
||||
output = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
output = new Utf8String(this.buffer.Slice(0, this.buffer.Length - pattern.Length));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes given <paramref name="leftPattern"/> from the start and <paramref name="rightPattern"/>
|
||||
/// from the end and outputs resultant <see cref="Utf8String" />.
|
||||
/// </summary>
|
||||
/// <param name="leftPattern">The <see cref="Utf8Span" /> to remove from left.</param>
|
||||
/// <param name="rightPattern">The <see cref="Utf8Span" /> to remove from right.</param>
|
||||
/// <param name="output">The <see cref="Utf8String" /> with value removed from end.</param>
|
||||
/// <remarks>Will return false if patterns overlap in string to trim.</remarks>
|
||||
/// <returns>Is success.</returns>
|
||||
public bool TryTrim(Utf8Span leftPattern, Utf8Span rightPattern, out Utf8String output)
|
||||
{
|
||||
if (!this.Span.StartsWith(leftPattern) ||
|
||||
!this.Span.EndsWith(rightPattern) ||
|
||||
(this.buffer.Length < leftPattern.Length + rightPattern.Length))
|
||||
{
|
||||
output = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
output = new Utf8String(this.buffer.Slice(leftPattern.Length, this.buffer.Length - leftPattern.Length - rightPattern.Length));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the specified <see cref="Utf8String"/> is null or an empty.
|
||||
/// </summary>
|
||||
/// <param name="utf8String"></param>
|
||||
/// <returns>If null or empty.</returns>
|
||||
public static bool IsNullOrEmpty(Utf8String utf8String)
|
||||
{
|
||||
return utf8String == null || utf8String.IsEmpty;
|
||||
}
|
||||
}
|
||||
}
|
||||
419
src/Core/Core/Utf8/Utf8Util.Helpers.cs
Normal file
419
src/Core/Core/Utf8/Utf8Util.Helpers.cs
Normal file
@@ -0,0 +1,419 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma warning disable SA1601 // Partial elements should be documented
|
||||
#pragma warning disable SA1119 // Statement should not use unnecessary parenthesis
|
||||
|
||||
//
|
||||
// This file contains utility methods shared by the UTF-8 workhorse methods.
|
||||
namespace Microsoft.Azure.Cosmos.Core.Utf8
|
||||
{
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
internal static partial class Utf8Util
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns <see langword="true" /> iff <paramref name="value" /> is between
|
||||
/// <paramref name="lowerBound" /> and <paramref name="upperBound" />, inclusive.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal static bool IsInRangeInclusive(uint value, uint lowerBound, uint upperBound)
|
||||
{
|
||||
return unchecked((value - lowerBound) <= (upperBound - lowerBound));
|
||||
}
|
||||
|
||||
/// <summary>Casts an <see cref="IntPtr" /> to an <see cref="int" /> without overflow checking.</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static int ConvertIntPtrToInt32WithoutOverflowCheck(IntPtr value)
|
||||
{
|
||||
if (IntPtr.Size == 4)
|
||||
{
|
||||
return (int)value;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (int)(long)value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given a 24-bit integer which represents a three-byte buffer read in machine endianness,
|
||||
/// counts the number of consecutive ASCII bytes starting from the beginning of the buffer. Returns a
|
||||
/// value 0 - 3, inclusive.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static uint CountNumberOfLeadingAsciiBytesFrom24BitInteger(uint value)
|
||||
{
|
||||
// The 'allBytesUpToNowAreAscii' DWORD uses bit twiddling to hold a 1 or a 0 depending
|
||||
// on whether all processed bytes were ASCII. Then we accumulate all of the
|
||||
// results to calculate how many consecutive ASCII bytes are present.
|
||||
value = ~value;
|
||||
|
||||
if (BitConverter.IsLittleEndian)
|
||||
{
|
||||
// Read first byte
|
||||
uint allBytesUpToNowAreAscii = (value >>= 7) & 1;
|
||||
uint numAsciiBytes = allBytesUpToNowAreAscii;
|
||||
|
||||
// Read second byte
|
||||
allBytesUpToNowAreAscii &= (value >>= 8);
|
||||
numAsciiBytes += allBytesUpToNowAreAscii;
|
||||
|
||||
// Read third byte
|
||||
allBytesUpToNowAreAscii &= (value >>= 8);
|
||||
numAsciiBytes += allBytesUpToNowAreAscii;
|
||||
|
||||
return numAsciiBytes;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Read first byte
|
||||
uint allBytesUpToNowAreAscii = (value = Utf8Util.ROL32(value, 1)) & 1;
|
||||
uint numAsciiBytes = allBytesUpToNowAreAscii;
|
||||
|
||||
// Read second byte
|
||||
allBytesUpToNowAreAscii &= (value = Utf8Util.ROL32(value, 8));
|
||||
numAsciiBytes += allBytesUpToNowAreAscii;
|
||||
|
||||
// Read third byte
|
||||
allBytesUpToNowAreAscii &= (_ = Utf8Util.ROL32(value, 8));
|
||||
numAsciiBytes += allBytesUpToNowAreAscii;
|
||||
|
||||
return numAsciiBytes;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Returns <see langword="true" /> iff all bytes in <paramref name="value" /> are ASCII.</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool DWordAllBytesAreAscii(uint value)
|
||||
{
|
||||
return ((value & 0x80808080U) == 0U);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given a UTF-8 buffer which has been read into a DWORD in machine endianness, returns
|
||||
/// <see langword="true" /> iff the buffer contains two UTF-8 sequences that match the mask [ 110yyyyy
|
||||
/// 10xxxxxx 110yyyyy 10xxxxxx ]. This method *does not* validate that the sequences are well-formed;
|
||||
/// the caller must still perform overlong form checking.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool DWordBeginsAndEndsWithUtf8TwoByteMask(uint value)
|
||||
{
|
||||
// The code in this method is equivalent to the code
|
||||
// below, but the JIT is able to inline + optimize it
|
||||
// better in release builds.
|
||||
//
|
||||
// if (BitConverter.IsLittleEndian)
|
||||
// {
|
||||
// const uint mask = 0xC0E0C0E0U;
|
||||
// const uint comparand = 0x80C080C0U;
|
||||
// return ((value & mask) == comparand);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// const uint mask = 0xE0C0E0C0U;
|
||||
// const uint comparand = 0xC080C080U;
|
||||
// return ((value & mask) == comparand);
|
||||
// }
|
||||
return (BitConverter.IsLittleEndian && ((value & 0xC0E0C0E0U) == 0x80C080C0U)) ||
|
||||
(!BitConverter.IsLittleEndian && ((value & 0xE0C0E0C0U) == 0xC080C080U));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given a UTF-8 buffer which has been read into a DWORD in machine endianness, returns
|
||||
/// <see langword="true" /> iff the first two bytes of the buffer are an overlong representation of a
|
||||
/// sequence that should be represented as one byte. This method *does not* validate that the sequence
|
||||
/// matches the appropriate 2-byte sequence mask (see <see cref="DWordBeginsWithUtf8TwoByteMask" />).
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool DWordBeginsWithOverlongUtf8TwoByteSequence(uint value)
|
||||
{
|
||||
// ASSUMPTION: Caller has already checked the '110yyyyy 10xxxxxx' mask of the input.
|
||||
Contract.Assert(Utf8Util.DWordBeginsWithUtf8TwoByteMask(value));
|
||||
|
||||
// Per Table 3-7, first byte of two-byte sequence must be within range C2 .. DF.
|
||||
// Since we already validated it's 80 <= ?? <= DF (per mask check earlier), now only need
|
||||
// to check that it's < C2.
|
||||
return (BitConverter.IsLittleEndian && (unchecked((byte)value) < (byte)0xC2)) || (!BitConverter.IsLittleEndian && (value < 0xC2000000U));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given a UTF-8 buffer which has been read into a DWORD in machine endianness, returns
|
||||
/// <see langword="true" /> iff the first four bytes of the buffer match the UTF-8 4-byte sequence mask
|
||||
/// [ 11110www 10zzzzzz 10yyyyyy 10xxxxxx ]. This method *does not* validate that the sequence is
|
||||
/// well-formed; the caller must still perform overlong form or out-of-range checking.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool DWordBeginsWithUtf8FourByteMask(uint value)
|
||||
{
|
||||
// The code in this method is equivalent to the code
|
||||
// below, but the JIT is able to inline + optimize it
|
||||
// better in release builds.
|
||||
//
|
||||
// if (BitConverter.IsLittleEndian)
|
||||
// {
|
||||
// const uint mask = 0xC0C0C0F8U;
|
||||
// const uint comparand = 0x808080F0U;
|
||||
// return ((value & mask) == comparand);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// const uint mask = 0xF8C0C0C0U;
|
||||
// const uint comparand = 0xF0808000U;
|
||||
// return ((value & mask) == comparand);
|
||||
// }
|
||||
return (BitConverter.IsLittleEndian && ((value & 0xC0C0C0F8U) == 0x808080F0U)) ||
|
||||
(!BitConverter.IsLittleEndian && ((value & 0xF8C0C0C0U) == 0xF0808000U));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given a UTF-8 buffer which has been read into a DWORD in machine endianness, returns
|
||||
/// <see langword="true" /> iff the first three bytes of the buffer match the UTF-8 3-byte sequence
|
||||
/// mask [ 1110zzzz 10yyyyyy 10xxxxxx ]. This method *does not* validate that the sequence is
|
||||
/// well-formed; the caller must still perform overlong form or surrogate checking.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool DWordBeginsWithUtf8ThreeByteMask(uint value)
|
||||
{
|
||||
// The code in this method is equivalent to the code
|
||||
// below, but the JIT is able to inline + optimize it
|
||||
// better in release builds.
|
||||
//
|
||||
// if (BitConverter.IsLittleEndian)
|
||||
// {
|
||||
// const uint mask = 0x00C0C0F0U;
|
||||
// const uint comparand = 0x008080E0U;
|
||||
// return ((value & mask) == comparand);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// const uint mask = 0xF0C0C000U;
|
||||
// const uint comparand = 0xE0808000U;
|
||||
// return ((value & mask) == comparand);
|
||||
// }
|
||||
return (BitConverter.IsLittleEndian && ((value & 0x00C0C0F0U) == 0x008080E0U)) ||
|
||||
(!BitConverter.IsLittleEndian && ((value & 0xF0C0C000U) == 0xE0808000U));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given a UTF-8 buffer which has been read into a DWORD in machine endianness, returns
|
||||
/// <see langword="true" /> iff the first two bytes of the buffer match the UTF-8 2-byte sequence mask
|
||||
/// [ 110yyyyy 10xxxxxx ]. This method *does not* validate that the sequence is well-formed; the caller
|
||||
/// must still perform overlong form checking.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool DWordBeginsWithUtf8TwoByteMask(uint value)
|
||||
{
|
||||
// The code in this method is equivalent to the code
|
||||
// below, but the JIT is able to inline + optimize it
|
||||
// better in release builds.
|
||||
//
|
||||
// if (BitConverter.IsLittleEndian)
|
||||
// {
|
||||
// const uint mask = 0x0000C0E0U;
|
||||
// const uint comparand = 0x000080C0U;
|
||||
// return ((value & mask) == comparand);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// const uint mask = 0xE0C00000U;
|
||||
// const uint comparand = 0xC0800000U;
|
||||
// return ((value & mask) == comparand);
|
||||
// }
|
||||
return (BitConverter.IsLittleEndian && ((value & 0x0000C0E0U) == 0x000080C0U)) ||
|
||||
(!BitConverter.IsLittleEndian && ((value & 0xE0C00000U) == 0xC0800000U));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given a UTF-8 buffer which has been read into a DWORD in machine endianness, returns
|
||||
/// <see langword="true" /> iff the first two bytes of the buffer are an overlong representation of a
|
||||
/// sequence that should be represented as one byte. This method *does not* validate that the sequence
|
||||
/// matches the appropriate 2-byte sequence mask (see <see cref="DWordBeginsWithUtf8TwoByteMask" />).
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool DWordEndsWithOverlongUtf8TwoByteSequence(uint value)
|
||||
{
|
||||
// ASSUMPTION: Caller has already checked the '110yyyyy 10xxxxxx' mask of the input.
|
||||
Contract.Assert(Utf8Util.DWordEndsWithUtf8TwoByteMask(value));
|
||||
|
||||
// Per Table 3-7, first byte of two-byte sequence must be within range C2 .. DF.
|
||||
// We already validated that it's 80 .. DF (per mask check earlier).
|
||||
// C2 = 1100 0010
|
||||
// DF = 1101 1111
|
||||
// This means that we can AND the leading byte with the mask 0001 1110 (1E),
|
||||
// and if the result is zero the sequence is overlong.
|
||||
return (BitConverter.IsLittleEndian && ((value & 0x001E0000U) == 0U)) || (!BitConverter.IsLittleEndian && ((value & 0x1E00U) == 0U));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given a UTF-8 buffer which has been read into a DWORD in machine endianness, returns
|
||||
/// <see langword="true" /> iff the last two bytes of the buffer match the UTF-8 2-byte sequence mask [
|
||||
/// 110yyyyy 10xxxxxx ]. This method *does not* validate that the sequence is well-formed; the caller
|
||||
/// must still perform overlong form checking.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool DWordEndsWithUtf8TwoByteMask(uint value)
|
||||
{
|
||||
// The code in this method is equivalent to the code
|
||||
// below, but the JIT is able to inline + optimize it
|
||||
// better in release builds.
|
||||
//
|
||||
// if (BitConverter.IsLittleEndian)
|
||||
// {
|
||||
// const uint mask = 0xC0E00000U;
|
||||
// const uint comparand = 0x80C00000U;
|
||||
// return ((value & mask) == comparand);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// const uint mask = 0x0000E0C0U;
|
||||
// const uint comparand = 0x0000C080U;
|
||||
// return ((value & mask) == comparand);
|
||||
// }
|
||||
return (BitConverter.IsLittleEndian && ((value & 0xC0E00000U) == 0x80C00000U)) ||
|
||||
(!BitConverter.IsLittleEndian && ((value & 0x0000E0C0U) == 0x0000C080U));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given a UTF-8 buffer which has been read into a DWORD on a little-endian machine, returns
|
||||
/// <see langword="true" /> iff the first two bytes of the buffer are a well-formed UTF-8 two-byte
|
||||
/// sequence. This wraps the mask check and the overlong check into a single operation. Returns
|
||||
/// <see langword="false" /> if running on a big-endian machine.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool DWordBeginsWithValidUtf8TwoByteSequenceLittleEndian(uint value)
|
||||
{
|
||||
// Per Table 3-7, valid 2-byte sequences are [ C2..DF ] [ 80..BF ].
|
||||
// In little-endian, that would be represented as:
|
||||
// [ ######## ######## 10xxxxxx 110yyyyy ].
|
||||
// Due to the little-endian representation we can perform a trick by ANDing the low
|
||||
// WORD with the bitmask [ 11000000 11111111 ] and checking that the value is within
|
||||
// the range [ 11000000_11000010, 11000000_11011111 ]. This performs both the
|
||||
// 2-byte-sequence bitmask check and overlong form validation with one comparison.
|
||||
Contract.Assert(BitConverter.IsLittleEndian);
|
||||
|
||||
return (BitConverter.IsLittleEndian && Utf8Util.IsInRangeInclusive(value & 0xC0FFU, 0x80C2U, 0x80DFU)) ||
|
||||
(!BitConverter.IsLittleEndian && false); // this line - while weird - helps JIT produce optimal code
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given a UTF-8 buffer which has been read into a DWORD on a little-endian machine, returns
|
||||
/// <see langword="true" /> iff the last two bytes of the buffer are a well-formed UTF-8 two-byte
|
||||
/// sequence. This wraps the mask check and the overlong check into a single operation. Returns
|
||||
/// <see langword="false" /> if running on a big-endian machine.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool DWordEndsWithValidUtf8TwoByteSequenceLittleEndian(uint value)
|
||||
{
|
||||
// See comments in DWordBeginsWithValidUtf8TwoByteSequenceLittleEndian.
|
||||
Contract.Assert(BitConverter.IsLittleEndian);
|
||||
|
||||
return (BitConverter.IsLittleEndian && Utf8Util.IsInRangeInclusive(value & 0xC0FF0000U, 0x80C20000U, 0x80DF0000U)) ||
|
||||
(!BitConverter.IsLittleEndian && false); // this line - while weird - helps JIT produce optimal code
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given a UTF-8 buffer which has been read into a DWORD in machine endianness, returns
|
||||
/// <see langword="true" /> iff the fourth byte of the buffer is ASCII.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool DWordFourthByteIsAscii(uint value)
|
||||
{
|
||||
// The code in this method is equivalent to the code
|
||||
// below, but the JIT is able to inline + optimize it
|
||||
// better in release builds.
|
||||
//
|
||||
// if (BitConverter.IsLittleEndian)
|
||||
// {
|
||||
// return ((int)value >= 0);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// return ((value & 0x80U) == 0U);
|
||||
// }
|
||||
return (BitConverter.IsLittleEndian && unchecked((int)value >= 0)) || (!BitConverter.IsLittleEndian && ((value & 0x80U) == 0U));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given a UTF-8 buffer which has been read into a DWORD in machine endianness, returns
|
||||
/// <see langword="true" /> iff the third byte of the buffer is ASCII.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool DWordThirdByteIsAscii(uint value)
|
||||
{
|
||||
// The code in this method is equivalent to the code
|
||||
// below, but the JIT is able to inline + optimize it
|
||||
// better in release builds.
|
||||
//
|
||||
// if (BitConverter.IsLittleEndian)
|
||||
// {
|
||||
// return ((value & 0x800000U) == 0U);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// return ((value & 0x8000U) == 0U);
|
||||
// }
|
||||
return (BitConverter.IsLittleEndian && ((value & 0x800000U) == 0U)) || (!BitConverter.IsLittleEndian && ((value & 0x8000U) == 0U));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given a memory reference, returns the number of bytes that must be added to the reference
|
||||
/// before the reference is DWORD-aligned. Returns a number in the range 0 - 3, inclusive.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static unsafe int GetNumberOfBytesToNextDWordAlignment(ref byte @ref)
|
||||
{
|
||||
return unchecked((int)((uint)sizeof(uint) - ((uint)Unsafe.AsPointer(ref @ref) % sizeof(uint))));
|
||||
}
|
||||
|
||||
/// <summary>Returns <see langword="true" /> iff (<paramref name="a" /> <= <paramref name="b" />).</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static unsafe bool IntPtrIsLessThanOrEqualTo(IntPtr a, IntPtr b)
|
||||
{
|
||||
return (a.ToPointer() <= b.ToPointer());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true" /> iff the low byte of <paramref name="value" /> is a UTF-8
|
||||
/// continuation byte (10xxxxxx).
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool IsUtf8ContinuationByte(uint value)
|
||||
{
|
||||
return ((value & 0xC0U) == 0x80U);
|
||||
}
|
||||
|
||||
/// <summary>Returns the OR of the next two DWORDs in the buffer.</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static uint ReadAndFoldTwoDWordsUnaligned(ref byte buffer)
|
||||
{
|
||||
return Unsafe.ReadUnaligned<uint>(ref buffer) | Unsafe.ReadUnaligned<uint>(ref Unsafe.Add(ref buffer, sizeof(uint)));
|
||||
}
|
||||
|
||||
/// <summary>Returns the OR of the next two QWORDs in the buffer.</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static ulong ReadAndFoldTwoQWordsUnaligned(ref byte buffer)
|
||||
{
|
||||
return Unsafe.ReadUnaligned<ulong>(ref buffer) | Unsafe.ReadUnaligned<ulong>(ref Unsafe.Add(ref buffer, sizeof(ulong)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rotates a DWORD left. The JIT is smart enough to turn this into a ROL / ROR
|
||||
/// instruction.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static uint ROL32(uint value, int shift)
|
||||
{
|
||||
return (value << shift) | (value >> (32 - shift));
|
||||
}
|
||||
|
||||
/// <summary>Returns <see langword="true" /> iff all bytes in <paramref name="value" /> are ASCII.</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool QWordAllBytesAreAscii(ulong value)
|
||||
{
|
||||
return ((value & 0x8080808080808080UL) == 0UL);
|
||||
}
|
||||
}
|
||||
}
|
||||
752
src/Core/Core/Utf8/Utf8Util.Validation.cs
Normal file
752
src/Core/Core/Utf8/Utf8Util.Validation.cs
Normal file
@@ -0,0 +1,752 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma warning disable SA1512 // Single-line comments should not be followed by blank line
|
||||
#pragma warning disable SA1601 // Partial elements should be documented
|
||||
|
||||
//
|
||||
// This file contains workhorse methods for performing validation of UTF-8 byte sequences.
|
||||
namespace Microsoft.Azure.Cosmos.Core.Utf8
|
||||
{
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
internal static partial class Utf8Util
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the offset in <paramref name="input" /> of where the first invalid UTF-8 sequence
|
||||
/// appears, or -1 if the input is valid UTF-8 text. (Empty inputs are considered valid.) On method
|
||||
/// return the <paramref name="scalarCount" /> parameter will contain the total number of Unicode
|
||||
/// scalar values seen up to (but not including) the first invalid sequence, and
|
||||
/// <paramref name="surrogatePairCount" /> will contain the number of surrogate pairs present if this
|
||||
/// text up to (but not including) the first invalid sequence were represented as UTF-16. To get the
|
||||
/// total UTF-16 code unit count, add <paramref name="surrogatePairCount" /> to
|
||||
/// <paramref name="scalarCount" />.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal static int GetIndexOfFirstInvalidUtf8Sequence(ReadOnlySpan<byte> input, out int scalarCount, out int surrogatePairCount)
|
||||
{
|
||||
return Utf8.Utf8Util.GetIndexOfFirstInvalidUtf8Sequence(
|
||||
ref MemoryMarshal.GetReference(input),
|
||||
input.Length,
|
||||
out scalarCount,
|
||||
out surrogatePairCount);
|
||||
}
|
||||
|
||||
// This method will consume as many ASCII bytes as it can using fast vectorized processing, returning the number of
|
||||
// consumed (ASCII) bytes. It's possible that the method exits early, perhaps because there is some non-ASCII byte
|
||||
// later in the sequence or because we're running out of input to search. The intent is that the caller *skips over*
|
||||
// the number of bytes returned by this method, then it continues data processing from the next byte.
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static unsafe IntPtr ConsumeAsciiBytesVectorized(ref byte buffer, int length)
|
||||
{
|
||||
// Only allow vectorization if vectors are hardware-accelerated and we have enough
|
||||
// data to allow a vectorized search.
|
||||
|
||||
if (!Vector.IsHardwareAccelerated || length < 3 * Vector<byte>.Count)
|
||||
{
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
|
||||
// JITter will generate VMOVUPD instructions, which performs better when the memory to read
|
||||
// is aligned. The GC may move the buffer around in memory, and while it will never cause
|
||||
// moved data to be misaligned with respect to the natural word size, no such guarantee is
|
||||
// made with respect to SIMD vector alignment. We'll pin the buffer so that we can enforce
|
||||
// alignment manually.
|
||||
|
||||
fixed (byte* pbBuffer = &buffer)
|
||||
{
|
||||
// [Potentially unaligned] single SIMD read and comparison, quick check for non-ASCII data.
|
||||
|
||||
Vector<byte> mask = new Vector<byte>((byte)0x80);
|
||||
if ((Unsafe.ReadUnaligned<Vector<byte>>(pbBuffer) & mask) != Vector<byte>.Zero)
|
||||
{
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
|
||||
// Round 'pbBuffer' up to the *next* aligned address. If 'pbBuffer' was already aligned, this
|
||||
// just bumps the address up to the next vector. The read above guaranteed that we read all
|
||||
// data between 'pbBuffer' and 'pbAlignedBuffer' and checked it for non-ASCII bytes. It's
|
||||
// possible we'll duplicate a little bit of work if 'pbBuffer' wasn't already aligned since
|
||||
// its tail end may overlap with the immediate upcoming aligned read, but it's faster just to
|
||||
// perform the extra work and not worry about checking for this condition.
|
||||
|
||||
// 'pbAlignedBuffer' will be somewhere between 1 and Vector<byte>.Count bytes ahead of 'pbBuffer',
|
||||
// hence the check for a length of >= 3 * Vector<byte>.Count at the beginning of this method.
|
||||
|
||||
byte* pbAlignedBuffer;
|
||||
if (IntPtr.Size >= 8)
|
||||
{
|
||||
pbAlignedBuffer = (byte*)(((long)pbBuffer + Vector<byte>.Count) & ~((long)Vector<byte>.Count - 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
pbAlignedBuffer = (byte*)(((int)pbBuffer + Vector<byte>.Count) & ~((int)Vector<byte>.Count - 1));
|
||||
}
|
||||
|
||||
// Now iterate and read two aligned SIMD vectors at a time. We can skip the first length check on the
|
||||
// first iteration of the loop since we already performed a length check at the very beginning of this
|
||||
// method.
|
||||
|
||||
byte* pbFinalPosAtWhichCanReadTwoVectors = &pbBuffer[length - (2 * Vector<byte>.Count)];
|
||||
Contract.Assert(pbAlignedBuffer <= pbFinalPosAtWhichCanReadTwoVectors);
|
||||
|
||||
do
|
||||
{
|
||||
if (((Unsafe.Read<Vector<byte>>(pbAlignedBuffer) | Unsafe.Read<Vector<byte>>(pbAlignedBuffer + Vector<byte>.Count)) & mask) !=
|
||||
Vector<byte>.Zero)
|
||||
{
|
||||
break; // non-ASCII data incoming
|
||||
}
|
||||
}
|
||||
while ((pbAlignedBuffer += 2 * Vector<byte>.Count) <= pbFinalPosAtWhichCanReadTwoVectors);
|
||||
|
||||
// We consumed all data up to 'pbAlignedBuffer' and know it to be non-ASCII.
|
||||
return (IntPtr)(pbAlignedBuffer - pbBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static int GetIndexOfFirstInvalidUtf8Sequence(ref byte inputBuffer, int inputLength, out int scalarCount, out int surrogatePairCount)
|
||||
{
|
||||
// The fields below control where we read from the buffer.
|
||||
|
||||
IntPtr inputBufferCurrentOffset = IntPtr.Zero;
|
||||
int tempScalarCount = inputLength;
|
||||
int tempSurrogatePairCount = 0;
|
||||
|
||||
// If the sequence is long enough, try running vectorized "is this sequence ASCII?"
|
||||
// logic. We perform a small test of the first few bytes to make sure they're all
|
||||
// ASCII before we incur the cost of invoking the vectorized code path.
|
||||
|
||||
if (Vector.IsHardwareAccelerated)
|
||||
{
|
||||
if (IntPtr.Size >= 8)
|
||||
{
|
||||
// Test first 16 bytes and check for all-ASCII.
|
||||
if ((inputLength >= (2 * sizeof(ulong)) + (3 * Vector<byte>.Count)) &&
|
||||
Utf8.Utf8Util.QWordAllBytesAreAscii(Utf8.Utf8Util.ReadAndFoldTwoQWordsUnaligned(ref inputBuffer)))
|
||||
{
|
||||
inputBufferCurrentOffset = Utf8.Utf8Util.ConsumeAsciiBytesVectorized(
|
||||
ref Unsafe.Add(ref inputBuffer, 2 * sizeof(ulong)),
|
||||
inputLength - (2 * sizeof(ulong))) +
|
||||
(2 * sizeof(ulong));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Test first 8 bytes and check for all-ASCII.
|
||||
if ((inputLength >= (2 * sizeof(uint)) + (3 * Vector<byte>.Count)) &&
|
||||
Utf8.Utf8Util.DWordAllBytesAreAscii(Utf8.Utf8Util.ReadAndFoldTwoDWordsUnaligned(ref inputBuffer)))
|
||||
{
|
||||
inputBufferCurrentOffset = Utf8.Utf8Util.ConsumeAsciiBytesVectorized(
|
||||
ref Unsafe.Add(ref inputBuffer, 2 * sizeof(uint)),
|
||||
inputLength - (2 * sizeof(uint))) +
|
||||
(2 * sizeof(uint));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int inputBufferRemainingBytes = inputLength - Utf8.Utf8Util.ConvertIntPtrToInt32WithoutOverflowCheck(inputBufferCurrentOffset);
|
||||
|
||||
// Begin the main loop.
|
||||
|
||||
#if DEBUG
|
||||
long lastOffsetProcessed = -1; // used for invariant checking in debug builds
|
||||
#endif
|
||||
|
||||
while (inputBufferRemainingBytes >= sizeof(uint))
|
||||
{
|
||||
// Read 32 bits at a time. This is enough to hold any possible UTF8-encoded scalar.
|
||||
|
||||
Contract.Assert(inputLength - (int)inputBufferCurrentOffset >= sizeof(uint));
|
||||
uint thisDWord = Unsafe.ReadUnaligned<uint>(ref Unsafe.Add(ref inputBuffer, inputBufferCurrentOffset));
|
||||
|
||||
AfterReadDWord:
|
||||
|
||||
#if DEBUG
|
||||
Contract.Assert(lastOffsetProcessed < (long)inputBufferCurrentOffset, "Algorithm should've made forward progress since last read.");
|
||||
lastOffsetProcessed = (long)inputBufferCurrentOffset;
|
||||
#endif
|
||||
|
||||
// First, check for the common case of all-ASCII bytes.
|
||||
|
||||
if (Utf8.Utf8Util.DWordAllBytesAreAscii(thisDWord))
|
||||
{
|
||||
// We read an all-ASCII sequence.
|
||||
|
||||
inputBufferCurrentOffset += 4;
|
||||
inputBufferRemainingBytes -= 4;
|
||||
|
||||
// If we saw a sequence of all ASCII, there's a good chance a significant amount of following data is also ASCII.
|
||||
// Below is basically unrolled loops with poor man's vectorization.
|
||||
|
||||
if (inputBufferRemainingBytes >= 5 * sizeof(uint))
|
||||
{
|
||||
// The JIT produces better codegen for aligned reads than it does for
|
||||
// unaligned reads, and we want the processor to operate at maximum
|
||||
// efficiency in the loop that follows, so we'll align the references
|
||||
// now. It's OK to do this without pinning because the GC will never
|
||||
// move a heap-allocated object in a manner that messes with its
|
||||
// alignment.
|
||||
{
|
||||
ref byte refToCurrentDWord = ref Unsafe.Add(ref inputBuffer, inputBufferCurrentOffset);
|
||||
thisDWord = Unsafe.ReadUnaligned<uint>(ref refToCurrentDWord);
|
||||
if (!Utf8.Utf8Util.DWordAllBytesAreAscii(thisDWord))
|
||||
{
|
||||
goto AfterReadDWordSkipAllBytesAsciiCheck;
|
||||
}
|
||||
|
||||
int adjustment = Utf8.Utf8Util.GetNumberOfBytesToNextDWordAlignment(ref refToCurrentDWord);
|
||||
inputBufferCurrentOffset += adjustment;
|
||||
|
||||
// will adjust 'bytes remaining' value after below loop
|
||||
}
|
||||
|
||||
// At this point, the input buffer offset points to an aligned DWORD.
|
||||
// We also know that there's enough room to read at least four DWORD's from the stream.
|
||||
|
||||
IntPtr inputBufferFinalOffsetAtWhichCanSafelyLoop = (IntPtr)(inputLength - (4 * sizeof(uint)));
|
||||
do
|
||||
{
|
||||
ref uint currentReadPosition = ref Unsafe.As<byte, uint>(ref Unsafe.Add(ref inputBuffer, inputBufferCurrentOffset));
|
||||
|
||||
if (!Utf8.Utf8Util.DWordAllBytesAreAscii(currentReadPosition | Unsafe.Add(ref currentReadPosition, 1)))
|
||||
{
|
||||
goto LoopTerminatedEarlyDueToNonAsciiData;
|
||||
}
|
||||
|
||||
if (!Utf8.Utf8Util.DWordAllBytesAreAscii(Unsafe.Add(ref currentReadPosition, 2) | Unsafe.Add(ref currentReadPosition, 3)))
|
||||
{
|
||||
inputBufferCurrentOffset += 2 * sizeof(uint);
|
||||
goto LoopTerminatedEarlyDueToNonAsciiData;
|
||||
}
|
||||
|
||||
inputBufferCurrentOffset += 4 * sizeof(uint);
|
||||
}
|
||||
while (Utf8.Utf8Util.IntPtrIsLessThanOrEqualTo(inputBufferCurrentOffset, inputBufferFinalOffsetAtWhichCanSafelyLoop));
|
||||
|
||||
inputBufferRemainingBytes = inputLength - Utf8.Utf8Util.ConvertIntPtrToInt32WithoutOverflowCheck(inputBufferCurrentOffset);
|
||||
continue; // need to perform a bounds check because we might be running out of data
|
||||
|
||||
LoopTerminatedEarlyDueToNonAsciiData:
|
||||
|
||||
// We know that there's *at least* two DWORD's of data remaining in the buffer.
|
||||
// We also know that one of them (or both of them) contains non-ASCII data somewhere.
|
||||
// Let's perform a quick check here to bypass the logic at the beginning of the main loop.
|
||||
|
||||
thisDWord = Unsafe.As<byte, uint>(ref Unsafe.Add(ref inputBuffer, inputBufferCurrentOffset));
|
||||
if (Utf8.Utf8Util.DWordAllBytesAreAscii(thisDWord))
|
||||
{
|
||||
inputBufferCurrentOffset += 4;
|
||||
thisDWord = Unsafe.As<byte, uint>(ref Unsafe.Add(ref inputBuffer, inputBufferCurrentOffset));
|
||||
}
|
||||
|
||||
inputBufferRemainingBytes = inputLength - Utf8.Utf8Util.ConvertIntPtrToInt32WithoutOverflowCheck(inputBufferCurrentOffset);
|
||||
goto AfterReadDWordSkipAllBytesAsciiCheck;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
AfterReadDWordSkipAllBytesAsciiCheck:
|
||||
|
||||
Contract.Assert(!Utf8.Utf8Util.DWordAllBytesAreAscii(thisDWord)); // this should have been handled earlier
|
||||
|
||||
// Next, try stripping off ASCII bytes one at a time.
|
||||
// We only handle up to three ASCII bytes here since we handled the four ASCII byte case above.
|
||||
{
|
||||
uint numLeadingAsciiBytes = Utf8.Utf8Util.CountNumberOfLeadingAsciiBytesFrom24BitInteger(thisDWord);
|
||||
inputBufferCurrentOffset += (int)numLeadingAsciiBytes;
|
||||
inputBufferRemainingBytes -= (int)numLeadingAsciiBytes;
|
||||
|
||||
if (inputBufferRemainingBytes < sizeof(uint))
|
||||
{
|
||||
goto ProcessRemainingBytesSlow; // Input buffer doesn't contain enough data to read a DWORD
|
||||
}
|
||||
else
|
||||
{
|
||||
// The input buffer at the current offset contains a non-ASCII byte.
|
||||
// Read an entire DWORD and fall through to multi-byte consumption logic.
|
||||
thisDWord = Unsafe.ReadUnaligned<uint>(ref Unsafe.Add(ref inputBuffer, inputBufferCurrentOffset));
|
||||
}
|
||||
}
|
||||
|
||||
// At this point, we know we're working with a multi-byte code unit,
|
||||
// but we haven't yet validated it.
|
||||
|
||||
// The masks and comparands are derived from the Unicode Standard, Table 3-6.
|
||||
// Additionally, we need to check for valid byte sequences per Table 3-7.
|
||||
|
||||
// Check the 2-byte case.
|
||||
|
||||
BeforeProcessTwoByteSequence:
|
||||
|
||||
if (Utf8.Utf8Util.DWordBeginsWithUtf8TwoByteMask(thisDWord))
|
||||
{
|
||||
// Per Table 3-7, valid sequences are:
|
||||
// [ C2..DF ] [ 80..BF ]
|
||||
|
||||
if (Utf8.Utf8Util.DWordBeginsWithOverlongUtf8TwoByteSequence(thisDWord))
|
||||
{
|
||||
goto Error;
|
||||
}
|
||||
|
||||
ProcessTwoByteSequenceSkipOverlongFormCheck:
|
||||
|
||||
// Optimization: If this is a two-byte-per-character language like Cyrillic or Hebrew,
|
||||
// there's a good chance that if we see one two-byte run then there's another two-byte
|
||||
// run immediately after. Let's check that now.
|
||||
|
||||
// On little-endian platforms, we can check for the two-byte UTF8 mask *and* validate that
|
||||
// the value isn't overlong using a single comparison. On big-endian platforms, we'll need
|
||||
// to validate the mask and validate that the sequence isn't overlong as two separate comparisons.
|
||||
|
||||
if ((BitConverter.IsLittleEndian && Utf8.Utf8Util.DWordEndsWithValidUtf8TwoByteSequenceLittleEndian(thisDWord)) ||
|
||||
(!BitConverter.IsLittleEndian &&
|
||||
(Utf8.Utf8Util.DWordEndsWithUtf8TwoByteMask(thisDWord) && !Utf8.Utf8Util.DWordEndsWithOverlongUtf8TwoByteSequence(thisDWord))))
|
||||
{
|
||||
ConsumeTwoAdjacentKnownGoodTwoByteSequences:
|
||||
|
||||
// We have two runs of two bytes each.
|
||||
inputBufferCurrentOffset += 4;
|
||||
inputBufferRemainingBytes -= 4;
|
||||
tempScalarCount -= 2; // 4 bytes -> 2 scalars
|
||||
|
||||
if (inputBufferRemainingBytes >= sizeof(uint))
|
||||
{
|
||||
// Optimization: If we read a long run of two-byte sequences, the next sequence is probably
|
||||
// also two bytes. Check for that first before going back to the beginning of the loop.
|
||||
|
||||
thisDWord = Unsafe.ReadUnaligned<uint>(ref Unsafe.Add(ref inputBuffer, inputBufferCurrentOffset));
|
||||
|
||||
if (BitConverter.IsLittleEndian)
|
||||
{
|
||||
if (Utf8.Utf8Util.DWordBeginsWithValidUtf8TwoByteSequenceLittleEndian(thisDWord))
|
||||
{
|
||||
// The next sequence is a valid two-byte sequence.
|
||||
goto ProcessTwoByteSequenceSkipOverlongFormCheck;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Utf8.Utf8Util.DWordBeginsAndEndsWithUtf8TwoByteMask(thisDWord))
|
||||
{
|
||||
if (Utf8.Utf8Util.DWordBeginsWithOverlongUtf8TwoByteSequence(thisDWord) ||
|
||||
Utf8.Utf8Util.DWordEndsWithOverlongUtf8TwoByteSequence(thisDWord))
|
||||
{
|
||||
// Mask said it was 2x 2-byte sequences but validation failed, go to beginning of loop for error handling
|
||||
goto AfterReadDWord;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Validated next bytes are 2x 2-byte sequences
|
||||
goto ConsumeTwoAdjacentKnownGoodTwoByteSequences;
|
||||
}
|
||||
}
|
||||
else if (Utf8.Utf8Util.DWordBeginsWithUtf8TwoByteMask(thisDWord))
|
||||
{
|
||||
if (Utf8.Utf8Util.DWordBeginsWithOverlongUtf8TwoByteSequence(thisDWord))
|
||||
{
|
||||
// Mask said it was a 2-byte sequence but validation failed
|
||||
goto Error;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Validated next bytes are a single 2-byte sequence with no valid 2-byte sequence following
|
||||
goto ConsumeSingleKnownGoodTwoByteSequence;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we reached this point, the next sequence is something other than a valid
|
||||
// two-byte sequence, so go back to the beginning of the loop.
|
||||
goto AfterReadDWord;
|
||||
}
|
||||
else
|
||||
{
|
||||
goto ProcessRemainingBytesSlow; // Running out of data - go down slow path
|
||||
}
|
||||
}
|
||||
|
||||
ConsumeSingleKnownGoodTwoByteSequence:
|
||||
|
||||
// The buffer contains a 2-byte sequence followed by 2 bytes that aren't a 2-byte sequence.
|
||||
// Unlikely that a 3-byte sequence would follow a 2-byte sequence, so perhaps remaining
|
||||
// bytes are ASCII?
|
||||
|
||||
if (Utf8.Utf8Util.DWordThirdByteIsAscii(thisDWord))
|
||||
{
|
||||
if (Utf8.Utf8Util.DWordFourthByteIsAscii(thisDWord))
|
||||
{
|
||||
inputBufferCurrentOffset += 4; // a 2-byte sequence + 2 ASCII bytes
|
||||
inputBufferRemainingBytes -= 4; // a 2-byte sequence + 2 ASCII bytes
|
||||
tempScalarCount--; // 2-byte sequence + 2 ASCII bytes -> 3 scalars
|
||||
}
|
||||
else
|
||||
{
|
||||
inputBufferCurrentOffset += 3; // a 2-byte sequence + 1 ASCII byte
|
||||
inputBufferRemainingBytes -= 3; // a 2-byte sequence + 1 ASCII byte
|
||||
tempScalarCount--; // 2-byte sequence + 1 ASCII bytes -> 2 scalars
|
||||
|
||||
// A two-byte sequence followed by an ASCII byte followed by a non-ASCII byte.
|
||||
// Read in the next DWORD and jump directly to the start of the multi-byte processing block.
|
||||
|
||||
if (inputBufferRemainingBytes >= sizeof(uint))
|
||||
{
|
||||
thisDWord = Unsafe.ReadUnaligned<uint>(ref Unsafe.Add(ref inputBuffer, inputBufferCurrentOffset));
|
||||
goto BeforeProcessTwoByteSequence;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
inputBufferCurrentOffset += 2;
|
||||
inputBufferRemainingBytes -= 2;
|
||||
tempScalarCount--; // 2-byte sequence -> 1 scalar
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check the 3-byte case.
|
||||
|
||||
if (Utf8.Utf8Util.DWordBeginsWithUtf8ThreeByteMask(thisDWord))
|
||||
{
|
||||
ProcessThreeByteSequenceWithCheck:
|
||||
|
||||
// We need to check for overlong or surrogate three-byte sequences.
|
||||
//
|
||||
// Per Table 3-7, valid sequences are:
|
||||
// [ E0 ] [ A0..BF ] [ 80..BF ]
|
||||
// [ E1..EC ] [ 80..BF ] [ 80..BF ]
|
||||
// [ ED ] [ 80..9F ] [ 80..BF ]
|
||||
// [ EE..EF ] [ 80..BF ] [ 80..BF ]
|
||||
//
|
||||
// Big-endian examples of using the above validation table:
|
||||
// E0A0 = 1110 0000 1010 0000 => invalid (overlong ) patterns are 1110 0000 100# ####
|
||||
// ED9F = 1110 1101 1001 1111 => invalid (surrogate) patterns are 1110 1101 101# ####
|
||||
// If using the bitmask ......................................... 0000 1111 0010 0000 (=0F20),
|
||||
// Then invalid (overlong) patterns match the comparand ......... 0000 0000 0000 0000 (=0000),
|
||||
// And invalid (surrogate) patterns match the comparand ......... 0000 1101 0010 0000 (=0D20).
|
||||
|
||||
if (BitConverter.IsLittleEndian)
|
||||
{
|
||||
// The "overlong or surrogate" check can be implemented using a single jump, but there's
|
||||
// some overhead to moving the bits into the correct locations in order to perform the
|
||||
// correct comparison, and in practice the processor's branch prediction capability is
|
||||
// good enough that we shouldn't bother. So we'll use two jumps instead.
|
||||
|
||||
// Can't extract this check into its own helper method because JIT produces suboptimal
|
||||
// assembly, even with aggressive inlining.
|
||||
|
||||
uint comparand = thisDWord & 0x0000200FU;
|
||||
if ((comparand == 0U) || (comparand == 0x0000200DU))
|
||||
{
|
||||
goto Error;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
uint comparand = thisDWord & 0x0F200000U;
|
||||
if ((comparand == 0U) || (comparand == 0x0D200000U))
|
||||
{
|
||||
goto Error;
|
||||
}
|
||||
}
|
||||
|
||||
ProcessSingleThreeByteSequenceSkipOverlongAndSurrogateChecks:
|
||||
|
||||
inputBufferCurrentOffset += 3;
|
||||
inputBufferRemainingBytes -= 3;
|
||||
tempScalarCount -= 2; // 3 bytes -> 1 scalar
|
||||
|
||||
// Occasionally one-off ASCII characters like spaces, periods, or newlines will make their way
|
||||
// in to the text. If this happens strip it off now before seeing if the next character
|
||||
// consists of three code units.
|
||||
|
||||
if (Utf8.Utf8Util.DWordFourthByteIsAscii(thisDWord))
|
||||
{
|
||||
inputBufferCurrentOffset += 1;
|
||||
inputBufferRemainingBytes--;
|
||||
}
|
||||
|
||||
SuccessfullyProcessedThreeByteSequence:
|
||||
|
||||
// Optimization: A three-byte character could indicate CJK text, which makes it likely
|
||||
// that the character following this one is also CJK. We'll try to process several
|
||||
// three-byte sequences at a time.
|
||||
|
||||
if (IntPtr.Size >= 8 && BitConverter.IsLittleEndian && inputBufferRemainingBytes >= (sizeof(ulong) + 1))
|
||||
{
|
||||
ulong thisQWord = Unsafe.ReadUnaligned<ulong>(ref Unsafe.Add(ref inputBuffer, inputBufferCurrentOffset));
|
||||
|
||||
// Is this three 3-byte sequences in a row?
|
||||
// thisQWord = [ 10yyyyyy 1110zzzz | 10xxxxxx 10yyyyyy 1110zzzz | 10xxxxxx 10yyyyyy 1110zzzz ] [ 10xxxxxx ]
|
||||
// ---- CHAR 3 ---- --------- CHAR 2 --------- --------- CHAR 1 --------- -CHAR 3-
|
||||
if ((thisQWord & 0xC0F0C0C0F0C0C0F0UL) == 0x80E08080E08080E0UL &&
|
||||
Utf8.Utf8Util.IsUtf8ContinuationByte(Unsafe.Add(ref inputBuffer, inputBufferCurrentOffset + sizeof(ulong))))
|
||||
{
|
||||
// Saw a proper bitmask for three incoming 3-byte sequences, perform the
|
||||
// overlong and surrogate sequence checking now.
|
||||
|
||||
// Check the first character.
|
||||
// If the first character is overlong or a surrogate, fail immediately.
|
||||
|
||||
uint comparand = unchecked((uint)thisQWord) & 0x200FU;
|
||||
if ((comparand == 0UL) || (comparand == 0x200DU))
|
||||
{
|
||||
goto Error;
|
||||
}
|
||||
|
||||
// Check the second character.
|
||||
// If this character is overlong or a surrogate, process the first character (which we
|
||||
// know to be good because the first check passed) before reporting an error.
|
||||
|
||||
comparand = unchecked((uint)(thisQWord >> 24)) & 0x200FU;
|
||||
if ((comparand == 0U) || (comparand == 0x200DU))
|
||||
{
|
||||
thisDWord = unchecked((uint)thisQWord);
|
||||
goto ProcessSingleThreeByteSequenceSkipOverlongAndSurrogateChecks;
|
||||
}
|
||||
|
||||
// Check the third character (we already checked that it's followed by a continuation byte).
|
||||
// If this character is overlong or a surrogate, process the first character (which we
|
||||
// know to be good because the first check passed) before reporting an error.
|
||||
|
||||
comparand = unchecked((uint)(thisQWord >> 48)) & 0x200FU;
|
||||
if ((comparand == 0U) || (comparand == 0x200DU))
|
||||
{
|
||||
thisDWord = unchecked((uint)thisQWord);
|
||||
goto ProcessSingleThreeByteSequenceSkipOverlongAndSurrogateChecks;
|
||||
}
|
||||
|
||||
inputBufferCurrentOffset += 9;
|
||||
inputBufferRemainingBytes -= 9;
|
||||
tempScalarCount -= 6; // 9 bytes -> 3 scalars
|
||||
goto SuccessfullyProcessedThreeByteSequence;
|
||||
}
|
||||
|
||||
// Is this two 3-byte sequences in a row?
|
||||
// thisQWord = [ ######## ######## | 10xxxxxx 10yyyyyy 1110zzzz | 10xxxxxx 10yyyyyy 1110zzzz ]
|
||||
// --------- CHAR 2 --------- --------- CHAR 1 ---------
|
||||
if ((thisQWord & 0xC0C0F0C0C0F0UL) == 0x8080E08080E0UL)
|
||||
{
|
||||
// Saw a proper bitmask for two incoming 3-byte sequences, perform the
|
||||
// overlong and surrogate sequence checking now.
|
||||
|
||||
// Check the first character.
|
||||
// If the first character is overlong or a surrogate, fail immediately.
|
||||
|
||||
uint comparand = unchecked((uint)thisQWord) & 0x200FU;
|
||||
if ((comparand == 0UL) || (comparand == 0x200DU))
|
||||
{
|
||||
goto Error;
|
||||
}
|
||||
|
||||
// Check the second character.
|
||||
// If this character is overlong or a surrogate, process the first character (which we
|
||||
// know to be good because the first check passed) before reporting an error.
|
||||
|
||||
comparand = unchecked((uint)(thisQWord >> 24)) & 0x200FU;
|
||||
if ((comparand == 0U) || (comparand == 0x200DU))
|
||||
{
|
||||
thisDWord = unchecked((uint)thisQWord);
|
||||
goto ProcessSingleThreeByteSequenceSkipOverlongAndSurrogateChecks;
|
||||
}
|
||||
|
||||
inputBufferCurrentOffset += 6;
|
||||
inputBufferRemainingBytes -= 6;
|
||||
tempScalarCount -= 4; // 6 bytes -> 2 scalars
|
||||
|
||||
// The next char in the sequence didn't have a 3-byte marker, so it's probably
|
||||
// an ASCII character. Jump back to the beginning of loop processing.
|
||||
continue;
|
||||
}
|
||||
|
||||
thisDWord = unchecked((uint)thisQWord);
|
||||
if (Utf8.Utf8Util.DWordBeginsWithUtf8ThreeByteMask(thisDWord))
|
||||
{
|
||||
// A single three-byte sequence.
|
||||
goto ProcessThreeByteSequenceWithCheck;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not a three-byte sequence; perhaps ASCII?
|
||||
goto AfterReadDWord;
|
||||
}
|
||||
}
|
||||
|
||||
if (inputBufferRemainingBytes >= sizeof(uint))
|
||||
{
|
||||
thisDWord = Unsafe.ReadUnaligned<uint>(ref Unsafe.Add(ref inputBuffer, inputBufferCurrentOffset));
|
||||
|
||||
// Optimization: A three-byte character could indicate CJK text, which makes it likely
|
||||
// that the character following this one is also CJK. We'll check for a three-byte sequence
|
||||
// marker now and jump directly to three-byte sequence processing if we see one, skipping
|
||||
// all of the logic at the beginning of the loop.
|
||||
|
||||
if (Utf8.Utf8Util.DWordBeginsWithUtf8ThreeByteMask(thisDWord))
|
||||
{
|
||||
goto ProcessThreeByteSequenceWithCheck; // Found another [not yet validated] three-byte sequence; process
|
||||
}
|
||||
else
|
||||
{
|
||||
goto AfterReadDWord; // Probably ASCII punctuation or whitespace; go back to start of loop
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
goto ProcessRemainingBytesSlow; // Running out of data
|
||||
}
|
||||
}
|
||||
|
||||
// Assume the 4-byte case, but we need to validate.
|
||||
|
||||
{
|
||||
// We need to check for overlong or invalid (over U+10FFFF) four-byte sequences.
|
||||
//
|
||||
// Per Table 3-7, valid sequences are:
|
||||
// [ F0 ] [ 90..BF ] [ 80..BF ] [ 80..BF ]
|
||||
// [ F1..F3 ] [ 80..BF ] [ 80..BF ] [ 80..BF ]
|
||||
// [ F4 ] [ 80..8F ] [ 80..BF ] [ 80..BF ]
|
||||
|
||||
if (!Utf8.Utf8Util.DWordBeginsWithUtf8FourByteMask(thisDWord))
|
||||
{
|
||||
goto Error;
|
||||
}
|
||||
|
||||
// Now check for overlong / out-of-range sequences.
|
||||
|
||||
if (BitConverter.IsLittleEndian)
|
||||
{
|
||||
// The DWORD we read is [ 10xxxxxx 10yyyyyy 10zzzzzz 11110www ].
|
||||
// We want to get the 'w' byte in front of the 'z' byte so that we can perform
|
||||
// a single range comparison. We'll take advantage of the fact that the JITter
|
||||
// can detect a ROR / ROL operation, then we'll just zero out the bytes that
|
||||
// aren't involved in the range check.
|
||||
|
||||
uint toCheck = unchecked((ushort)thisDWord);
|
||||
|
||||
// At this point, toCheck = [ 00000000 00000000 10zzzzzz 11110www ].
|
||||
|
||||
toCheck = (toCheck << 24) | (toCheck >> 8); // ROR 8 / ROL 24
|
||||
|
||||
// At this point, toCheck = [ 11110www 00000000 00000000 10zzzzzz ].
|
||||
|
||||
if (!Utf8.Utf8Util.IsInRangeInclusive(toCheck, 0xF0000090U, 0xF400008FU))
|
||||
{
|
||||
goto Error;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!Utf8.Utf8Util.IsInRangeInclusive(thisDWord, 0xF0900000U, 0xF48FFFFFU))
|
||||
{
|
||||
goto Error;
|
||||
}
|
||||
}
|
||||
|
||||
// Validation complete.
|
||||
|
||||
inputBufferCurrentOffset += 4;
|
||||
inputBufferRemainingBytes -= 4;
|
||||
tempScalarCount -= 3; // 4 bytes -> 1 scalar
|
||||
tempSurrogatePairCount++; // 4 bytes implies UTF16 surrogate pair
|
||||
|
||||
continue; // go back to beginning of loop for processing
|
||||
}
|
||||
}
|
||||
|
||||
ProcessRemainingBytesSlow:
|
||||
|
||||
Contract.Assert(inputBufferRemainingBytes < 4);
|
||||
while (inputBufferRemainingBytes > 0)
|
||||
{
|
||||
uint firstByte = Unsafe.Add(ref inputBuffer, inputBufferCurrentOffset);
|
||||
|
||||
if (firstByte < 0x80U)
|
||||
{
|
||||
// 1-byte (ASCII) case
|
||||
inputBufferCurrentOffset += 1;
|
||||
inputBufferRemainingBytes -= 1;
|
||||
continue;
|
||||
}
|
||||
else if (inputBufferRemainingBytes >= 2)
|
||||
{
|
||||
uint secondByte = Unsafe.Add(ref inputBuffer, inputBufferCurrentOffset + 1);
|
||||
if (firstByte < 0xE0U)
|
||||
{
|
||||
// 2-byte case
|
||||
if (firstByte >= 0xC2U && Utf8.Utf8Util.IsUtf8ContinuationByte(secondByte))
|
||||
{
|
||||
inputBufferCurrentOffset += 2;
|
||||
inputBufferRemainingBytes -= 2;
|
||||
tempScalarCount--; // 2 bytes -> 1 scalar
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (inputBufferRemainingBytes >= 3)
|
||||
{
|
||||
uint thirdByte = Unsafe.Add(ref inputBuffer, inputBufferCurrentOffset + 2);
|
||||
if (firstByte < 0xF0U)
|
||||
{
|
||||
if (firstByte == 0xE0U)
|
||||
{
|
||||
if (!Utf8.Utf8Util.IsInRangeInclusive(secondByte, 0xA0U, 0xBFU))
|
||||
{
|
||||
goto Error;
|
||||
}
|
||||
}
|
||||
else if (firstByte == 0xEDU)
|
||||
{
|
||||
if (!Utf8.Utf8Util.IsInRangeInclusive(secondByte, 0x80U, 0x9FU))
|
||||
{
|
||||
goto Error;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!Utf8.Utf8Util.IsUtf8ContinuationByte(secondByte))
|
||||
{
|
||||
goto Error;
|
||||
}
|
||||
}
|
||||
|
||||
if (Utf8.Utf8Util.IsUtf8ContinuationByte(thirdByte))
|
||||
{
|
||||
inputBufferCurrentOffset += 3;
|
||||
inputBufferRemainingBytes -= 3;
|
||||
tempScalarCount -= 2; // 3 bytes -> 1 scalar
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Error - no match.
|
||||
|
||||
goto Error;
|
||||
}
|
||||
|
||||
// If we reached this point, we're out of data, and we saw no bad UTF8 sequence.
|
||||
|
||||
scalarCount = tempScalarCount;
|
||||
surrogatePairCount = tempSurrogatePairCount;
|
||||
return -1;
|
||||
|
||||
// Error handling logic.
|
||||
|
||||
Error:
|
||||
|
||||
scalarCount = tempScalarCount -
|
||||
inputBufferRemainingBytes; // we assumed earlier each byte corresponded to a single scalar, perform fixup now to account for unread bytes
|
||||
|
||||
surrogatePairCount = tempSurrogatePairCount;
|
||||
return Utf8.Utf8Util.ConvertIntPtrToInt32WithoutOverflowCheck(inputBufferCurrentOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
480
src/Core/Core/Utf8/UtfAnyString.cs
Normal file
480
src/Core/Core/Utf8/UtfAnyString.cs
Normal file
@@ -0,0 +1,480 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma warning disable CA2225 // Operator overloads have named alternates
|
||||
|
||||
// ReSharper disable once UseNameofExpression
|
||||
namespace Microsoft.Azure.Cosmos.Core.Utf8
|
||||
{
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
/// <summary>A string whose memory representation may be either UTF8 or UTF16.</summary>
|
||||
/// <remarks>
|
||||
/// This type supports polymorphic use of <see cref="string" /> and <see cref="Utf8String" />
|
||||
/// when equality, hashing, and comparison are needed against either encoding. An API leveraging
|
||||
/// <see cref="UtfAnyString" /> can avoid separate method overloads while still accepting either
|
||||
/// encoding without imposing additional allocations.
|
||||
/// </remarks>
|
||||
[DebuggerDisplay("{ToString()}")]
|
||||
public readonly struct UtfAnyString :
|
||||
IEquatable<UtfAnyString>, IComparable<UtfAnyString>,
|
||||
IEquatable<Utf8String>, IComparable<Utf8String>,
|
||||
IEquatable<string>, IComparable<string>
|
||||
{
|
||||
public static UtfAnyString Empty => string.Empty;
|
||||
|
||||
private readonly object buffer;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public UtfAnyString(string utf16String)
|
||||
{
|
||||
this.buffer = utf16String;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public UtfAnyString(Utf8String utf8String)
|
||||
{
|
||||
this.buffer = utf8String;
|
||||
}
|
||||
|
||||
public bool IsUtf8 => this.buffer is Utf8String;
|
||||
|
||||
public bool IsUtf16 => this.buffer is string;
|
||||
|
||||
/// <summary>True if the length is empty.</summary>
|
||||
public bool IsNull => object.ReferenceEquals(null, this.buffer);
|
||||
|
||||
/// <summary>True if the length is empty.</summary>
|
||||
public bool IsEmpty
|
||||
{
|
||||
get
|
||||
{
|
||||
if (object.ReferenceEquals(null, this.buffer))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (this.buffer)
|
||||
{
|
||||
case string s:
|
||||
return s.Length == 0;
|
||||
default:
|
||||
return ((Utf8String)this.buffer).IsEmpty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static implicit operator UtfAnyString(string utf16String)
|
||||
{
|
||||
return new UtfAnyString(utf16String);
|
||||
}
|
||||
|
||||
public static implicit operator string(UtfAnyString str)
|
||||
{
|
||||
return str.buffer?.ToString();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
// ReSharper disable once AssignNullToNotNullAttribute
|
||||
return this.buffer?.ToString();
|
||||
}
|
||||
|
||||
public Utf8String ToUtf8String()
|
||||
{
|
||||
if (object.ReferenceEquals(null, this.buffer))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (this.buffer)
|
||||
{
|
||||
case string s:
|
||||
return Utf8String.TranscodeUtf16(s);
|
||||
default:
|
||||
return (Utf8String)this.buffer;
|
||||
}
|
||||
}
|
||||
|
||||
public bool ReferenceEquals(UtfAnyString other)
|
||||
{
|
||||
return this.buffer == other.buffer;
|
||||
}
|
||||
|
||||
public bool Equals(UtfAnyString other)
|
||||
{
|
||||
if (object.ReferenceEquals(null, this.buffer))
|
||||
{
|
||||
return object.ReferenceEquals(null, other.buffer);
|
||||
}
|
||||
|
||||
switch (this.buffer)
|
||||
{
|
||||
case string s:
|
||||
return other.Equals(s);
|
||||
default:
|
||||
return other.Equals((Utf8String)this.buffer);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Equals(Utf8Span other)
|
||||
{
|
||||
return other.Equals(this.buffer);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
switch (obj)
|
||||
{
|
||||
case string s:
|
||||
return this.Equals(s);
|
||||
case Utf8String s:
|
||||
return this.Equals(s);
|
||||
case UtfAnyString s:
|
||||
return this.Equals(s);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool Equals(Utf8String other)
|
||||
{
|
||||
if (object.ReferenceEquals(null, other))
|
||||
{
|
||||
return object.ReferenceEquals(null, this.buffer);
|
||||
}
|
||||
|
||||
return other.Equals(this.buffer);
|
||||
}
|
||||
|
||||
public bool Equals(string other)
|
||||
{
|
||||
if (object.ReferenceEquals(null, this.buffer))
|
||||
{
|
||||
return object.ReferenceEquals(null, other);
|
||||
}
|
||||
|
||||
switch (this.buffer)
|
||||
{
|
||||
case string s:
|
||||
return string.Equals(s, other, StringComparison.Ordinal);
|
||||
default:
|
||||
return ((Utf8String)this.buffer).Equals(other);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool operator ==(UtfAnyString left, UtfAnyString right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator !=(UtfAnyString left, UtfAnyString right)
|
||||
{
|
||||
return !left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator ==(UtfAnyString left, string right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator !=(UtfAnyString left, string right)
|
||||
{
|
||||
return !left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator ==(string left, UtfAnyString right)
|
||||
{
|
||||
return right.Equals(left);
|
||||
}
|
||||
|
||||
public static bool operator !=(string left, UtfAnyString right)
|
||||
{
|
||||
return !right.Equals(left);
|
||||
}
|
||||
|
||||
public static bool operator ==(UtfAnyString left, Utf8String right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator !=(UtfAnyString left, Utf8String right)
|
||||
{
|
||||
return !left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator ==(Utf8String left, UtfAnyString right)
|
||||
{
|
||||
return right.Equals(left);
|
||||
}
|
||||
|
||||
public static bool operator !=(Utf8String left, UtfAnyString right)
|
||||
{
|
||||
return !right.Equals(left);
|
||||
}
|
||||
|
||||
public static bool operator ==(UtfAnyString left, Utf8Span right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator !=(UtfAnyString left, Utf8Span right)
|
||||
{
|
||||
return !left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator ==(Utf8Span left, UtfAnyString right)
|
||||
{
|
||||
return right.Equals(left);
|
||||
}
|
||||
|
||||
public static bool operator !=(Utf8Span left, UtfAnyString right)
|
||||
{
|
||||
return !right.Equals(left);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
uint hash1 = 5381;
|
||||
uint hash2 = hash1;
|
||||
|
||||
if (object.ReferenceEquals(null, this.buffer))
|
||||
{
|
||||
return unchecked((int)(hash1 + (hash2 * 1566083941)));
|
||||
}
|
||||
|
||||
switch (this.buffer)
|
||||
{
|
||||
case string s:
|
||||
unchecked
|
||||
{
|
||||
Utf16LittleEndianCodePointEnumerator thisEnumerator = new Utf16LittleEndianCodePointEnumerator(s);
|
||||
for (int i = 0; thisEnumerator.MoveNext(); i++)
|
||||
{
|
||||
uint c = thisEnumerator.Current;
|
||||
if (i % 2 == 0)
|
||||
{
|
||||
hash1 = ((hash1 << 5) + hash1) ^ c;
|
||||
}
|
||||
else
|
||||
{
|
||||
hash2 = ((hash2 << 5) + hash2) ^ c;
|
||||
}
|
||||
}
|
||||
|
||||
return (int)(hash1 + (hash2 * 1566083941));
|
||||
}
|
||||
|
||||
default:
|
||||
return this.buffer.GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
public static bool operator <(UtfAnyString left, UtfAnyString right)
|
||||
{
|
||||
return left.CompareTo(right) < 0;
|
||||
}
|
||||
|
||||
public static bool operator <=(UtfAnyString left, UtfAnyString right)
|
||||
{
|
||||
return left.CompareTo(right) <= 0;
|
||||
}
|
||||
|
||||
public static bool operator >(UtfAnyString left, UtfAnyString right)
|
||||
{
|
||||
return left.CompareTo(right) > 0;
|
||||
}
|
||||
|
||||
public static bool operator >=(UtfAnyString left, UtfAnyString right)
|
||||
{
|
||||
return left.CompareTo(right) >= 0;
|
||||
}
|
||||
|
||||
public static bool operator <(UtfAnyString left, string right)
|
||||
{
|
||||
return left.CompareTo(right) < 0;
|
||||
}
|
||||
|
||||
public static bool operator <=(UtfAnyString left, string right)
|
||||
{
|
||||
return left.CompareTo(right) <= 0;
|
||||
}
|
||||
|
||||
public static bool operator >(UtfAnyString left, string right)
|
||||
{
|
||||
return left.CompareTo(right) > 0;
|
||||
}
|
||||
|
||||
public static bool operator >=(UtfAnyString left, string right)
|
||||
{
|
||||
return left.CompareTo(right) >= 0;
|
||||
}
|
||||
|
||||
public static bool operator <(string left, UtfAnyString right)
|
||||
{
|
||||
return right.CompareTo(left) >= 0;
|
||||
}
|
||||
|
||||
public static bool operator <=(string left, UtfAnyString right)
|
||||
{
|
||||
return right.CompareTo(left) > 0;
|
||||
}
|
||||
|
||||
public static bool operator >(string left, UtfAnyString right)
|
||||
{
|
||||
return right.CompareTo(left) <= 0;
|
||||
}
|
||||
|
||||
public static bool operator >=(string left, UtfAnyString right)
|
||||
{
|
||||
return right.CompareTo(left) < 0;
|
||||
}
|
||||
|
||||
public static bool operator <(UtfAnyString left, Utf8String right)
|
||||
{
|
||||
return left.CompareTo(right) < 0;
|
||||
}
|
||||
|
||||
public static bool operator <=(UtfAnyString left, Utf8String right)
|
||||
{
|
||||
return left.CompareTo(right) <= 0;
|
||||
}
|
||||
|
||||
public static bool operator >(UtfAnyString left, Utf8String right)
|
||||
{
|
||||
return left.CompareTo(right) > 0;
|
||||
}
|
||||
|
||||
public static bool operator >=(UtfAnyString left, Utf8String right)
|
||||
{
|
||||
return left.CompareTo(right) >= 0;
|
||||
}
|
||||
|
||||
public static bool operator <(Utf8String left, UtfAnyString right)
|
||||
{
|
||||
return right.CompareTo(left) >= 0;
|
||||
}
|
||||
|
||||
public static bool operator <=(Utf8String left, UtfAnyString right)
|
||||
{
|
||||
return right.CompareTo(left) > 0;
|
||||
}
|
||||
|
||||
public static bool operator >(Utf8String left, UtfAnyString right)
|
||||
{
|
||||
return right.CompareTo(left) <= 0;
|
||||
}
|
||||
|
||||
public static bool operator >=(Utf8String left, UtfAnyString right)
|
||||
{
|
||||
return right.CompareTo(left) < 0;
|
||||
}
|
||||
|
||||
public static bool operator <(UtfAnyString left, Utf8Span right)
|
||||
{
|
||||
return left.CompareTo(right) < 0;
|
||||
}
|
||||
|
||||
public static bool operator <=(UtfAnyString left, Utf8Span right)
|
||||
{
|
||||
return left.CompareTo(right) <= 0;
|
||||
}
|
||||
|
||||
public static bool operator >(UtfAnyString left, Utf8Span right)
|
||||
{
|
||||
return left.CompareTo(right) > 0;
|
||||
}
|
||||
|
||||
public static bool operator >=(UtfAnyString left, Utf8Span right)
|
||||
{
|
||||
return left.CompareTo(right) >= 0;
|
||||
}
|
||||
|
||||
public static bool operator <(Utf8Span left, UtfAnyString right)
|
||||
{
|
||||
return right.CompareTo(left) >= 0;
|
||||
}
|
||||
|
||||
public static bool operator <=(Utf8Span left, UtfAnyString right)
|
||||
{
|
||||
return right.CompareTo(left) > 0;
|
||||
}
|
||||
|
||||
public static bool operator >(Utf8Span left, UtfAnyString right)
|
||||
{
|
||||
return right.CompareTo(left) <= 0;
|
||||
}
|
||||
|
||||
public static bool operator >=(Utf8Span left, UtfAnyString right)
|
||||
{
|
||||
return right.CompareTo(left) < 0;
|
||||
}
|
||||
|
||||
public int CompareTo(UtfAnyString other)
|
||||
{
|
||||
if (object.ReferenceEquals(null, other.buffer))
|
||||
{
|
||||
return object.ReferenceEquals(null, this.buffer) ? 0 : 1;
|
||||
}
|
||||
|
||||
switch (other.buffer)
|
||||
{
|
||||
case string s:
|
||||
return this.CompareTo(s);
|
||||
default:
|
||||
return this.CompareTo((Utf8String)other.buffer);
|
||||
}
|
||||
}
|
||||
|
||||
public int CompareTo(Utf8String other)
|
||||
{
|
||||
if (object.ReferenceEquals(null, this.buffer))
|
||||
{
|
||||
return object.ReferenceEquals(null, other) ? 0 : -1;
|
||||
}
|
||||
|
||||
switch (this.buffer)
|
||||
{
|
||||
case string s:
|
||||
return -other.Span.CompareTo(s);
|
||||
default:
|
||||
return -other.Span.CompareTo((Utf8String)this.buffer);
|
||||
}
|
||||
}
|
||||
|
||||
public int CompareTo(Utf8Span other)
|
||||
{
|
||||
if (object.ReferenceEquals(null, this.buffer))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (this.buffer)
|
||||
{
|
||||
case string s:
|
||||
return -other.CompareTo(s);
|
||||
default:
|
||||
return -other.CompareTo((Utf8String)this.buffer);
|
||||
}
|
||||
}
|
||||
|
||||
public int CompareTo(string other)
|
||||
{
|
||||
if (object.ReferenceEquals(null, this.buffer))
|
||||
{
|
||||
return object.ReferenceEquals(null, other) ? 0 : -1;
|
||||
}
|
||||
|
||||
switch (this.buffer)
|
||||
{
|
||||
case string s:
|
||||
return string.Compare(s, other, StringComparison.Ordinal);
|
||||
default:
|
||||
return ((Utf8String)this.buffer).CompareTo(other);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user