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:
Jason Hunter
2021-05-05 17:18:35 -07:00
committed by GitHub
parent c36b0adfe8
commit ce112dcdf1
643 changed files with 73408 additions and 20737 deletions

457
src/CodeAnalysis.ruleset Normal file
View File

@@ -0,0 +1,457 @@
<?xml version="1.0" encoding="utf-8"?>
<RuleSet Name="Code Analysis Ruleset" Description="" ToolsVersion="15.0">
<Rules AnalyzerId="Microsoft.Analyzers.ManagedCodeAnalysis" RuleNamespace="Microsoft.Rules.Managed">
<Rule Id="CA1001" Action="Error" />
<Rule Id="CA1028" Action="None" />
<Rule Id="CA1031" Action="None" />
<Rule Id="CA1034" Action="None" />
<Rule Id="CA1062" Action="None" />
<Rule Id="CA1063" Action="Error" />
<Rule Id="CA1200" Action="None" />
<Rule Id="CA1303" Action="None" />
<Rule Id="CA1305" Action="None" />
<Rule Id="CA1307" Action="None" />
<Rule Id="CA1308" Action="None" />
<Rule Id="CA1710" Action="None" />
<Rule Id="CA1717" Action="None" />
<Rule Id="CA1720" Action="None" />
<Rule Id="CA1724" Action="None" />
<Rule Id="CA1801" Action="None" />
<Rule Id="CA1806" Action="Error" />
<Rule Id="CA1810" Action="None" />
<Rule Id="CA1815" Action="None" />
<Rule Id="CA1816" Action="Error" />
<Rule Id="CA2000" Action="Error" />
<Rule Id="CA2004" Action="Error" />
<Rule Id="CA2007" Action="None" />
<Rule Id="CA2115" Action="Error" />
<Rule Id="CA2213" Action="Error" />
<Rule Id="CA2214" Action="Error" />
<Rule Id="CA2215" Action="Error" />
<Rule Id="CA2216" Action="Error" />
<Rule Id="CA2220" Action="Error" />
<Rule Id="CA2221" Action="Error" />
<Rule Id="CA2227" Action="None" />
</Rules>
<Rules AnalyzerId="Microsoft.CodeAnalysis.CSharp" RuleNamespace="Microsoft.CodeAnalysis.CSharp">
<Rule Id="AD0001" Action="None" />
<Rule Id="CS0078" Action="None" />
<Rule Id="CS0105" Action="None" />
<Rule Id="CS0108" Action="None" />
<Rule Id="CS0109" Action="None" />
<Rule Id="CS0114" Action="None" />
<Rule Id="CS0162" Action="None" />
<Rule Id="CS0164" Action="None" />
<Rule Id="CS0168" Action="Error" />
<Rule Id="CS0183" Action="None" />
<Rule Id="CS0184" Action="None" />
<Rule Id="CS0197" Action="None" />
<Rule Id="CS0219" Action="Error" />
<Rule Id="CS0251" Action="None" />
<Rule Id="CS0252" Action="None" />
<Rule Id="CS0253" Action="None" />
<Rule Id="CS0278" Action="None" />
<Rule Id="CS0279" Action="None" />
<Rule Id="CS0280" Action="None" />
<Rule Id="CS0282" Action="None" />
<Rule Id="CS0419" Action="None" />
<Rule Id="CS0420" Action="None" />
<Rule Id="CS0435" Action="None" />
<Rule Id="CS0436" Action="None" />
<Rule Id="CS0437" Action="None" />
<Rule Id="CS0440" Action="None" />
<Rule Id="CS0458" Action="None" />
<Rule Id="CS0464" Action="None" />
<Rule Id="CS0465" Action="None" />
<Rule Id="CS0469" Action="None" />
<Rule Id="CS0472" Action="None" />
<Rule Id="CS0473" Action="None" />
<Rule Id="CS0612" Action="None" />
<Rule Id="CS0618" Action="None" />
<Rule Id="CS0626" Action="None" />
<Rule Id="CS0628" Action="None" />
<Rule Id="CS0642" Action="None" />
<Rule Id="CS0652" Action="None" />
<Rule Id="CS0657" Action="None" />
<Rule Id="CS0658" Action="None" />
<Rule Id="CS0659" Action="Error" />
<Rule Id="CS0660" Action="Error" />
<Rule Id="CS0661" Action="Error" />
<Rule Id="CS0665" Action="None" />
<Rule Id="CS0672" Action="None" />
<Rule Id="CS0675" Action="None" />
<Rule Id="CS0684" Action="None" />
<Rule Id="CS0693" Action="None" />
<Rule Id="CS0728" Action="None" />
<Rule Id="CS0809" Action="None" />
<Rule Id="CS0811" Action="None" />
<Rule Id="CS0824" Action="None" />
<Rule Id="CS1028" Action="None" />
<Rule Id="CS1030" Action="None" />
<Rule Id="CS1058" Action="None" />
<Rule Id="CS1062" Action="None" />
<Rule Id="CS1064" Action="None" />
<Rule Id="CS1066" Action="None" />
<Rule Id="CS1072" Action="None" />
<Rule Id="CS1308" Action="None" />
<Rule Id="CS1522" Action="None" />
<Rule Id="CS1570" Action="None" />
<Rule Id="CS1571" Action="None" />
<Rule Id="CS1572" Action="None" />
<Rule Id="CS1573" Action="None" />
<Rule Id="CS1574" Action="None" />
<Rule Id="CS1580" Action="None" />
<Rule Id="CS1581" Action="None" />
<Rule Id="CS1584" Action="None" />
<Rule Id="CS1587" Action="None" />
<Rule Id="CS1589" Action="None" />
<Rule Id="CS1590" Action="None" />
<Rule Id="CS1591" Action="None" />
<Rule Id="CS1592" Action="None" />
<Rule Id="CS1616" Action="None" />
<Rule Id="CS1633" Action="None" />
<Rule Id="CS1634" Action="None" />
<Rule Id="CS1635" Action="None" />
<Rule Id="CS1645" Action="None" />
<Rule Id="CS1658" Action="None" />
<Rule Id="CS1668" Action="None" />
<Rule Id="CS1685" Action="None" />
<Rule Id="CS1687" Action="None" />
<Rule Id="CS1690" Action="None" />
<Rule Id="CS1692" Action="None" />
<Rule Id="CS1695" Action="None" />
<Rule Id="CS1696" Action="None" />
<Rule Id="CS1697" Action="None" />
<Rule Id="CS1700" Action="None" />
<Rule Id="CS1701" Action="None" />
<Rule Id="CS1702" Action="None" />
<Rule Id="CS1710" Action="None" />
<Rule Id="CS1711" Action="None" />
<Rule Id="CS1712" Action="None" />
<Rule Id="CS1717" Action="Error" />
<Rule Id="CS1718" Action="None" />
<Rule Id="CS1720" Action="None" />
<Rule Id="CS1723" Action="None" />
<Rule Id="CS1734" Action="None" />
<Rule Id="CS1735" Action="None" />
<Rule Id="CS1762" Action="None" />
<Rule Id="CS1927" Action="None" />
<Rule Id="CS1956" Action="None" />
<Rule Id="CS1957" Action="None" />
<Rule Id="CS1974" Action="None" />
<Rule Id="CS1981" Action="None" />
<Rule Id="CS1998" Action="Error" />
<Rule Id="CS2002" Action="None" />
<Rule Id="CS2008" Action="None" />
<Rule Id="CS2023" Action="None" />
<Rule Id="CS2029" Action="None" />
<Rule Id="CS2038" Action="None" />
<Rule Id="CS3000" Action="None" />
<Rule Id="CS3001" Action="None" />
<Rule Id="CS3002" Action="None" />
<Rule Id="CS3003" Action="None" />
<Rule Id="CS3005" Action="None" />
<Rule Id="CS3006" Action="None" />
<Rule Id="CS3007" Action="None" />
<Rule Id="CS3008" Action="None" />
<Rule Id="CS3009" Action="None" />
<Rule Id="CS3010" Action="None" />
<Rule Id="CS3011" Action="None" />
<Rule Id="CS3012" Action="None" />
<Rule Id="CS3013" Action="None" />
<Rule Id="CS3014" Action="None" />
<Rule Id="CS3015" Action="None" />
<Rule Id="CS3016" Action="None" />
<Rule Id="CS3017" Action="None" />
<Rule Id="CS3018" Action="None" />
<Rule Id="CS3019" Action="None" />
<Rule Id="CS3021" Action="None" />
<Rule Id="CS3022" Action="None" />
<Rule Id="CS3023" Action="None" />
<Rule Id="CS3024" Action="None" />
<Rule Id="CS3026" Action="None" />
<Rule Id="CS3027" Action="None" />
<Rule Id="CS4014" Action="Error" />
<Rule Id="CS4024" Action="None" />
<Rule Id="CS4025" Action="None" />
<Rule Id="CS4026" Action="None" />
<Rule Id="CS7033" Action="None" />
<Rule Id="CS7035" Action="None" />
<Rule Id="CS7080" Action="None" />
<Rule Id="CS7081" Action="None" />
<Rule Id="CS7082" Action="None" />
<Rule Id="CS7090" Action="None" />
<Rule Id="CS7095" Action="None" />
<Rule Id="CS8001" Action="None" />
<Rule Id="CS8002" Action="None" />
<Rule Id="CS8009" Action="None" />
<Rule Id="CS8012" Action="None" />
<Rule Id="CS8018" Action="None" />
<Rule Id="CS8019" Action="None" />
<Rule Id="CS8020" Action="None" />
<Rule Id="CS8021" Action="None" />
<Rule Id="CS8029" Action="None" />
<Rule Id="CS8032" Action="None" />
<Rule Id="CS8033" Action="None" />
<Rule Id="CS8034" Action="None" />
<Rule Id="CS8040" Action="None" />
<Rule Id="CS8073" Action="None" />
<Rule Id="CS8094" Action="None" />
<Rule Id="CS8105" Action="None" />
<Rule Id="CS8123" Action="None" />
</Rules>
<Rules AnalyzerId="Microsoft.CodeAnalysis.CSharp.EditorFeatures" RuleNamespace="Microsoft.CodeAnalysis.CSharp.EditorFeatures">
<Rule Id="IDE0060" Action="None" />
<Rule Id="IDE0032" Action="None" />
<Rule Id="IDE0032WithoutSuggestion" Action="None" />
</Rules>
<Rules AnalyzerId="Microsoft.CodeAnalysis.CSharp.Features" RuleNamespace="Microsoft.CodeAnalysis.CSharp.Features">
<Rule Id="IDE0001" Action="None" />
<Rule Id="IDE0002" Action="None" />
<Rule Id="IDE0003" Action="None" />
<Rule Id="IDE0004" Action="None" />
<Rule Id="IDE0005" Action="None" />
<Rule Id="IDE0007" Action="None" />
<Rule Id="IDE0007WithoutSuggestion" Action="None" />
<Rule Id="IDE0008" Action="Error" />
<Rule Id="IDE0008WithoutSuggestion" Action="Error" />
<Rule Id="IDE0009" Action="None" />
<Rule Id="IDE0009WithoutSuggestion" Action="None" />
<Rule Id="IDE0011" Action="None" />
<Rule Id="IDE0011WithoutSuggestion" Action="None" />
<Rule Id="IDE0012" Action="None" />
<Rule Id="IDE0013" Action="None" />
<Rule Id="IDE0014" Action="None" />
<Rule Id="IDE0015" Action="None" />
<Rule Id="IDE0016" Action="None" />
<Rule Id="IDE0016WithoutSuggestion" Action="None" />
<Rule Id="IDE0017" Action="None" />
<Rule Id="IDE0017WithoutSuggestion" Action="None" />
<Rule Id="IDE0018" Action="None" />
<Rule Id="IDE0018WithoutSuggestion" Action="None" />
<Rule Id="IDE0019" Action="None" />
<Rule Id="IDE0019WithoutSuggestion" Action="None" />
<Rule Id="IDE0020" Action="None" />
<Rule Id="IDE0020WithoutSuggestion" Action="None" />
<Rule Id="IDE0021" Action="None" />
<Rule Id="IDE0021WithoutSuggestion" Action="None" />
<Rule Id="IDE0022" Action="None" />
<Rule Id="IDE0022WithoutSuggestion" Action="None" />
<Rule Id="IDE0023" Action="None" />
<Rule Id="IDE0023WithoutSuggestion" Action="None" />
<Rule Id="IDE0024" Action="None" />
<Rule Id="IDE0024WithoutSuggestion" Action="None" />
<Rule Id="IDE0025" Action="None" />
<Rule Id="IDE0025WithoutSuggestion" Action="None" />
<Rule Id="IDE0026" Action="None" />
<Rule Id="IDE0026WithoutSuggestion" Action="None" />
<Rule Id="IDE0027" Action="None" />
<Rule Id="IDE0027WithoutSuggestion" Action="None" />
<Rule Id="IDE0028" Action="None" />
<Rule Id="IDE0028WithoutSuggestion" Action="None" />
<Rule Id="IDE0029" Action="None" />
<Rule Id="IDE0029WithoutSuggestion" Action="None" />
<Rule Id="IDE0030" Action="None" />
<Rule Id="IDE0030WithoutSuggestion" Action="None" />
<Rule Id="IDE0031" Action="None" />
<Rule Id="IDE0031WithoutSuggestion" Action="None" />
<Rule Id="IDE1005" Action="None" />
<Rule Id="IDE1005WithoutSuggestion" Action="None" />
<Rule Id="IDE1006" Action="None" />
<Rule Id="IDE1006WithoutSuggestion" Action="None" />
</Rules>
<Rules AnalyzerId="Microsoft.CodeAnalysis.Features" RuleNamespace="Microsoft.CodeAnalysis.Features">
<Rule Id="IDE0010" Action="None" />
<Rule Id="IDE0010WithoutSuggestion" Action="None" />
<Rule Id="IDE0033" Action="None" />
<Rule Id="IDE0033WithoutSuggestion" Action="None" />
</Rules>
<Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.Analyzers">
<Rule Id="SA0001" Action="None" />
<Rule Id="SA0002" Action="Error" />
<Rule Id="SA1000" Action="Error" />
<Rule Id="SA1001" Action="Error" />
<Rule Id="SA1002" Action="Error" />
<Rule Id="SA1003" Action="Error" />
<Rule Id="SA1004" Action="None" />
<Rule Id="SA1005" Action="None" />
<Rule Id="SA1006" Action="Error" />
<Rule Id="SA1007" Action="Error" />
<Rule Id="SA1008" Action="Error" />
<Rule Id="SA1009" Action="Error" />
<Rule Id="SA1010" Action="Error" />
<Rule Id="SA1011" Action="Error" />
<Rule Id="SA1012" Action="Error" />
<Rule Id="SA1013" Action="None" />
<Rule Id="SA1014" Action="Error" />
<Rule Id="SA1015" Action="Error" />
<Rule Id="SA1016" Action="Error" />
<Rule Id="SA1017" Action="Error" />
<Rule Id="SA1018" Action="Error" />
<Rule Id="SA1019" Action="Error" />
<Rule Id="SA1020" Action="Error" />
<Rule Id="SA1021" Action="Error" />
<Rule Id="SA1022" Action="Error" />
<Rule Id="SA1023" Action="Error" />
<Rule Id="SA1024" Action="Error" />
<Rule Id="SA1025" Action="Error" />
<Rule Id="SA1026" Action="Error" />
<Rule Id="SA1027" Action="Error" />
<Rule Id="SA1028" Action="None" />
<Rule Id="SA1100" Action="Error" />
<Rule Id="SA1101" Action="Error" />
<Rule Id="SA1102" Action="Error" />
<Rule Id="SA1103" Action="Error" />
<Rule Id="SA1104" Action="Error" />
<Rule Id="SA1105" Action="Error" />
<Rule Id="SA1106" Action="Error" />
<Rule Id="SA1107" Action="Error" />
<Rule Id="SA1108" Action="Error" />
<Rule Id="SA1110" Action="Error" />
<Rule Id="SA1111" Action="Error" />
<Rule Id="SA1112" Action="Error" />
<Rule Id="SA1113" Action="Error" />
<Rule Id="SA1114" Action="Error" />
<Rule Id="SA1115" Action="Error" />
<Rule Id="SA1116" Action="None" />
<Rule Id="SA1117" Action="None" />
<Rule Id="SA1118" Action="None" />
<Rule Id="SA1119" Action="None" />
<Rule Id="SA1120" Action="Error" />
<Rule Id="SA1121" Action="Error" />
<Rule Id="SA1122" Action="None" />
<Rule Id="SA1123" Action="Error" />
<Rule Id="SA1124" Action="None" />
<Rule Id="SA1125" Action="Error" />
<Rule Id="SA1127" Action="Error" />
<Rule Id="SA1128" Action="None" />
<Rule Id="SA1129" Action="Error" />
<Rule Id="SA1130" Action="Error" />
<Rule Id="SA1131" Action="None" />
<Rule Id="SA1132" Action="Error" />
<Rule Id="SA1133" Action="Error" />
<Rule Id="SA1134" Action="Error" />
<Rule Id="SA1136" Action="Error" />
<Rule Id="SA1137" Action="None" />
<Rule Id="SA1139" Action="Error" />
<Rule Id="SA1200" Action="Error" />
<Rule Id="SA1201" Action="None" />
<Rule Id="SA1202" Action="None" />
<Rule Id="SA1203" Action="Error" />
<Rule Id="SA1204" Action="None" />
<Rule Id="SA1205" Action="Error" />
<Rule Id="SA1206" Action="Error" />
<Rule Id="SA1207" Action="Error" />
<Rule Id="SA1208" Action="Error" />
<Rule Id="SA1209" Action="Error" />
<Rule Id="SA1210" Action="Error" />
<Rule Id="SA1211" Action="Error" />
<Rule Id="SA1212" Action="Error" />
<Rule Id="SA1213" Action="Error" />
<Rule Id="SA1214" Action="None" />
<Rule Id="SA1216" Action="Error" />
<Rule Id="SA1217" Action="Error" />
<Rule Id="SA1300" Action="Error" />
<Rule Id="SA1302" Action="Error" />
<Rule Id="SA1303" Action="Error" />
<Rule Id="SA1304" Action="Error" />
<Rule Id="SA1306" Action="None" />
<Rule Id="SA1307" Action="Error" />
<Rule Id="SA1308" Action="Error" />
<Rule Id="SA1309" Action="Error" />
<Rule Id="SA1310" Action="Error" />
<Rule Id="SA1311" Action="Error" />
<Rule Id="SA1312" Action="None" />
<Rule Id="SA1313" Action="Error" />
<Rule Id="SA1314" Action="Error" />
<Rule Id="SA1316" Action="None" />
<Rule Id="SA1400" Action="Error" />
<Rule Id="SA1401" Action="Error" />
<Rule Id="SA1402" Action="Error" />
<Rule Id="SA1403" Action="Error" />
<Rule Id="SA1404" Action="Error" />
<Rule Id="SA1405" Action="Error" />
<Rule Id="SA1406" Action="Error" />
<Rule Id="SA1407" Action="Error" />
<Rule Id="SA1408" Action="Error" />
<Rule Id="SA1410" Action="Error" />
<Rule Id="SA1411" Action="Error" />
<Rule Id="SA1413" Action="None" />
<Rule Id="SA1500" Action="Error" />
<Rule Id="SA1501" Action="Error" />
<Rule Id="SA1502" Action="Error" />
<Rule Id="SA1503" Action="Error" />
<Rule Id="SA1504" Action="Error" />
<Rule Id="SA1505" Action="None" />
<Rule Id="SA1506" Action="Error" />
<Rule Id="SA1507" Action="None" />
<Rule Id="SA1508" Action="None" />
<Rule Id="SA1509" Action="Error" />
<Rule Id="SA1510" Action="Error" />
<Rule Id="SA1511" Action="Error" />
<Rule Id="SA1512" Action="None" />
<Rule Id="SA1513" Action="None" />
<Rule Id="SA1514" Action="Error" />
<Rule Id="SA1515" Action="Error" />
<Rule Id="SA1516" Action="Error" />
<Rule Id="SA1517" Action="Error" />
<Rule Id="SA1518" Action="Error" />
<Rule Id="SA1519" Action="Error" />
<Rule Id="SA1520" Action="Error" />
<Rule Id="SA1600" Action="None" />
<Rule Id="SA1601" Action="Error" />
<Rule Id="SA1602" Action="None" />
<Rule Id="SA1604" Action="Error" />
<Rule Id="SA1605" Action="Error" />
<Rule Id="SA1606" Action="Error" />
<Rule Id="SA1607" Action="Error" />
<Rule Id="SA1608" Action="Error" />
<Rule Id="SA1610" Action="Error" />
<Rule Id="SA1611" Action="None" />
<Rule Id="SA1612" Action="Error" />
<Rule Id="SA1613" Action="Error" />
<Rule Id="SA1614" Action="None" />
<Rule Id="SA1615" Action="None" />
<Rule Id="SA1616" Action="Error" />
<Rule Id="SA1617" Action="Error" />
<Rule Id="SA1618" Action="None" />
<Rule Id="SA1619" Action="Error" />
<Rule Id="SA1620" Action="Error" />
<Rule Id="SA1621" Action="Error" />
<Rule Id="SA1622" Action="Error" />
<Rule Id="SA1623" Action="None" />
<Rule Id="SA1624" Action="Error" />
<Rule Id="SA1625" Action="Error" />
<Rule Id="SA1626" Action="Error" />
<Rule Id="SA1627" Action="Error" />
<Rule Id="SA1629" Action="None" />
<Rule Id="SA1633" Action="None" />
<Rule Id="SA1634" Action="Error" />
<Rule Id="SA1635" Action="None" />
<Rule Id="SA1636" Action="None" />
<Rule Id="SA1637" Action="Error" />
<Rule Id="SA1638" Action="Error" />
<Rule Id="SA1640" Action="Error" />
<Rule Id="SA1641" Action="None" />
<Rule Id="SA1642" Action="None" />
<Rule Id="SA1643" Action="Error" />
<Rule Id="SA1648" Action="Error" />
<Rule Id="SA1649" Action="Error" />
<Rule Id="SA1651" Action="Error" />
</Rules>
<Rules AnalyzerId="Microsoft.CodeAnalysis.FxCopAnalyzers" RuleNamespace="Microsoft.CodeAnalysis.FxCopAnalyzers">
<!-- Do not directly await a Task without calling ConfigureAwait -->
<Rule Id="CA2007" Action="None" />
<!-- Do not create tasks without passing a TaskScheduler -->
<Rule Id="CA2008" Action="None" />
</Rules>
<Rules AnalyzerId="Microsoft.VisualStudio.Threading" RuleNamespace="Microsoft.VisualStudio.Threading">
<!-- Avoid problematic synchronous waits -->
<Rule Id="VSTHRD002" Action="None" />
<!-- Avoid method overloads that assume TaskScheduler.Current -->
<Rule Id="VSTHRD105" Action="None" />
</Rules>
<Rules AnalyzerId="Style" RuleNamespace="Style">
<Rule Id="IDE0059" Action="None" />
</Rules>
</RuleSet>

View 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);
}
}

View 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));
}
};
}

View 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());
}
};
}

View 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);
}
};
}

View 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;
}
};
}

View 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());
}
}

View File

@@ -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>

View 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());
}
};
}

View 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());
}
}
};
}

View 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());
}
};
}

View File

@@ -0,0 +1,5 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------
#include "pch.h"

View 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"

View 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;
}
}

View 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;
}

View 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();
}
}

View 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);
};
}

View 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"

File diff suppressed because it is too large Load Diff

View 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;
};
}

View 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);
}
};
}

View 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();
}
}

View 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;
}

View 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;
}
}

View 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; }
}

View 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};
};
}

View 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;
}

View 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;
}

View 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); }
}

View 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);
}
};
}

View 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>

View 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); }
}

View 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;
}
}

View 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
View 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;
}
}

View 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;
}
}

View 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);
}
}

View 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;
}

View 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&lt;std::string&gt;("foo: %s", "some value").c_str());
/// Logger::WriteMessage(make_string&lt;std::wstring&gt;(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;
};
}
}

View 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();
}
}

View 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())
};
}
};
}

View 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;

View 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);
}
}

View File

@@ -0,0 +1,5 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------
#include "pch.h"

View File

@@ -0,0 +1,7 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------
#pragma once
#include "framework.h"

View 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
View 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>>>;
}

View 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;
}
}
}

View 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
View 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);
}
}
}

View 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
View 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
View 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;
}
}
}

View 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>

View 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;
}
}
}

View 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).

View 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;
}
}
}

View 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;
}
}
}

View 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
}
}

View 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;
}
}
}

View 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;
}
}
}

View 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" /> &lt;= <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);
}
}
}

View 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);
}
}
}

View 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);
}
}
}
}

52
src/Directory.Build.props Normal file
View File

@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<Project>
<PropertyGroup>
<LangVersion>8.0</LangVersion>
<CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
<CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)\CodeAnalysis.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup Condition="$(UsingMicrosoftNoTargetsSdk) != 'true'">
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="3.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.164">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<PropertyGroup>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<ItemDefinitionGroup>
<ClCompile>
<TreatSpecificWarningsAsErrors>4706;4189;4701;4244;%(TreatSpecificWarningsAsErrors)</TreatSpecificWarningsAsErrors>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
<LanguageStandard>stdcpplatest</LanguageStandard>
<!-- Generate doc files from cpp/h files locally. -->
<GenerateXMLDocumentationFiles>true</GenerateXMLDocumentationFiles>
<UseFullPaths>true</UseFullPaths>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<GenerateDebugInformation>DebugFull</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
</ClCompile>
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
</Project>

181
src/HybridRow.sln Normal file
View File

@@ -0,0 +1,181 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29926.136
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Azure.Cosmos.Serialization.HybridRow", "Serialization\HybridRow\Microsoft.Azure.Cosmos.Serialization.HybridRow.csproj", "{490D42EE-1FEF-47CC-97E4-782A353B4D58}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Azure.Cosmos.Core", "Core\Core\Microsoft.Azure.Cosmos.Core.csproj", "{4955C705-BA27-4813-83EF-A55A6B6A49CD}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Azure.Cosmos.Serialization.HybridRowGenerator", "Serialization\HybridRowGenerator\Microsoft.Azure.Cosmos.Serialization.HybridRowGenerator.csproj", "{A1522622-82DF-4ADF-A6BC-7E2992C97BBF}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Azure.Cosmos.Serialization.HybridRow.Tests.Unit", "Serialization\HybridRow.Tests.Unit\Microsoft.Azure.Cosmos.Serialization.HybridRow.Tests.Unit.csproj", "{61A97067-8426-4EA3-A176-91E1C50BC279}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Azure.Cosmos.Serialization.HybridRow.Json", "Serialization\HybridRow.Json\Microsoft.Azure.Cosmos.Serialization.HybridRow.Json.csproj", "{6CEE8A59-74F2-40A9-AA66-E6A6EEFDEEEE}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Azure.Cosmos.Serialization.HybridRow.Tests.Perf", "Serialization\HybridRow.Tests.Perf\Microsoft.Azure.Cosmos.Serialization.HybridRow.Tests.Perf.csproj", "{0FC8DEDB-1DEF-477B-A669-029A60109FA0}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Azure.Cosmos.Serialization.HybridRowCLI", "Serialization\HybridRowCLI\Microsoft.Azure.Cosmos.Serialization.HybridRowCLI.csproj", "{AFAA0AAB-F62C-400F-AFB4-22D902732721}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Azure.Cosmos.Serialization.HybridRowStress", "Serialization\HybridRowStress\Microsoft.Azure.Cosmos.Serialization.HybridRowStress.csproj", "{0E1D663C-398A-4CD9-A1B6-C273FEDC801B}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "HybridRow.Native", "Serialization\HybridRow.Native\HybridRow.Native.vcxproj", "{D02FD209-EFF3-4D3F-B98E-E1BF9A1DD894}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Azure.Cosmos.Core.Native", "Core\Core.Native\Microsoft.Azure.Cosmos.Core.Native.vcxproj", "{EAED7D41-3DE6-4C41-A0E4-40D53EA3DABA}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Azure.Cosmos.Core.Native.Tests.Unit", "Core\Core.Native.Tests.Unit\Microsoft.Azure.Cosmos.Core.Native.Tests.Unit.vcxproj", "{BE99633B-5D7D-41DA-8550-035E6689B243}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "HybridRow.Native.Tests.Unit", "Serialization\HybridRow.Native.Tests.Unit\HybridRow.Native.Tests.Unit.vcxproj", "{209D2BFA-982F-4BB7-ADEA-C63B926152B3}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{490D42EE-1FEF-47CC-97E4-782A353B4D58}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{490D42EE-1FEF-47CC-97E4-782A353B4D58}.Debug|Any CPU.Build.0 = Debug|Any CPU
{490D42EE-1FEF-47CC-97E4-782A353B4D58}.Debug|x64.ActiveCfg = Debug|Any CPU
{490D42EE-1FEF-47CC-97E4-782A353B4D58}.Debug|x64.Build.0 = Debug|Any CPU
{490D42EE-1FEF-47CC-97E4-782A353B4D58}.Debug|x86.ActiveCfg = Debug|Any CPU
{490D42EE-1FEF-47CC-97E4-782A353B4D58}.Debug|x86.Build.0 = Debug|Any CPU
{490D42EE-1FEF-47CC-97E4-782A353B4D58}.Release|Any CPU.ActiveCfg = Release|Any CPU
{490D42EE-1FEF-47CC-97E4-782A353B4D58}.Release|Any CPU.Build.0 = Release|Any CPU
{490D42EE-1FEF-47CC-97E4-782A353B4D58}.Release|x64.ActiveCfg = Release|Any CPU
{490D42EE-1FEF-47CC-97E4-782A353B4D58}.Release|x64.Build.0 = Release|Any CPU
{490D42EE-1FEF-47CC-97E4-782A353B4D58}.Release|x86.ActiveCfg = Release|Any CPU
{490D42EE-1FEF-47CC-97E4-782A353B4D58}.Release|x86.Build.0 = Release|Any CPU
{4955C705-BA27-4813-83EF-A55A6B6A49CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4955C705-BA27-4813-83EF-A55A6B6A49CD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4955C705-BA27-4813-83EF-A55A6B6A49CD}.Debug|x64.ActiveCfg = Debug|Any CPU
{4955C705-BA27-4813-83EF-A55A6B6A49CD}.Debug|x64.Build.0 = Debug|Any CPU
{4955C705-BA27-4813-83EF-A55A6B6A49CD}.Debug|x86.ActiveCfg = Debug|Any CPU
{4955C705-BA27-4813-83EF-A55A6B6A49CD}.Debug|x86.Build.0 = Debug|Any CPU
{4955C705-BA27-4813-83EF-A55A6B6A49CD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4955C705-BA27-4813-83EF-A55A6B6A49CD}.Release|Any CPU.Build.0 = Release|Any CPU
{4955C705-BA27-4813-83EF-A55A6B6A49CD}.Release|x64.ActiveCfg = Release|Any CPU
{4955C705-BA27-4813-83EF-A55A6B6A49CD}.Release|x64.Build.0 = Release|Any CPU
{4955C705-BA27-4813-83EF-A55A6B6A49CD}.Release|x86.ActiveCfg = Release|Any CPU
{4955C705-BA27-4813-83EF-A55A6B6A49CD}.Release|x86.Build.0 = Release|Any CPU
{A1522622-82DF-4ADF-A6BC-7E2992C97BBF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A1522622-82DF-4ADF-A6BC-7E2992C97BBF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A1522622-82DF-4ADF-A6BC-7E2992C97BBF}.Debug|x64.ActiveCfg = Debug|Any CPU
{A1522622-82DF-4ADF-A6BC-7E2992C97BBF}.Debug|x64.Build.0 = Debug|Any CPU
{A1522622-82DF-4ADF-A6BC-7E2992C97BBF}.Debug|x86.ActiveCfg = Debug|Any CPU
{A1522622-82DF-4ADF-A6BC-7E2992C97BBF}.Debug|x86.Build.0 = Debug|Any CPU
{A1522622-82DF-4ADF-A6BC-7E2992C97BBF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A1522622-82DF-4ADF-A6BC-7E2992C97BBF}.Release|Any CPU.Build.0 = Release|Any CPU
{A1522622-82DF-4ADF-A6BC-7E2992C97BBF}.Release|x64.ActiveCfg = Release|Any CPU
{A1522622-82DF-4ADF-A6BC-7E2992C97BBF}.Release|x64.Build.0 = Release|Any CPU
{A1522622-82DF-4ADF-A6BC-7E2992C97BBF}.Release|x86.ActiveCfg = Release|Any CPU
{A1522622-82DF-4ADF-A6BC-7E2992C97BBF}.Release|x86.Build.0 = Release|Any CPU
{61A97067-8426-4EA3-A176-91E1C50BC279}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{61A97067-8426-4EA3-A176-91E1C50BC279}.Debug|Any CPU.Build.0 = Debug|Any CPU
{61A97067-8426-4EA3-A176-91E1C50BC279}.Debug|x64.ActiveCfg = Debug|Any CPU
{61A97067-8426-4EA3-A176-91E1C50BC279}.Debug|x64.Build.0 = Debug|Any CPU
{61A97067-8426-4EA3-A176-91E1C50BC279}.Debug|x86.ActiveCfg = Debug|Any CPU
{61A97067-8426-4EA3-A176-91E1C50BC279}.Debug|x86.Build.0 = Debug|Any CPU
{61A97067-8426-4EA3-A176-91E1C50BC279}.Release|Any CPU.ActiveCfg = Release|Any CPU
{61A97067-8426-4EA3-A176-91E1C50BC279}.Release|Any CPU.Build.0 = Release|Any CPU
{61A97067-8426-4EA3-A176-91E1C50BC279}.Release|x64.ActiveCfg = Release|Any CPU
{61A97067-8426-4EA3-A176-91E1C50BC279}.Release|x64.Build.0 = Release|Any CPU
{61A97067-8426-4EA3-A176-91E1C50BC279}.Release|x86.ActiveCfg = Release|Any CPU
{61A97067-8426-4EA3-A176-91E1C50BC279}.Release|x86.Build.0 = Release|Any CPU
{6CEE8A59-74F2-40A9-AA66-E6A6EEFDEEEE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6CEE8A59-74F2-40A9-AA66-E6A6EEFDEEEE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6CEE8A59-74F2-40A9-AA66-E6A6EEFDEEEE}.Debug|x64.ActiveCfg = Debug|Any CPU
{6CEE8A59-74F2-40A9-AA66-E6A6EEFDEEEE}.Debug|x64.Build.0 = Debug|Any CPU
{6CEE8A59-74F2-40A9-AA66-E6A6EEFDEEEE}.Debug|x86.ActiveCfg = Debug|Any CPU
{6CEE8A59-74F2-40A9-AA66-E6A6EEFDEEEE}.Debug|x86.Build.0 = Debug|Any CPU
{6CEE8A59-74F2-40A9-AA66-E6A6EEFDEEEE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6CEE8A59-74F2-40A9-AA66-E6A6EEFDEEEE}.Release|Any CPU.Build.0 = Release|Any CPU
{6CEE8A59-74F2-40A9-AA66-E6A6EEFDEEEE}.Release|x64.ActiveCfg = Release|Any CPU
{6CEE8A59-74F2-40A9-AA66-E6A6EEFDEEEE}.Release|x64.Build.0 = Release|Any CPU
{6CEE8A59-74F2-40A9-AA66-E6A6EEFDEEEE}.Release|x86.ActiveCfg = Release|Any CPU
{6CEE8A59-74F2-40A9-AA66-E6A6EEFDEEEE}.Release|x86.Build.0 = Release|Any CPU
{0FC8DEDB-1DEF-477B-A669-029A60109FA0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0FC8DEDB-1DEF-477B-A669-029A60109FA0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0FC8DEDB-1DEF-477B-A669-029A60109FA0}.Debug|x64.ActiveCfg = Debug|Any CPU
{0FC8DEDB-1DEF-477B-A669-029A60109FA0}.Debug|x64.Build.0 = Debug|Any CPU
{0FC8DEDB-1DEF-477B-A669-029A60109FA0}.Debug|x86.ActiveCfg = Debug|Any CPU
{0FC8DEDB-1DEF-477B-A669-029A60109FA0}.Debug|x86.Build.0 = Debug|Any CPU
{0FC8DEDB-1DEF-477B-A669-029A60109FA0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0FC8DEDB-1DEF-477B-A669-029A60109FA0}.Release|Any CPU.Build.0 = Release|Any CPU
{0FC8DEDB-1DEF-477B-A669-029A60109FA0}.Release|x64.ActiveCfg = Release|Any CPU
{0FC8DEDB-1DEF-477B-A669-029A60109FA0}.Release|x64.Build.0 = Release|Any CPU
{0FC8DEDB-1DEF-477B-A669-029A60109FA0}.Release|x86.ActiveCfg = Release|Any CPU
{0FC8DEDB-1DEF-477B-A669-029A60109FA0}.Release|x86.Build.0 = Release|Any CPU
{AFAA0AAB-F62C-400F-AFB4-22D902732721}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AFAA0AAB-F62C-400F-AFB4-22D902732721}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AFAA0AAB-F62C-400F-AFB4-22D902732721}.Debug|x64.ActiveCfg = Debug|Any CPU
{AFAA0AAB-F62C-400F-AFB4-22D902732721}.Debug|x64.Build.0 = Debug|Any CPU
{AFAA0AAB-F62C-400F-AFB4-22D902732721}.Debug|x86.ActiveCfg = Debug|Any CPU
{AFAA0AAB-F62C-400F-AFB4-22D902732721}.Debug|x86.Build.0 = Debug|Any CPU
{AFAA0AAB-F62C-400F-AFB4-22D902732721}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AFAA0AAB-F62C-400F-AFB4-22D902732721}.Release|Any CPU.Build.0 = Release|Any CPU
{AFAA0AAB-F62C-400F-AFB4-22D902732721}.Release|x64.ActiveCfg = Release|Any CPU
{AFAA0AAB-F62C-400F-AFB4-22D902732721}.Release|x64.Build.0 = Release|Any CPU
{AFAA0AAB-F62C-400F-AFB4-22D902732721}.Release|x86.ActiveCfg = Release|Any CPU
{AFAA0AAB-F62C-400F-AFB4-22D902732721}.Release|x86.Build.0 = Release|Any CPU
{0E1D663C-398A-4CD9-A1B6-C273FEDC801B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0E1D663C-398A-4CD9-A1B6-C273FEDC801B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0E1D663C-398A-4CD9-A1B6-C273FEDC801B}.Debug|x64.ActiveCfg = Debug|Any CPU
{0E1D663C-398A-4CD9-A1B6-C273FEDC801B}.Debug|x64.Build.0 = Debug|Any CPU
{0E1D663C-398A-4CD9-A1B6-C273FEDC801B}.Debug|x86.ActiveCfg = Debug|Any CPU
{0E1D663C-398A-4CD9-A1B6-C273FEDC801B}.Debug|x86.Build.0 = Debug|Any CPU
{0E1D663C-398A-4CD9-A1B6-C273FEDC801B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0E1D663C-398A-4CD9-A1B6-C273FEDC801B}.Release|Any CPU.Build.0 = Release|Any CPU
{0E1D663C-398A-4CD9-A1B6-C273FEDC801B}.Release|x64.ActiveCfg = Release|Any CPU
{0E1D663C-398A-4CD9-A1B6-C273FEDC801B}.Release|x64.Build.0 = Release|Any CPU
{0E1D663C-398A-4CD9-A1B6-C273FEDC801B}.Release|x86.ActiveCfg = Release|Any CPU
{0E1D663C-398A-4CD9-A1B6-C273FEDC801B}.Release|x86.Build.0 = Release|Any CPU
{D02FD209-EFF3-4D3F-B98E-E1BF9A1DD894}.Debug|Any CPU.ActiveCfg = Debug|x64
{D02FD209-EFF3-4D3F-B98E-E1BF9A1DD894}.Debug|Any CPU.Build.0 = Debug|x64
{D02FD209-EFF3-4D3F-B98E-E1BF9A1DD894}.Debug|x64.ActiveCfg = Debug|x64
{D02FD209-EFF3-4D3F-B98E-E1BF9A1DD894}.Debug|x64.Build.0 = Debug|x64
{D02FD209-EFF3-4D3F-B98E-E1BF9A1DD894}.Debug|x86.ActiveCfg = Debug|x64
{D02FD209-EFF3-4D3F-B98E-E1BF9A1DD894}.Release|Any CPU.ActiveCfg = Release|x64
{D02FD209-EFF3-4D3F-B98E-E1BF9A1DD894}.Release|Any CPU.Build.0 = Release|x64
{D02FD209-EFF3-4D3F-B98E-E1BF9A1DD894}.Release|x64.ActiveCfg = Release|x64
{D02FD209-EFF3-4D3F-B98E-E1BF9A1DD894}.Release|x64.Build.0 = Release|x64
{D02FD209-EFF3-4D3F-B98E-E1BF9A1DD894}.Release|x86.ActiveCfg = Release|x64
{EAED7D41-3DE6-4C41-A0E4-40D53EA3DABA}.Debug|Any CPU.ActiveCfg = Debug|x64
{EAED7D41-3DE6-4C41-A0E4-40D53EA3DABA}.Debug|Any CPU.Build.0 = Debug|x64
{EAED7D41-3DE6-4C41-A0E4-40D53EA3DABA}.Debug|x64.ActiveCfg = Debug|x64
{EAED7D41-3DE6-4C41-A0E4-40D53EA3DABA}.Debug|x64.Build.0 = Debug|x64
{EAED7D41-3DE6-4C41-A0E4-40D53EA3DABA}.Debug|x86.ActiveCfg = Debug|x64
{EAED7D41-3DE6-4C41-A0E4-40D53EA3DABA}.Release|Any CPU.ActiveCfg = Release|x64
{EAED7D41-3DE6-4C41-A0E4-40D53EA3DABA}.Release|Any CPU.Build.0 = Release|x64
{EAED7D41-3DE6-4C41-A0E4-40D53EA3DABA}.Release|x64.ActiveCfg = Release|x64
{EAED7D41-3DE6-4C41-A0E4-40D53EA3DABA}.Release|x64.Build.0 = Release|x64
{EAED7D41-3DE6-4C41-A0E4-40D53EA3DABA}.Release|x86.ActiveCfg = Release|x64
{BE99633B-5D7D-41DA-8550-035E6689B243}.Debug|Any CPU.ActiveCfg = Debug|x64
{BE99633B-5D7D-41DA-8550-035E6689B243}.Debug|Any CPU.Build.0 = Debug|x64
{BE99633B-5D7D-41DA-8550-035E6689B243}.Debug|x64.ActiveCfg = Debug|x64
{BE99633B-5D7D-41DA-8550-035E6689B243}.Debug|x64.Build.0 = Debug|x64
{BE99633B-5D7D-41DA-8550-035E6689B243}.Debug|x86.ActiveCfg = Debug|x64
{BE99633B-5D7D-41DA-8550-035E6689B243}.Release|Any CPU.ActiveCfg = Release|x64
{BE99633B-5D7D-41DA-8550-035E6689B243}.Release|Any CPU.Build.0 = Release|x64
{BE99633B-5D7D-41DA-8550-035E6689B243}.Release|x64.ActiveCfg = Release|x64
{BE99633B-5D7D-41DA-8550-035E6689B243}.Release|x64.Build.0 = Release|x64
{BE99633B-5D7D-41DA-8550-035E6689B243}.Release|x86.ActiveCfg = Release|x64
{209D2BFA-982F-4BB7-ADEA-C63B926152B3}.Debug|Any CPU.ActiveCfg = Debug|x64
{209D2BFA-982F-4BB7-ADEA-C63B926152B3}.Debug|x64.ActiveCfg = Debug|x64
{209D2BFA-982F-4BB7-ADEA-C63B926152B3}.Debug|x64.Build.0 = Debug|x64
{209D2BFA-982F-4BB7-ADEA-C63B926152B3}.Debug|x86.ActiveCfg = Debug|x64
{209D2BFA-982F-4BB7-ADEA-C63B926152B3}.Release|Any CPU.ActiveCfg = Release|x64
{209D2BFA-982F-4BB7-ADEA-C63B926152B3}.Release|x64.ActiveCfg = Release|x64
{209D2BFA-982F-4BB7-ADEA-C63B926152B3}.Release|x64.Build.0 = Release|x64
{209D2BFA-982F-4BB7-ADEA-C63B926152B3}.Release|x86.ActiveCfg = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {282F987F-245A-48B9-8115-F2503FCEAF4F}
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
<OutputType>Library</OutputType>
<RootNamespace>Microsoft.Azure.Cosmos.Serialization.HybridRow.Json</RootNamespace>
<AssemblyName>Microsoft.Azure.Cosmos.Serialization.HybridRow.Json</AssemblyName>
<TargetFramework>netstandard2.1</TargetFramework>
<PlatformTarget>AnyCPU</PlatformTarget>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="10.0.2"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Core\Core\Microsoft.Azure.Cosmos.Core.csproj" />
<ProjectReference Include="..\HybridRow\Microsoft.Azure.Cosmos.Serialization.HybridRow.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,453 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------
namespace Microsoft.Azure.Cosmos.Serialization.HybridRow.Json
{
using System;
using System.Text;
using Microsoft.Azure.Cosmos.Core;
using Microsoft.Azure.Cosmos.Core.Utf8;
using Microsoft.Azure.Cosmos.Serialization.HybridRow.IO;
using Microsoft.Azure.Cosmos.Serialization.HybridRow.Layouts;
public static class RowReaderJsonExtensions
{
/// <summary>
/// Project a JSON document from a HybridRow <see cref="RowReader"/>.
/// </summary>
/// <param name="reader">The reader to project to JSON.</param>
/// <param name="str">If successful, the JSON document that corresponds to the <paramref name="reader"/>.</param>
/// <returns>The result.</returns>
public static Result ToJson(this ref RowReader reader, out string str)
{
return reader.ToJson(new RowReaderJsonSettings(" "), out str);
}
/// <summary>
/// Project a JSON document from a HybridRow <see cref="RowReader"/>.
/// </summary>
/// <param name="reader">The reader to project to JSON.</param>
/// <param name="settings">Settings that control how the JSON document is formatted.</param>
/// <param name="str">If successful, the JSON document that corresponds to the <paramref name="reader"/>.</param>
/// <returns>The result.</returns>
public static Result ToJson(this ref RowReader reader, RowReaderJsonSettings settings, out string str)
{
ReaderStringContext ctx = new ReaderStringContext(
new StringBuilder(),
new RowReaderJsonSettings(settings.IndentChars, settings.QuoteChar == '\'' ? '\'' : '"'),
1);
ctx.Builder.Append("{");
Result result = RowReaderJsonExtensions.ToJson(ref reader, ctx);
if (result != Result.Success)
{
str = null;
return result;
}
ctx.Builder.Append(ctx.NewLine);
ctx.Builder.Append("}");
str = ctx.Builder.ToString();
return Result.Success;
}
private static Result ToJson(ref RowReader reader, ReaderStringContext ctx)
{
int index = 0;
while (reader.Read())
{
string path = !reader.Path.IsNull ? $"{ctx.Settings.QuoteChar}{reader.Path}{ctx.Settings.QuoteChar}:" : null;
if (index != 0)
{
ctx.Builder.Append(',');
}
index++;
ctx.Builder.Append(ctx.NewLine);
ctx.WriteIndent();
if (path != null)
{
ctx.Builder.Append(path);
ctx.Builder.Append(ctx.Separator);
}
Result r;
char scopeBracket = default;
char scopeCloseBracket = default;
switch (reader.Type.LayoutCode)
{
case LayoutCode.Null:
{
r = reader.ReadNull(out NullValue _);
if (r != Result.Success)
{
return r;
}
ctx.Builder.Append("null");
break;
}
case LayoutCode.Boolean:
{
r = reader.ReadBool(out bool value);
if (r != Result.Success)
{
return r;
}
ctx.Builder.Append(value);
break;
}
case LayoutCode.Int8:
{
r = reader.ReadInt8(out sbyte value);
if (r != Result.Success)
{
return r;
}
ctx.Builder.Append(value);
break;
}
case LayoutCode.Int16:
{
r = reader.ReadInt16(out short value);
if (r != Result.Success)
{
return r;
}
ctx.Builder.Append(value);
break;
}
case LayoutCode.Int32:
{
r = reader.ReadInt32(out int value);
if (r != Result.Success)
{
return r;
}
ctx.Builder.Append(value);
break;
}
case LayoutCode.Int64:
{
r = reader.ReadInt64(out long value);
if (r != Result.Success)
{
return r;
}
ctx.Builder.Append(value);
break;
}
case LayoutCode.UInt8:
{
r = reader.ReadUInt8(out byte value);
if (r != Result.Success)
{
return r;
}
ctx.Builder.Append(value);
break;
}
case LayoutCode.UInt16:
{
r = reader.ReadUInt16(out ushort value);
if (r != Result.Success)
{
return r;
}
ctx.Builder.Append(value);
break;
}
case LayoutCode.UInt32:
{
r = reader.ReadUInt32(out uint value);
if (r != Result.Success)
{
return r;
}
ctx.Builder.Append(value);
break;
}
case LayoutCode.UInt64:
{
r = reader.ReadUInt64(out ulong value);
if (r != Result.Success)
{
return r;
}
ctx.Builder.Append(value);
break;
}
case LayoutCode.VarInt:
{
r = reader.ReadVarInt(out long value);
if (r != Result.Success)
{
return r;
}
ctx.Builder.Append(value);
break;
}
case LayoutCode.VarUInt:
{
r = reader.ReadVarUInt(out ulong value);
if (r != Result.Success)
{
return r;
}
ctx.Builder.Append(value);
break;
}
case LayoutCode.Float32:
{
r = reader.ReadFloat32(out float value);
if (r != Result.Success)
{
return r;
}
ctx.Builder.Append(value);
break;
}
case LayoutCode.Float64:
{
r = reader.ReadFloat64(out double value);
if (r != Result.Success)
{
return r;
}
ctx.Builder.Append(value);
break;
}
case LayoutCode.Float128:
{
r = reader.ReadFloat128(out Float128 _);
if (r != Result.Success)
{
return r;
}
// ctx.Builder.AppendFormat("High: {0}, Low: {1}\n", value.High, value.Low);
Contract.Assert(false, "Float128 are not supported.");
break;
}
case LayoutCode.Decimal:
{
r = reader.ReadDecimal(out decimal value);
if (r != Result.Success)
{
return r;
}
ctx.Builder.Append(value);
break;
}
case LayoutCode.DateTime:
{
r = reader.ReadDateTime(out DateTime value);
if (r != Result.Success)
{
return r;
}
ctx.Builder.Append(ctx.Settings.QuoteChar);
ctx.Builder.Append(value);
ctx.Builder.Append(ctx.Settings.QuoteChar);
break;
}
case LayoutCode.UnixDateTime:
{
r = reader.ReadUnixDateTime(out UnixDateTime value);
if (r != Result.Success)
{
return r;
}
ctx.Builder.Append(value.Milliseconds);
break;
}
case LayoutCode.Guid:
{
r = reader.ReadGuid(out Guid value);
if (r != Result.Success)
{
return r;
}
ctx.Builder.Append(ctx.Settings.QuoteChar);
ctx.Builder.Append(value.ToString());
ctx.Builder.Append(ctx.Settings.QuoteChar);
break;
}
case LayoutCode.MongoDbObjectId:
{
r = reader.ReadMongoDbObjectId(out MongoDbObjectId value);
if (r != Result.Success)
{
return r;
}
ctx.Builder.Append(ctx.Settings.QuoteChar);
ReadOnlyMemory<byte> bytes = value.ToByteArray();
ctx.Builder.Append(bytes.Span.ToHexString());
ctx.Builder.Append(ctx.Settings.QuoteChar);
break;
}
case LayoutCode.Utf8:
{
r = reader.ReadString(out Utf8Span value);
if (r != Result.Success)
{
return r;
}
ctx.Builder.Append(ctx.Settings.QuoteChar);
ctx.Builder.Append(value.ToString());
ctx.Builder.Append(ctx.Settings.QuoteChar);
break;
}
case LayoutCode.Binary:
{
r = reader.ReadBinary(out ReadOnlySpan<byte> value);
if (r != Result.Success)
{
return r;
}
ctx.Builder.Append(ctx.Settings.QuoteChar);
ctx.Builder.Append(value.ToHexString());
ctx.Builder.Append(ctx.Settings.QuoteChar);
break;
}
case LayoutCode.NullableScope:
case LayoutCode.ImmutableNullableScope:
{
if (!reader.HasValue)
{
ctx.Builder.Append("null");
break;
}
goto case LayoutCode.TypedTupleScope;
}
case LayoutCode.ArrayScope:
case LayoutCode.ImmutableArrayScope:
case LayoutCode.TypedArrayScope:
case LayoutCode.ImmutableTypedArrayScope:
case LayoutCode.TypedSetScope:
case LayoutCode.ImmutableTypedSetScope:
case LayoutCode.TypedMapScope:
case LayoutCode.ImmutableTypedMapScope:
case LayoutCode.TupleScope:
case LayoutCode.ImmutableTupleScope:
case LayoutCode.TypedTupleScope:
case LayoutCode.ImmutableTypedTupleScope:
case LayoutCode.TaggedScope:
case LayoutCode.ImmutableTaggedScope:
case LayoutCode.Tagged2Scope:
case LayoutCode.ImmutableTagged2Scope:
scopeBracket = '[';
scopeCloseBracket = ']';
goto case LayoutCode.EndScope;
case LayoutCode.ObjectScope:
case LayoutCode.ImmutableObjectScope:
case LayoutCode.Schema:
case LayoutCode.ImmutableSchema:
scopeBracket = '{';
scopeCloseBracket = '}';
goto case LayoutCode.EndScope;
case LayoutCode.EndScope:
{
ctx.Builder.Append(scopeBracket);
int snapshot = ctx.Builder.Length;
r = reader.ReadScope(new ReaderStringContext(ctx.Builder, ctx.Settings, ctx.Indent + 1), RowReaderJsonExtensions.ToJson);
if (r != Result.Success)
{
return r;
}
if (ctx.Builder.Length != snapshot)
{
ctx.Builder.Append(ctx.NewLine);
ctx.WriteIndent();
}
ctx.Builder.Append(scopeCloseBracket);
break;
}
default:
{
Contract.Assert(false, $"Unknown type will be ignored: {reader.Type.LayoutCode}");
break;
}
}
}
return Result.Success;
}
private readonly struct ReaderStringContext
{
public readonly int Indent;
public readonly StringBuilder Builder;
public readonly RowReaderJsonSettings Settings;
public readonly string Separator;
public readonly string NewLine;
public ReaderStringContext(StringBuilder builder, RowReaderJsonSettings settings, int indent)
{
this.Settings = settings;
this.Separator = settings.IndentChars == null ? "" : " ";
this.NewLine = settings.IndentChars == null ? "" : "\n";
this.Indent = indent;
this.Builder = builder;
}
public void WriteIndent()
{
string indentChars = this.Settings.IndentChars ?? "";
for (int i = 0; i < this.Indent; i++)
{
this.Builder.Append(indentChars);
}
}
}
}
}

View File

@@ -0,0 +1,24 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------
#pragma warning disable CA1051 // Do not declare visible instance fields
namespace Microsoft.Azure.Cosmos.Serialization.HybridRow.Json
{
public readonly struct RowReaderJsonSettings
{
/// <summary>If non-null then child objects are indented by one copy of this string per level.</summary>
public readonly string IndentChars;
/// <summary>The quote character to use.</summary>
/// <remarks>May be <see cref="lang:\""/> or <see cref="'" />.</remarks>
public readonly char QuoteChar;
public RowReaderJsonSettings(string indentChars = " ", char quoteChar = '"')
{
this.IndentChars = indentChars;
this.QuoteChar = quoteChar;
}
}
}

View File

@@ -0,0 +1,31 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------
#pragma once
#include "CppUnitTest.h"
namespace cdb_hr_test
{
using namespace std::literals;
using Assert = Microsoft::VisualStudio::CppUnitTestFramework::Assert;
struct CollectionAssert final
{
template<typename T>
static void AreEqual(cdb_core::ReadOnlySpan<T> expected, cdb_core::ReadOnlySpan<T> value)
{
CollectionAssert::AreEqual<T>(expected, value, ""sv);
}
template<typename T>
static void AreEqual(cdb_core::ReadOnlySpan<T> expected, cdb_core::ReadOnlySpan<T> value, std::wstring_view message)
{
Assert::AreEqual(expected.Length(), value.Length(), message.data());
for (uint32_t i = 0; i < expected.Length(); i++)
{
Assert::AreEqual(expected[i], value[i], message.data());
}
}
};
}

View File

@@ -0,0 +1,203 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------
#pragma once
#include "CppUnitTest.h"
// ReSharper disable CppClangTidyCppcoreguidelinesMacroUsage
// Additional MACROS to simplify definitions.
#define TEST_METHOD_WITH_OWNER(methodName, ownerAlias)\
BEGIN_TEST_METHOD_ATTRIBUTE(methodName)\
TEST_OWNER(L ## ownerAlias)\
END_TEST_METHOD_ATTRIBUTE()\
TEST_METHOD(methodName)
// Additional string function for test diagnostics.
namespace Microsoft::VisualStudio::CppUnitTestFramework
{
template<>
inline 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());
}
template<>
inline std::wstring ToString<cdb_hr::Result>(
const cdb_hr::Result& q)
{
return cdb_core::make_string(L"{%d}", q);
}
template<>
inline std::wstring ToString<std::byte>(const std::byte& q)
{
return cdb_core::make_string(L"{%u}", q);
}
template<>
inline std::wstring ToString<uint16_t>(const uint16_t& q)
{
return cdb_core::make_string(L"{%u}", q);
}
template<>
inline std::wstring ToString<cdb_hr::LayoutType>(
const cdb_hr::LayoutType* q)
{
return cdb_core::make_string(L"{%p}", q);
}
template<>
inline std::wstring ToString<cdb_hr::Layout>(
const cdb_hr::Layout* q)
{
return cdb_core::make_string(L"{%p}", q);
}
template<>
inline std::wstring ToString<cdb_hr::Layout>(
const cdb_hr::Layout& q)
{
return cdb_core::make_string(L"{%p}", &q);
}
template<>
inline std::wstring ToString<cdb_hr::LayoutScope>(
const cdb_hr::LayoutScope* q)
{
return cdb_core::make_string(L"{%p}", q);
}
template<>
inline std::wstring ToString<cdb_hr::LayoutUDT>(
const cdb_hr::LayoutUDT* q)
{
return cdb_core::make_string(L"{%p}", q);
}
template<>
inline std::wstring ToString<cdb_hr::LayoutColumn>(
const cdb_hr::LayoutColumn& q)
{
return cdb_core::make_string(L"{%p}", &q);
}
template<>
inline std::wstring ToString<cdb_hr::LayoutCode>(
const cdb_hr::LayoutCode& q)
{
return cdb_core::make_string(L"{%u}", q);
}
template<>
inline std::wstring ToString<cdb_hr::TypeKind>(
const cdb_hr::TypeKind& q)
{
std::string_view v = cdb_hr::ToStringView(q);
return cdb_core::make_string(L"{%.*S}", v.size(), v.data());
}
template<>
inline std::wstring ToString<cdb_hr::SortDirection>(
const cdb_hr::SortDirection& q)
{
return cdb_core::make_string(L"{%u}", q);
}
template<>
inline std::wstring ToString<cdb_hr::AllowEmptyKind>(
const cdb_hr::AllowEmptyKind& q)
{
return cdb_core::make_string(L"{%u}", q);
}
template<>
inline std::wstring ToString<cdb_hr::SchemaLanguageVersion>(
const cdb_hr::SchemaLanguageVersion& q)
{
return cdb_core::make_string(L"{%u}", q);
}
template<>
inline std::wstring ToString<cdb_hr::StorageKind>(
const cdb_hr::StorageKind& q)
{
switch (q)
{
case cdb_hr::StorageKind::Sparse:
return cdb_core::make_string(L"Sparse{%u}", q);
case cdb_hr::StorageKind::Fixed:
return cdb_core::make_string(L"Fixed{%u}", q);
case cdb_hr::StorageKind::Variable:
return cdb_core::make_string(L"Variable{%u}", q);
default:
return cdb_core::make_string(L"{%u}", q);
}
}
template<>
inline std::wstring ToString<cdb_hr::HybridRowVersion>(
const cdb_hr::HybridRowVersion& q)
{
return cdb_core::make_string(L"{%u}", q);
}
template<>
inline std::wstring ToString<cdb_hr::SchemaId>(
const cdb_hr::SchemaId& q)
{
return cdb_core::make_string(L"{%d}", q.Id());
}
template<>
inline std::wstring ToString<cdb_hr::MongoDbObjectId>(
const cdb_hr::MongoDbObjectId& q)
{
return L"<mongo id>";
}
template<>
inline std::wstring ToString<cdb_hr::Guid>(
const cdb_hr::Guid& q)
{
return L"<guid>";
}
template<>
inline std::wstring ToString<cdb_hr::DateTime>(
const cdb_hr::DateTime& q)
{
return cdb_core::make_string(L"{%ld}", q.Ticks());
}
template<>
inline std::wstring ToString<cdb_hr::UnixDateTime>(
const cdb_hr::UnixDateTime& q)
{
return cdb_core::make_string(L"{%ld}", q.GetMilliseconds());
}
template<>
inline std::wstring ToString<cdb_hr::Decimal>(
const cdb_hr::Decimal& q)
{
return L"<decimal>";
}
template<>
inline std::wstring ToString<cdb_hr::Float128>(
const cdb_hr::Float128& q)
{
return cdb_core::make_string(L"Low: {%ld}, High: {%ld}", q.Low, q.High);
}
template<>
inline std::wstring ToString<cdb_hr::NullValue>(
const cdb_hr::NullValue& q)
{
return L"<NullValue>";
}
}

View File

@@ -0,0 +1,102 @@
<?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>{209D2BFA-982F-4BB7-ADEA-C63B926152B3}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>HybridRowNativeTestsUnit</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
<ProjectSubType>NativeUnitTestProject</ProjectSubType>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<UseOfMfc>false</UseOfMfc>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<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>
<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>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<AdditionalLibraryDirectories>$(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="HybridRowSerializerUnitTests.cpp" />
<ClCompile Include="SystemSchemaUnitTests.cpp" />
<ClCompile Include="TaggedSchema.generated.cpp" />
<ClCompile Include="TypedArrayUnitTests.cpp" />
<ClInclude Include="CollectionAssert.h" />
<ClInclude Include="CppUnitTestFramework.inl" />
<ClCompile Include="LayoutCompilerUnitTests.cpp" />
<ClCompile Include="LayoutLiteralUnitTests.cpp" />
<ClCompile Include="StringTokenizerUnitTests.cpp" />
<ClInclude Include="ResultAssert.h" />
<ClInclude Include="TaggedSchema.generated.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="pch.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Core\Core.Native\Microsoft.Azure.Cosmos.Core.Native.vcxproj">
<Project>{eaed7d41-3de6-4c41-a0e4-40d53ea3daba}</Project>
</ProjectReference>
<ProjectReference Include="..\HybridRow.Native\HybridRow.Native.vcxproj">
<Project>{d02fd209-eff3-4d3f-b98e-e1bf9a1dd894}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="cpp.hint" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
</Project>

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ClCompile Include="SystemSchemaUnitTests.cpp" />
<ClCompile Include="LayoutCompilerUnitTests.cpp" />
<ClCompile Include="LayoutLiteralUnitTests.cpp" />
<ClCompile Include="StringTokenizerUnitTests.cpp" />
<ClCompile Include="pch.cpp" />
<ClCompile Include="TaggedSchema.generated.cpp">
<Filter>Generated</Filter>
</ClCompile>
<ClCompile Include="TypedArrayUnitTests.cpp" />
<ClCompile Include="HybridRowSerializerUnitTests.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="CollectionAssert.h" />
<ClInclude Include="CppUnitTestFramework.inl" />
<ClInclude Include="ResultAssert.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="TaggedSchema.generated.h">
<Filter>Generated</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="cpp.hint" />
</ItemGroup>
<ItemGroup>
<Filter Include="Generated">
<UniqueIdentifier>{6bb289c5-5c7e-4d07-b35f-521c7671671a}</UniqueIdentifier>
</Filter>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,119 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------
#include "pch.h"
#include "CppUnitTest.h"
#include "CppUnitTestFramework.inl"
#include "ResultAssert.h"
namespace cdb_hr_test
{
using namespace std::literals;
using Assert = Microsoft::VisualStudio::CppUnitTestFramework::Assert;
TEST_CLASS(HybridRowSerializerUnitTests)
{
public:
TEST_METHOD_WITH_OWNER(TypedArraySerializerTest, "jthunter")
{
using TS1 = cdb_hr::TypedArrayHybridRowSerializer<std::string, cdb_hr::Utf8HybridRowSerializer>;
cdb_hr::TypeArgumentList typeArgs1{cdb_hr::TypeArgument{&cdb_hr::LayoutLiteral::Utf8}};
TestSerializer<TS1>(typeArgs1, {"abc"s, "xyz"s}, {"abc"s, "ghk"s}, {"abc"s});
using TS2 = cdb_hr::TypedArrayHybridRowSerializer<int32_t, cdb_hr::Int32HybridRowSerializer>;
cdb_hr::TypeArgumentList typeArgs2{{cdb_hr::TypeArgument{&cdb_hr::LayoutLiteral::Int32}}};
TestSerializer<TS2>(typeArgs2, {123, 456}, {123, 789}, {4733584});
}
TEST_METHOD_WITH_OWNER(ArraySerializerTest, "jthunter")
{
using TS1 = cdb_hr::ArrayHybridRowSerializer<std::string, cdb_hr::Utf8HybridRowSerializer>;
cdb_hr::TypeArgumentList typeArgs1{cdb_hr::TypeArgument{&cdb_hr::LayoutLiteral::Utf8}};
TestSerializer<TS1>(typeArgs1, {"abc"s, "xyz"s}, {"abc"s, "ghk"s}, {"abc"s});
using TS2 = cdb_hr::ArrayHybridRowSerializer<int32_t, cdb_hr::Int32HybridRowSerializer>;
cdb_hr::TypeArgumentList typeArgs2{{cdb_hr::TypeArgument{&cdb_hr::LayoutLiteral::Int32}}};
TestSerializer<TS2>(typeArgs2, {123, 456}, {123, 789}, {4733584});
}
TEST_METHOD_WITH_OWNER(TypedTupleSerializerTest, "jthunter")
{
using TS1 = cdb_hr::TypedTupleHybridRowSerializer<
cdb_hr::Utf8HybridRowSerializer,
cdb_hr::Int32HybridRowSerializer>;
cdb_hr::TypeArgumentList typeArgs1{
{&cdb_hr::LayoutLiteral::Utf8, &cdb_hr::LayoutLiteral::Int32}
};
TestSerializer<TS1>(typeArgs1, {"abc"s, 123}, {"xyz"s, 123}, {"abc"s, 456});
using TS2 = cdb_hr::TypedTupleHybridRowSerializer<
cdb_hr::Int64HybridRowSerializer,
cdb_hr::Int32HybridRowSerializer,
cdb_hr::DateTimeHybridRowSerializer>;
cdb_hr::TypeArgumentList typeArgs2{
{
&cdb_hr::LayoutLiteral::Int64,
&cdb_hr::LayoutLiteral::Int32,
&cdb_hr::LayoutLiteral::DateTime
}
};
TestSerializer<TS2>(typeArgs2, {789, 123, {456}}, {654, 123, {456}}, {789, 123, {789}});
}
TEST_METHOD_WITH_OWNER(NullableSerializerTest, "jthunter")
{
using TS1 = cdb_hr::NullableHybridRowSerializer<
std::optional<int32_t>, int32_t, cdb_hr::Int32HybridRowSerializer>;
cdb_hr::TypeArgumentList typeArgs1{
{cdb_hr::TypeArgument{&cdb_hr::LayoutLiteral::Int32}}
};
TestSerializer<TS1>(typeArgs1, 123, 456, std::nullopt);
}
TEST_METHOD_WITH_OWNER(MapSerializerTest, "jthunter")
{
using TS1 = cdb_hr::TypedMapHybridRowSerializer<
cdb_hr::Utf8HybridRowSerializer,
cdb_hr::Int32HybridRowSerializer>;
cdb_hr::TypeArgumentList typeArgs1{
{&cdb_hr::LayoutLiteral::Utf8, &cdb_hr::LayoutLiteral::Int32}
};
TestSerializer<TS1>(typeArgs1, {{"abc"s, 123}}, {{"xyz"s, 123}}, {{"abc"s, 456}});
}
private:
template<typename TS>
static void TestSerializer(
const cdb_hr::TypeArgumentList& typeArgs,
typename TS::const_reference t1,
typename TS::const_reference t2,
typename TS::const_reference t3)
{
const cdb_hr::LayoutResolver& resolver = cdb_hr::SchemasHrSchema::GetLayoutResolver();
const cdb_hr::Layout& layout = resolver.Resolve(cdb_hr::SystemSchemaLiteral::EmptySchemaId);
cdb_hr::MemorySpanResizer<byte> resizer{0};
cdb_hr::RowBuffer row{0, &resizer};
row.InitLayout(cdb_hr::HybridRowVersion::V1, layout, &resolver);
cdb_hr::RowCursor root = cdb_hr::RowCursor::Create(row);
ResultAssert::IsSuccess(TS::Write(row, root.Clone().Find(row, "a"), false, typeArgs, t1));
auto [r2, v1] = TS::Read(row, root.Clone().Find(row, "a"), false);
ResultAssert::IsSuccess(r2);
using comparer_type = typename TS::comparer_type;
Assert::IsTrue(comparer_type{}.operator()(t1, v1));
Assert::AreEqual(comparer_type{}.operator()(t1), comparer_type{}.operator()(v1));
Assert::AreNotEqual(comparer_type{}.operator()(t1), size_t{0});
Assert::IsFalse(comparer_type{}.operator()(t1, t2));
Assert::AreNotEqual(comparer_type{}.operator()(t1), comparer_type{}.operator()(t2));
Assert::IsFalse(comparer_type{}.operator()(t1, t3));
Assert::AreNotEqual(comparer_type{}.operator()(t1), comparer_type{}.operator()(t3));
}
};
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,104 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------
#include "pch.h"
#include "CppUnitTest.h"
#include "CppUnitTestFramework.inl"
namespace cdb_hr_test
{
using namespace std::literals;
using Assert = Microsoft::VisualStudio::CppUnitTestFramework::Assert;
TEST_CLASS(LayoutLiteralUnitTests)
{
public:
template<typename T, cdb_hr::LayoutCode code, typename = std::enable_if_t<std::is_base_of_v<cdb_hr::LayoutType, T>>>
struct LayoutTypeCheck
{
static void Check(const cdb_hr::LayoutType& literal) noexcept
{
const cdb_hr::LayoutType* q = static_cast<const cdb_hr::LayoutType*>(&literal);
Assert::AreEqual(q, cdb_hr::LayoutLiteral::FromCode<code>());
Assert::AreEqual(code, literal.GetLayoutCode());
const T& r = q->TypeAs<T>();
Assert::AreEqual(q, static_cast<const cdb_hr::LayoutType*>(&r));
}
};
TEST_METHOD_WITH_OWNER(TypeAsTest, "jthunter")
{
LayoutTypeCheck<cdb_hr::LayoutInt8, cdb_hr::LayoutCode::Int8>::Check(cdb_hr::LayoutLiteral::Int8);
LayoutTypeCheck<cdb_hr::LayoutInt8, cdb_hr::LayoutCode::Int8>::Check(cdb_hr::LayoutLiteral::Int8);
LayoutTypeCheck<cdb_hr::LayoutInt16, cdb_hr::LayoutCode::Int16>::Check(cdb_hr::LayoutLiteral::Int16);
LayoutTypeCheck<cdb_hr::LayoutInt32, cdb_hr::LayoutCode::Int32>::Check(cdb_hr::LayoutLiteral::Int32);
LayoutTypeCheck<cdb_hr::LayoutInt64, cdb_hr::LayoutCode::Int64>::Check(cdb_hr::LayoutLiteral::Int64);
LayoutTypeCheck<cdb_hr::LayoutUInt8, cdb_hr::LayoutCode::UInt8>::Check(cdb_hr::LayoutLiteral::UInt8);
LayoutTypeCheck<cdb_hr::LayoutUInt16, cdb_hr::LayoutCode::UInt16>::Check(cdb_hr::LayoutLiteral::UInt16);
LayoutTypeCheck<cdb_hr::LayoutUInt32, cdb_hr::LayoutCode::UInt32>::Check(cdb_hr::LayoutLiteral::UInt32);
LayoutTypeCheck<cdb_hr::LayoutUInt64, cdb_hr::LayoutCode::UInt64>::Check(cdb_hr::LayoutLiteral::UInt64);
LayoutTypeCheck<cdb_hr::LayoutVarInt, cdb_hr::LayoutCode::VarInt>::Check(cdb_hr::LayoutLiteral::VarInt);
LayoutTypeCheck<cdb_hr::LayoutVarUInt, cdb_hr::LayoutCode::VarUInt>::Check(cdb_hr::LayoutLiteral::VarUInt);
LayoutTypeCheck<cdb_hr::LayoutFloat32, cdb_hr::LayoutCode::Float32>::Check(cdb_hr::LayoutLiteral::Float32);
LayoutTypeCheck<cdb_hr::LayoutFloat64, cdb_hr::LayoutCode::Float64>::Check(cdb_hr::LayoutLiteral::Float64);
LayoutTypeCheck<cdb_hr::LayoutFloat128, cdb_hr::LayoutCode::Float128>::Check(cdb_hr::LayoutLiteral::Float128);
LayoutTypeCheck<cdb_hr::LayoutDecimal, cdb_hr::LayoutCode::Decimal>::Check(cdb_hr::LayoutLiteral::Decimal);
LayoutTypeCheck<cdb_hr::LayoutDateTime, cdb_hr::LayoutCode::DateTime>::Check(cdb_hr::LayoutLiteral::DateTime);
LayoutTypeCheck<cdb_hr::LayoutUnixDateTime, cdb_hr::LayoutCode::UnixDateTime>::Check(
cdb_hr::LayoutLiteral::UnixDateTime);
LayoutTypeCheck<cdb_hr::LayoutGuid, cdb_hr::LayoutCode::Guid>::Check(cdb_hr::LayoutLiteral::Guid);
LayoutTypeCheck<cdb_hr::LayoutMongoDbObjectId, cdb_hr::LayoutCode::MongoDbObjectId>::Check(
cdb_hr::LayoutLiteral::MongoDbObjectId);
LayoutTypeCheck<cdb_hr::LayoutNull, cdb_hr::LayoutCode::Null>::Check(cdb_hr::LayoutLiteral::Null);
LayoutTypeCheck<cdb_hr::LayoutBoolean, cdb_hr::LayoutCode::Boolean>::Check(cdb_hr::LayoutLiteral::Boolean);
LayoutTypeCheck<cdb_hr::LayoutBoolean, cdb_hr::LayoutCode::BooleanFalse>::Check(
cdb_hr::LayoutLiteral::BooleanFalse);
LayoutTypeCheck<cdb_hr::LayoutUtf8, cdb_hr::LayoutCode::Utf8>::Check(cdb_hr::LayoutLiteral::Utf8);
LayoutTypeCheck<cdb_hr::LayoutBinary, cdb_hr::LayoutCode::Binary>::Check(cdb_hr::LayoutLiteral::Binary);
LayoutTypeCheck<cdb_hr::LayoutObject, cdb_hr::LayoutCode::ObjectScope>::Check(cdb_hr::LayoutLiteral::Object);
LayoutTypeCheck<cdb_hr::LayoutObject, cdb_hr::LayoutCode::ImmutableObjectScope>::Check(
cdb_hr::LayoutLiteral::ImmutableObject);
LayoutTypeCheck<cdb_hr::LayoutArray, cdb_hr::LayoutCode::ArrayScope>::Check(cdb_hr::LayoutLiteral::Array);
LayoutTypeCheck<cdb_hr::LayoutArray, cdb_hr::LayoutCode::ImmutableArrayScope>::Check(
cdb_hr::LayoutLiteral::ImmutableArray);
LayoutTypeCheck<cdb_hr::LayoutTypedArray, cdb_hr::LayoutCode::TypedArrayScope>::Check(
cdb_hr::LayoutLiteral::TypedArray);
LayoutTypeCheck<cdb_hr::LayoutTypedArray, cdb_hr::LayoutCode::ImmutableTypedArrayScope>
::Check(cdb_hr::LayoutLiteral::ImmutableTypedArray);
LayoutTypeCheck<cdb_hr::LayoutTypedSet, cdb_hr::LayoutCode::TypedSetScope
>::Check(cdb_hr::LayoutLiteral::TypedSet);
LayoutTypeCheck<cdb_hr::LayoutTypedSet, cdb_hr::LayoutCode::ImmutableTypedSetScope>::Check(
cdb_hr::LayoutLiteral::ImmutableTypedSet);
LayoutTypeCheck<cdb_hr::LayoutTypedMap, cdb_hr::LayoutCode::TypedMapScope
>::Check(cdb_hr::LayoutLiteral::TypedMap);
LayoutTypeCheck<cdb_hr::LayoutTypedMap, cdb_hr::LayoutCode::ImmutableTypedMapScope>::Check(
cdb_hr::LayoutLiteral::ImmutableTypedMap);
LayoutTypeCheck<cdb_hr::LayoutTuple, cdb_hr::LayoutCode::TupleScope>::Check(cdb_hr::LayoutLiteral::Tuple);
LayoutTypeCheck<cdb_hr::LayoutTuple, cdb_hr::LayoutCode::ImmutableTupleScope>::Check(
cdb_hr::LayoutLiteral::ImmutableTuple);
LayoutTypeCheck<cdb_hr::LayoutTypedTuple, cdb_hr::LayoutCode::TypedTupleScope>::Check(
cdb_hr::LayoutLiteral::TypedTuple);
LayoutTypeCheck<cdb_hr::LayoutTypedTuple, cdb_hr::LayoutCode::ImmutableTypedTupleScope>
::Check(cdb_hr::LayoutLiteral::ImmutableTypedTuple);
LayoutTypeCheck<cdb_hr::LayoutTagged, cdb_hr::LayoutCode::TaggedScope>::Check(cdb_hr::LayoutLiteral::Tagged);
LayoutTypeCheck<cdb_hr::LayoutTagged, cdb_hr::LayoutCode::ImmutableTaggedScope>::Check(
cdb_hr::LayoutLiteral::ImmutableTagged);
LayoutTypeCheck<cdb_hr::LayoutTagged2, cdb_hr::LayoutCode::Tagged2Scope>::Check(cdb_hr::LayoutLiteral::Tagged2);
LayoutTypeCheck<cdb_hr::LayoutTagged2, cdb_hr::LayoutCode::ImmutableTagged2Scope>::Check(
cdb_hr::LayoutLiteral::ImmutableTagged2);
LayoutTypeCheck<cdb_hr::LayoutNullable, cdb_hr::LayoutCode::NullableScope
>::Check(cdb_hr::LayoutLiteral::Nullable);
LayoutTypeCheck<cdb_hr::LayoutNullable, cdb_hr::LayoutCode::ImmutableNullableScope>::Check(
cdb_hr::LayoutLiteral::ImmutableNullable);
LayoutTypeCheck<cdb_hr::LayoutUDT, cdb_hr::LayoutCode::Schema>::Check(cdb_hr::LayoutLiteral::UDT);
LayoutTypeCheck<cdb_hr::LayoutUDT, cdb_hr::LayoutCode::ImmutableSchema>::Check(
cdb_hr::LayoutLiteral::ImmutableUDT);
LayoutTypeCheck<cdb_hr::LayoutEndScope, cdb_hr::LayoutCode::EndScope>::Check(cdb_hr::LayoutLiteral::EndScope);
}
};
}

View File

@@ -0,0 +1,74 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------
#pragma once
#include "CppUnitTest.h"
namespace cdb_hr_test
{
using Assert = Microsoft::VisualStudio::CppUnitTestFramework::Assert;
struct ResultAssert final
{
static void IsSuccess(cdb_hr::Result actual)
{
Assert::AreEqual(cdb_hr::Result::Success, actual);
}
static void IsSuccess(cdb_hr::Result actual, std::wstring_view message)
{
Assert::AreEqual(cdb_hr::Result::Success, actual, message.data());
}
static void NotFound(cdb_hr::Result actual)
{
Assert::AreEqual(cdb_hr::Result::NotFound, actual);
}
static void NotFound(cdb_hr::Result actual, std::wstring_view message)
{
Assert::AreEqual(cdb_hr::Result::NotFound, actual, message.data());
}
static void Exists(cdb_hr::Result actual)
{
Assert::AreEqual(cdb_hr::Result::Exists, actual);
}
static void Exists(cdb_hr::Result actual, std::wstring_view message)
{
Assert::AreEqual(cdb_hr::Result::Exists, actual, message.data());
}
static void TypeMismatch(cdb_hr::Result actual)
{
Assert::AreEqual(cdb_hr::Result::TypeMismatch, actual);
}
static void TypeMismatch(cdb_hr::Result actual, std::wstring_view message)
{
Assert::AreEqual(cdb_hr::Result::TypeMismatch, actual, message.data());
}
static void InsufficientPermissions(cdb_hr::Result actual)
{
Assert::AreEqual(cdb_hr::Result::InsufficientPermissions, actual);
}
static void InsufficientPermissions(cdb_hr::Result actual, std::wstring_view message)
{
Assert::AreEqual(cdb_hr::Result::InsufficientPermissions, actual, message.data());
}
static void TypeConstraint(cdb_hr::Result actual)
{
Assert::AreEqual(cdb_hr::Result::TypeConstraint, actual);
}
static void TypeConstraint(cdb_hr::Result actual, std::wstring_view message)
{
Assert::AreEqual(cdb_hr::Result::TypeConstraint, actual, message.data());
}
};
}

View File

@@ -0,0 +1,43 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------
#include "pch.h"
#include "CppUnitTest.h"
#include "CppUnitTestFramework.inl"
namespace cdb_hr_test
{
using namespace std::literals;
using Assert = Microsoft::VisualStudio::CppUnitTestFramework::Assert;
TEST_CLASS(StringTokenizerUnitTests)
{
public:
TEST_METHOD_WITH_OWNER(RoundTripTest, "jthunter")
{
cdb_hr::StringTokenizer tokenizer{};
const auto& fooToken = tokenizer.Add("foo"sv);
Assert::IsFalse(fooToken.IsNull());
Assert::AreEqual("foo"sv, fooToken.GetPath());
Assert::AreNotEqual(static_cast<uint32_t>(0), fooToken.GetVarint().Length());
const auto& [fooSuccess, fooString] = tokenizer.TryFindString(fooToken.GetId());
Assert::IsTrue(fooSuccess);
Assert::AreEqual("foo"sv, fooString);
const auto& barToken = tokenizer.Add("bar"sv);
Assert::IsFalse(barToken.IsNull());
Assert::AreEqual("bar"sv, barToken.GetPath());
Assert::AreNotEqual(static_cast<uint32_t>(0), barToken.GetVarint().Length());
const auto& [barSuccess, barString] = tokenizer.TryFindString(barToken.GetId());
Assert::IsTrue(barSuccess);
Assert::AreEqual("bar"sv, barString);
Assert::AreNotEqual(barToken.GetId(), fooToken.GetId());
Assert::AreNotEqual(barToken.GetPath(), fooToken.GetPath());
Assert::AreNotEqual(barToken.GetVarint(), fooToken.GetVarint());
}
};
}

View File

@@ -0,0 +1,345 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------
#include "pch.h"
#include "CppUnitTest.h"
#include "CppUnitTestFramework.inl"
#include "ResultAssert.h"
// ReSharper disable CppClangTidyCppcoreguidelinesProTypeStaticCastDowncast
namespace cdb_hr_test
{
using namespace std::literals;
using Assert = Microsoft::VisualStudio::CppUnitTestFramework::Assert;
TEST_CLASS(SystemSchemaUnitTests)
{
public:
TEST_METHOD_WITH_OWNER(LoadGeneratedHrSchema, "jthunter")
{
const cdb_hr::Layout& layout = cdb_hr::SchemasHrSchema::GetLayoutResolver().Resolve(cdb_hr::SchemaOptionsHybridRowSerializer::Id);
Assert::AreEqual(cdb_hr::SchemaOptionsHybridRowSerializer::Id, layout.GetSchemaId());
}
TEST_METHOD_WITH_OWNER(LoadTest, "jthunter")
{
const cdb_hr::LayoutResolver& resolver = cdb_hr::SchemasHrSchema::GetLayoutResolver();
std::array systemSchemas{
cdb_hr::SystemSchemaLiteral::EmptySchemaId,
cdb_hr::SegmentHybridRowSerializer::Id,
cdb_hr::RecordHybridRowSerializer::Id,
cdb_hr::NamespaceHybridRowSerializer::Id,
cdb_hr::SchemaHybridRowSerializer::Id,
cdb_hr::SchemaOptionsHybridRowSerializer::Id,
cdb_hr::PartitionKeyHybridRowSerializer::Id,
cdb_hr::PrimarySortKeyHybridRowSerializer::Id,
cdb_hr::StaticKeyHybridRowSerializer::Id,
cdb_hr::PropertyHybridRowSerializer::Id,
cdb_hr::PropertyTypeHybridRowSerializer::Id,
cdb_hr::PrimitivePropertyTypeHybridRowSerializer::Id,
cdb_hr::ScopePropertyTypeHybridRowSerializer::Id,
cdb_hr::ArrayPropertyTypeHybridRowSerializer::Id,
cdb_hr::ObjectPropertyTypeHybridRowSerializer::Id,
cdb_hr::SetPropertyTypeHybridRowSerializer::Id,
cdb_hr::MapPropertyTypeHybridRowSerializer::Id,
cdb_hr::TuplePropertyTypeHybridRowSerializer::Id,
cdb_hr::TaggedPropertyTypeHybridRowSerializer::Id,
};
// Make sure all system schemas are loadable.
for (cdb_hr::SchemaId id : systemSchemas)
{
const cdb_hr::Layout& l = resolver.Resolve(id);
Assert::AreEqual(id, l.GetSchemaId());
}
// Make sure all system schema ids are unique.
for (cdb_hr::SchemaId id : systemSchemas)
{
int count = 0;
for (cdb_hr::SchemaId other : systemSchemas)
{
if (other == id)
{
count++;
}
}
Assert::AreEqual(1, count);
}
}
static void AssertEqual(const cdb_hr::PartitionKey& i1, const cdb_hr::PartitionKey& i2)
{
Assert::AreEqual(i1.GetPath(), i2.GetPath());
}
static void AssertEqual(const cdb_hr::PrimarySortKey& i1, const cdb_hr::PrimarySortKey& i2)
{
Assert::AreEqual(i1.GetPath(), i2.GetPath());
Assert::AreEqual(i1.GetDirection(), i2.GetDirection());
}
static void AssertEqual(const cdb_hr::StaticKey& i1, const cdb_hr::StaticKey& i2)
{
Assert::AreEqual(i1.GetPath(), i2.GetPath());
}
static void AssertEqual(const cdb_hr::Property& i1, const cdb_hr::Property& i2)
{
Assert::AreEqual(i1.GetComment(), i2.GetComment());
Assert::AreEqual(i1.GetPath(), i2.GetPath());
Assert::AreEqual(i1.GetApiName(), i2.GetApiName());
Assert::AreEqual(i1.GetAllowEmpty(), i2.GetAllowEmpty());
if (i1.GetPropertyType().has_value())
{
Assert::IsTrue(i2.GetPropertyType().has_value());
AssertEqual(i1.GetPropertyType().value(), i2.GetPropertyType().value());
}
else
{
Assert::IsFalse(i2.GetPropertyType().has_value());
}
}
static void AssertEqual(const cdb_hr::PropertyType& i1, const cdb_hr::PropertyType& i2)
{
Assert::AreEqual(i1.GetApiType(), i2.GetApiType());
Assert::AreEqual(i1.GetType(), i2.GetType());
Assert::AreEqual(i1.GetNullable(), i2.GetNullable());
Assert::AreEqual(i1.GetRuntimeSchemaId(), i2.GetRuntimeSchemaId());
switch (i1.GetRuntimeSchemaId().Id())
{
case cdb_hr::PrimitivePropertyTypeHybridRowSerializer::Id.Id():
{
AssertEqual(static_cast<const cdb_hr::PrimitivePropertyType&>(i1), static_cast<const cdb_hr::PrimitivePropertyType&>(i2));
return;
}
case cdb_hr::ArrayPropertyTypeHybridRowSerializer::Id.Id():
case cdb_hr::ObjectPropertyTypeHybridRowSerializer::Id.Id():
case cdb_hr::UdtPropertyTypeHybridRowSerializer::Id.Id():
case cdb_hr::SetPropertyTypeHybridRowSerializer::Id.Id():
case cdb_hr::MapPropertyTypeHybridRowSerializer::Id.Id():
case cdb_hr::TuplePropertyTypeHybridRowSerializer::Id.Id():
case cdb_hr::TaggedPropertyTypeHybridRowSerializer::Id.Id():
case cdb_hr::ScopePropertyTypeHybridRowSerializer::Id.Id():
{
AssertEqual(static_cast<const cdb_hr::ScopePropertyType&>(i1), static_cast<const cdb_hr::ScopePropertyType&>(i2));
return;
}
default:
Assert::Fail(L"Type is abstract.");
}
}
static void AssertEqual(const cdb_hr::PrimitivePropertyType& i1, const cdb_hr::PrimitivePropertyType& i2)
{
Assert::AreEqual(i1.GetLength(), i2.GetLength());
Assert::AreEqual(i1.GetStorage(), i2.GetStorage());
}
static void AssertEqual(const cdb_hr::ScopePropertyType& i1, const cdb_hr::ScopePropertyType& i2)
{
Assert::AreEqual(i1.GetImmutable(), i2.GetImmutable());
switch (i1.GetRuntimeSchemaId().Id())
{
case cdb_hr::ArrayPropertyTypeHybridRowSerializer::Id.Id():
{
const cdb_hr::ArrayPropertyType& p = static_cast<const cdb_hr::ArrayPropertyType&>(i1);
const cdb_hr::ArrayPropertyType& q = static_cast<const cdb_hr::ArrayPropertyType&>(i2);
Assert::AreEqual(p.GetItems().has_value(), q.GetItems().has_value());
AssertEqual(p.GetItems().value(), q.GetItems().value());
break;
}
case cdb_hr::ObjectPropertyTypeHybridRowSerializer::Id.Id():
{
const cdb_hr::ObjectPropertyType& p = static_cast<const cdb_hr::ObjectPropertyType&>(i1);
const cdb_hr::ObjectPropertyType& q = static_cast<const cdb_hr::ObjectPropertyType&>(i2);
Assert::AreEqual(p.GetProperties().size(), q.GetProperties().size());
for (size_t i = 0; i < p.GetProperties().size(); i++)
{
AssertEqual(*p.GetProperties()[i], *q.GetProperties()[i]);
}
break;
}
case cdb_hr::SetPropertyTypeHybridRowSerializer::Id.Id():
{
const cdb_hr::SetPropertyType& p = static_cast<const cdb_hr::SetPropertyType&>(i1);
const cdb_hr::SetPropertyType& q = static_cast<const cdb_hr::SetPropertyType&>(i2);
Assert::AreEqual(p.GetItems().has_value(), q.GetItems().has_value());
AssertEqual(p.GetItems().value(), q.GetItems().value());
break;
}
case cdb_hr::MapPropertyTypeHybridRowSerializer::Id.Id():
{
const cdb_hr::MapPropertyType& p = static_cast<const cdb_hr::MapPropertyType&>(i1);
const cdb_hr::MapPropertyType& q = static_cast<const cdb_hr::MapPropertyType&>(i2);
Assert::AreEqual(p.GetKeys().has_value(), q.GetKeys().has_value());
Assert::AreEqual(p.GetValues().has_value(), q.GetValues().has_value());
AssertEqual(p.GetKeys().value(), q.GetKeys().value());
AssertEqual(p.GetValues().value(), q.GetValues().value());
break;
}
case cdb_hr::TuplePropertyTypeHybridRowSerializer::Id.Id():
{
const cdb_hr::TuplePropertyType& p = static_cast<const cdb_hr::TuplePropertyType&>(i1);
const cdb_hr::TuplePropertyType& q = static_cast<const cdb_hr::TuplePropertyType&>(i2);
Assert::AreEqual(p.GetItems().size(), q.GetItems().size());
for (size_t i = 0; i < p.GetItems().size(); i++)
{
AssertEqual(*p.GetItems()[i], *q.GetItems()[i]);
}
break;
}
case cdb_hr::TaggedPropertyTypeHybridRowSerializer::Id.Id():
{
const cdb_hr::TaggedPropertyType& p = static_cast<const cdb_hr::TaggedPropertyType&>(i1);
const cdb_hr::TaggedPropertyType& q = static_cast<const cdb_hr::TaggedPropertyType&>(i2);
Assert::AreEqual(p.GetItems().size(), q.GetItems().size());
for (size_t i = 0; i < p.GetItems().size(); i++)
{
AssertEqual(*p.GetItems()[i], *q.GetItems()[i]);
}
break;
}
case cdb_hr::UdtPropertyTypeHybridRowSerializer::Id.Id():
{
const cdb_hr::UdtPropertyType& p = static_cast<const cdb_hr::UdtPropertyType&>(i1);
const cdb_hr::UdtPropertyType& q = static_cast<const cdb_hr::UdtPropertyType&>(i2);
Assert::AreEqual(p.GetName(), q.GetName());
Assert::AreEqual(p.GetSchemaId(), q.GetSchemaId());
break;
}
default:
Assert::Fail(L"Type is abstract.");
}
}
static void AssertEqual(const cdb_hr::EnumSchema& s1, const cdb_hr::EnumSchema& s2)
{
Assert::AreEqual(s1.GetType(), s2.GetType());
Assert::AreEqual(s1.GetApiType(), s2.GetApiType());
Assert::AreEqual(s1.GetName(), s2.GetName());
Assert::AreEqual(s1.GetComment(), s2.GetComment());
Assert::AreEqual(s1.GetValues().size(), s2.GetValues().size());
for (size_t i = 0; i < s1.GetValues().size(); i++)
{
AssertEqual(*s1.GetValues()[i], *s2.GetValues()[i]);
}
}
static void AssertEqual(const cdb_hr::EnumValue& s1, const cdb_hr::EnumValue& s2)
{
Assert::AreEqual(s1.GetValue(), s2.GetValue());
Assert::AreEqual(s1.GetName(), s2.GetName());
Assert::AreEqual(s1.GetComment(), s2.GetComment());
}
static void AssertEqual(const cdb_hr::SchemaOptions& s1, const cdb_hr::SchemaOptions& s2)
{
Assert::AreEqual(s1.GetDisallowUnschematized(), s2.GetDisallowUnschematized());
Assert::AreEqual(s1.GetEnablePropertyLevelTimestamp(), s2.GetEnablePropertyLevelTimestamp());
Assert::AreEqual(s1.GetDisableSystemPrefix(), s2.GetDisableSystemPrefix());
Assert::AreEqual(s1.GetAbstract(), s2.GetAbstract());
}
static void AssertEqual(const cdb_hr::Schema& s1, const cdb_hr::Schema& s2)
{
Assert::AreEqual(s1.GetVersion(), s2.GetVersion());
Assert::AreEqual(s1.GetType(), s2.GetType());
Assert::AreEqual(s1.GetSchemaId(), s2.GetSchemaId());
Assert::AreEqual(s1.GetName(), s2.GetName());
Assert::AreEqual(s1.GetComment(), s2.GetComment());
if (!s1.GetOptions().has_value())
{
Assert::IsFalse(s2.GetOptions().has_value());
}
else
{
Assert::IsTrue(s2.GetOptions().has_value());
AssertEqual(s1.GetOptions().value(), s2.GetOptions().value());
}
Assert::AreEqual(s1.GetPartitionKeys().size(), s2.GetPartitionKeys().size());
for (size_t i = 0; i < s1.GetPartitionKeys().size(); i++)
{
AssertEqual(*s1.GetPartitionKeys()[i], *s2.GetPartitionKeys()[i]);
}
Assert::AreEqual(s1.GetPrimaryKeys().size(), s2.GetPrimaryKeys().size());
for (size_t i = 0; i < s1.GetPrimaryKeys().size(); i++)
{
AssertEqual(*s1.GetPrimaryKeys()[i], *s2.GetPrimaryKeys()[i]);
}
Assert::AreEqual(s1.GetStaticKeys().size(), s2.GetStaticKeys().size());
for (size_t i = 0; i < s1.GetStaticKeys().size(); i++)
{
AssertEqual(*s1.GetStaticKeys()[i], *s2.GetStaticKeys()[i]);
}
Assert::AreEqual(s1.GetProperties().size(), s2.GetProperties().size());
for (size_t i = 0; i < s1.GetProperties().size(); i++)
{
AssertEqual(*s1.GetProperties()[i], *s2.GetProperties()[i]);
}
}
static void SerializerRoundtripNamespace(const cdb_hr::Namespace& ns1)
{
const cdb_hr::LayoutResolver& resolver = cdb_hr::SystemSchemaLiteral::GetLayoutResolver();
const cdb_hr::Layout& layout = resolver.Resolve(cdb_hr::NamespaceHybridRowSerializer::Id);
cdb_hr::MemorySpanResizer<byte> resizer{0};
cdb_hr::RowBuffer row{0, &resizer};
row.InitLayout(cdb_hr::HybridRowVersion::V1, layout, &resolver);
// Write the whole namespace to a row.
cdb_hr::Result r = ns1.Write(row);
ResultAssert::IsSuccess(r);
// Read the namespace back.
std::unique_ptr<cdb_hr::Namespace> pns2;
std::tie(r, pns2) = cdb_hr::Namespace::Read(row);
ResultAssert::IsSuccess(r);
const cdb_hr::Namespace& ns2 = *pns2;
// Compare the materialized row with the original in-memory object model.
Assert::AreEqual(ns1.GetVersion(), ns2.GetVersion());
Assert::AreEqual(ns1.GetName(), ns2.GetName());
Assert::AreEqual(ns1.GetComment(), ns2.GetComment());
Assert::AreEqual(ns1.GetSchemas().size(), ns2.GetSchemas().size());
for (size_t i = 0; i < ns1.GetSchemas().size(); i++)
{
const cdb_hr::Schema& s1 = *ns1.GetSchemas()[i];
const cdb_hr::Schema& s2 = *ns2.GetSchemas()[i];
AssertEqual(s1, s2);
}
Assert::AreEqual(ns1.GetEnums().size(), ns2.GetEnums().size());
for (size_t i = 0; i < ns1.GetEnums().size(); i++)
{
const cdb_hr::EnumSchema& s1 = *ns1.GetEnums()[i];
const cdb_hr::EnumSchema& s2 = *ns2.GetEnums()[i];
AssertEqual(s1, s2);
}
}
TEST_METHOD_WITH_OWNER(SerializeSystemNamespaceTest, "jthunter")
{
const cdb_hr::Namespace& ns1 = cdb_hr::SystemSchemaLiteral::GetNamespace();
SystemSchemaUnitTests::SerializerRoundtripNamespace(ns1);
}
};
}

View File

@@ -0,0 +1,546 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------
#include "pch.h"
#include "TaggedSchema.generated.h"
// ------------------------------------------------------------
// This file was generated by:
// Microsoft.Azure.Cosmos.Serialization.HybridRowCLI: 1.0.0.0
//
// This file should not be modified directly.
// ------------------------------------------------------------
// ReSharper disable CppClangTidyCppcoreguidelinesProTypeStaticCastDowncast
// ReSharper disable CppClangTidyPerformanceMoveConstArg
// ReSharper disable CppRedundantControlFlowJump
// ReSharper disable CppClangTidyClangDiagnosticExitTimeDestructors
namespace cdb_hr_test::typed_array
{
using namespace std::literals;
class TypedArrayHrSchema::Literal final
{
friend struct TypedArrayHrSchema;
static const cdb_hr::Namespace& GetNamespace() noexcept
{
return *s_namespace;
}
static std::unique_ptr<cdb_hr::LayoutResolver> LoadSchema()
{
auto ns = cdb_core::make_unique_with([&](cdb_hr::Namespace& n)
{
n.SetName("Microsoft.Azure.Cosmos.Serialization.HybridRow.Tests.Unit.TypedArray");
n.SetVersion(cdb_hr::SchemaLanguageVersion::V2);
n.SetCppNamespace("cdb_hr_test::typed_array");
//////////////////////////////////////////////////////////////////////////////
n.GetSchemas().emplace_back(cdb_core::make_unique_with([](cdb_hr::Schema& s)
{
s.SetName("Tagged");
s.SetSchemaId(cdb_hr::SchemaId{1});
s.GetProperties().emplace_back(cdb_core::make_unique_with([](cdb_hr::Property& p)
{
p.SetPath("title");
p.SetPropertyType(cdb_core::make_unique_with([](cdb_hr::PrimitivePropertyType& pt)
{
pt.SetType(cdb_hr::TypeKind::Utf8);
pt.SetStorage(cdb_hr::StorageKind::Variable);
}));
}));
s.GetProperties().emplace_back(cdb_core::make_unique_with([](cdb_hr::Property& p)
{
p.SetPath("tags");
p.SetPropertyType(
std::make_unique<cdb_hr::ArrayPropertyType>(cdb_core::make_unique_with([](cdb_hr::PrimitivePropertyType& pt)
{
pt.SetType(cdb_hr::TypeKind::Utf8);
pt.SetNullable(false);
})));
}));
s.GetProperties().emplace_back(cdb_core::make_unique_with([](cdb_hr::Property& p)
{
p.SetPath("options");
p.SetPropertyType(
std::make_unique<cdb_hr::ArrayPropertyType>(cdb_core::make_unique_with([](cdb_hr::PrimitivePropertyType& pt)
{
pt.SetType(cdb_hr::TypeKind::Int32);
})));
}));
s.GetProperties().emplace_back(cdb_core::make_unique_with([](cdb_hr::Property& p)
{
p.SetPath("ratings");
p.SetPropertyType(
std::make_unique<cdb_hr::ArrayPropertyType>(
std::make_unique<cdb_hr::ArrayPropertyType>(cdb_core::make_unique_with([](cdb_hr::PrimitivePropertyType& pt)
{
pt.SetType(cdb_hr::TypeKind::Float64);
pt.SetNullable(false);
}), false)));
}));
s.GetProperties().emplace_back(cdb_core::make_unique_with([](cdb_hr::Property& p)
{
p.SetPath("similars");
p.SetPropertyType(
std::make_unique<cdb_hr::ArrayPropertyType>(
std::make_unique<cdb_hr::UdtPropertyType>("SimilarMatch", cdb_hr::SchemaId{0}, false)));
}));
s.GetProperties().emplace_back(cdb_core::make_unique_with([](cdb_hr::Property& p)
{
p.SetPath("priority");
p.SetPropertyType(
std::make_unique<cdb_hr::ArrayPropertyType>(
std::make_unique<cdb_hr::TuplePropertyType>(
cdb_hr::IHybridRowSerializer::make_unique_vector<cdb_hr::PropertyType>(
cdb_core::make_unique_with([](cdb_hr::PrimitivePropertyType& pt)
{
pt.SetType(cdb_hr::TypeKind::Utf8);
pt.SetNullable(false);
}),
cdb_core::make_unique_with([](cdb_hr::PrimitivePropertyType& pt)
{
pt.SetType(cdb_hr::TypeKind::Int64);
pt.SetNullable(false);
})
), false)));
}));
}));
//////////////////////////////////////////////////////////////////////////////
n.GetSchemas().emplace_back(cdb_core::make_unique_with([](cdb_hr::Schema& s)
{
s.SetName("SimilarMatch");
s.SetSchemaId(cdb_hr::SchemaId{2});
s.GetProperties().emplace_back(cdb_core::make_unique_with([](cdb_hr::Property& p)
{
p.SetPath("thumbprint");
p.SetPropertyType(cdb_core::make_unique_with([](cdb_hr::PrimitivePropertyType& pt)
{
pt.SetType(cdb_hr::TypeKind::Utf8);
pt.SetLength(18);
pt.SetStorage(cdb_hr::StorageKind::Fixed);
}));
}));
s.GetProperties().emplace_back(cdb_core::make_unique_with([](cdb_hr::Property& p)
{
p.SetPath("score");
p.SetPropertyType(cdb_core::make_unique_with([](cdb_hr::PrimitivePropertyType& pt)
{
pt.SetType(cdb_hr::TypeKind::Float64);
pt.SetStorage(cdb_hr::StorageKind::Fixed);
}));
}));
}));
});
s_namespace = ns.get();
return std::make_unique<cdb_hr::LayoutResolverNamespace>(std::move(ns));
}
inline static cdb_hr::Namespace* s_namespace{nullptr};
inline static std::unique_ptr<cdb_hr::LayoutResolver> s_layoutResolver{LoadSchema()};
};
const cdb_hr::Namespace& TypedArrayHrSchema::GetNamespace() noexcept
{
return *Literal::s_namespace;
}
const cdb_hr::LayoutResolver& TypedArrayHrSchema::GetLayoutResolver() noexcept
{
return *Literal::s_layoutResolver;
}
class TaggedHybridRowSerializer::Literal final
{
friend struct TaggedHybridRowSerializer;
constexpr static std::string_view TitleName{"title"sv};
constexpr static std::string_view TagsName{"tags"sv};
constexpr static std::string_view OptionsName{"options"sv};
constexpr static std::string_view RatingsName{"ratings"sv};
constexpr static std::string_view SimilarsName{"similars"sv};
constexpr static std::string_view PriorityName{"priority"sv};
inline static const cdb_hr::Layout& Layout{TypedArrayHrSchema::GetLayoutResolver().Resolve(Id)};
inline static const cdb_hr::LayoutColumn& TitleColumn{cdb_hr::IHybridRowSerializer::InitLayoutColumn(Layout, TitleName)};
inline static const cdb_hr::LayoutColumn& TagsColumn{cdb_hr::IHybridRowSerializer::InitLayoutColumn(Layout, TagsName)};
inline static const cdb_hr::LayoutColumn& OptionsColumn{cdb_hr::IHybridRowSerializer::InitLayoutColumn(Layout, OptionsName)};
inline static const cdb_hr::LayoutColumn& RatingsColumn{cdb_hr::IHybridRowSerializer::InitLayoutColumn(Layout, RatingsName)};
inline static const cdb_hr::LayoutColumn& SimilarsColumn{cdb_hr::IHybridRowSerializer::InitLayoutColumn(Layout, SimilarsName)};
inline static const cdb_hr::LayoutColumn& PriorityColumn{cdb_hr::IHybridRowSerializer::InitLayoutColumn(Layout, PriorityName)};
inline static const cdb_hr::StringTokenizer::StringToken& TagsToken{
cdb_hr::IHybridRowSerializer::InitStringToken(Layout, TagsColumn.GetPath())
};
inline static const cdb_hr::StringTokenizer::StringToken& OptionsToken{
cdb_hr::IHybridRowSerializer::InitStringToken(Layout, OptionsColumn.GetPath())
};
inline static const cdb_hr::StringTokenizer::StringToken& RatingsToken{
cdb_hr::IHybridRowSerializer::InitStringToken(Layout, RatingsColumn.GetPath())
};
inline static const cdb_hr::StringTokenizer::StringToken& SimilarsToken{
cdb_hr::IHybridRowSerializer::InitStringToken(Layout, SimilarsColumn.GetPath())
};
inline static const cdb_hr::StringTokenizer::StringToken& PriorityToken{
cdb_hr::IHybridRowSerializer::InitStringToken(Layout, PriorityColumn.GetPath())
};
static cdb_hr::Result Write(cdb_hr::RowBuffer& row, cdb_hr::RowCursor& scope, const Tagged& value) noexcept;
static cdb_hr::Result Read(const cdb_hr::RowBuffer& row, cdb_hr::RowCursor& scope, Tagged& value);
};
cdb_hr::Result TaggedHybridRowSerializer::Write(cdb_hr::RowBuffer& row, cdb_hr::RowCursor& scope, bool isRoot,
const cdb_hr::TypeArgumentList& typeArgs, const Tagged& value) noexcept
{
if (isRoot)
{
return TaggedHybridRowSerializer::Literal::Write(row, scope, value);
}
auto [r, childScope] = cdb_hr::LayoutLiteral::UDT.WriteScope(row, scope, Id);
if (r != cdb_hr::Result::Success)
{
return r;
}
r = TaggedHybridRowSerializer::Literal::Write(row, childScope, value);
if (r != cdb_hr::Result::Success)
{
return r;
}
scope.Skip(row, childScope);
return cdb_hr::Result::Success;
}
cdb_hr::Result TaggedHybridRowSerializer::Literal::Write(cdb_hr::RowBuffer& row, cdb_hr::RowCursor& scope, const Tagged& value) noexcept
{
if (!cdb_hr::IHybridRowSerializer::is_default(value.GetTitle()))
{
cdb_hr::Result r = cdb_hr::LayoutLiteral::Utf8.WriteVariable(
row, scope, TitleColumn, cdb_hr::IHybridRowSerializer::get(value.GetTitle()));
if (r != cdb_hr::Result::Success)
{
return r;
}
}
if (!cdb_hr::IHybridRowSerializer::is_default(value.GetTags()))
{
scope.Find(row, TagsColumn.GetPath());
cdb_hr::Result r = cdb_hr::TypedArrayHybridRowSerializer<std::string, cdb_hr::Utf8HybridRowSerializer>::Write(
row,
scope,
false,
TagsColumn.GetTypeArgs(),
cdb_hr::IHybridRowSerializer::get(value.GetTags()));
if (r != cdb_hr::Result::Success)
{
return r;
}
}
if (!cdb_hr::IHybridRowSerializer::is_default(value.GetOptions()))
{
scope.Find(row, OptionsColumn.GetPath());
cdb_hr::Result r = cdb_hr::TypedArrayHybridRowSerializer<std::optional<int32_t>, cdb_hr::NullableHybridRowSerializer<std::optional<int32_t>, int32_t, cdb_hr::Int32HybridRowSerializer>>::Write(
row,
scope,
false,
OptionsColumn.GetTypeArgs(),
cdb_hr::IHybridRowSerializer::get(value.GetOptions()));
if (r != cdb_hr::Result::Success)
{
return r;
}
}
if (!cdb_hr::IHybridRowSerializer::is_default(value.GetRatings()))
{
scope.Find(row, RatingsColumn.GetPath());
cdb_hr::Result r = cdb_hr::TypedArrayHybridRowSerializer<std::vector<float64_t>, cdb_hr::TypedArrayHybridRowSerializer<float64_t, cdb_hr::Float64HybridRowSerializer>>::Write(
row,
scope,
false,
RatingsColumn.GetTypeArgs(),
cdb_hr::IHybridRowSerializer::get(value.GetRatings()));
if (r != cdb_hr::Result::Success)
{
return r;
}
}
if (!cdb_hr::IHybridRowSerializer::is_default(value.GetSimilars()))
{
scope.Find(row, SimilarsColumn.GetPath());
cdb_hr::Result r = cdb_hr::TypedArrayHybridRowSerializer<SimilarMatch, SimilarMatchHybridRowSerializer>::Write(
row,
scope,
false,
SimilarsColumn.GetTypeArgs(),
cdb_hr::IHybridRowSerializer::get(value.GetSimilars()));
if (r != cdb_hr::Result::Success)
{
return r;
}
}
if (!cdb_hr::IHybridRowSerializer::is_default(value.GetPriority()))
{
scope.Find(row, PriorityColumn.GetPath());
cdb_hr::Result r = cdb_hr::TypedArrayHybridRowSerializer<std::tuple<std::string, int64_t>, cdb_hr::TypedTupleHybridRowSerializer<cdb_hr::Utf8HybridRowSerializer, cdb_hr::Int64HybridRowSerializer>>::Write(
row,
scope,
false,
PriorityColumn.GetTypeArgs(),
cdb_hr::IHybridRowSerializer::get(value.GetPriority()));
if (r != cdb_hr::Result::Success)
{
return r;
}
}
return cdb_hr::Result::Success;
}
std::tuple<cdb_hr::Result, std::unique_ptr<Tagged>> TaggedHybridRowSerializer::Read(const cdb_hr::RowBuffer& row, cdb_hr::RowCursor& scope, bool isRoot)
{
if (isRoot)
{
std::unique_ptr<Tagged> value = std::make_unique<Tagged>();
cdb_hr::Result r = TaggedHybridRowSerializer::Literal::Read(row, scope, *value);
return {r, std::move(value)};
}
auto [r, childScope] = cdb_hr::LayoutLiteral::UDT.ReadScope(row, scope);
if (r != cdb_hr::Result::Success)
{
return {r, std::unique_ptr<Tagged>{}};
}
std::unique_ptr<Tagged> value = std::make_unique<Tagged>();
r = TaggedHybridRowSerializer::Literal::Read(row, childScope, *value);
if (r != cdb_hr::Result::Success)
{
return {r, std::unique_ptr<Tagged>{}};
}
scope.Skip(row, childScope);
return {cdb_hr::Result::Success, std::move(value)};
}
cdb_hr::Result TaggedHybridRowSerializer::Literal::Read(const cdb_hr::RowBuffer& row, cdb_hr::RowCursor& scope, Tagged& value)
{
{
auto [r, fieldValue] = cdb_hr::LayoutLiteral::Utf8.ReadVariable(row, scope, TitleColumn);
switch (r)
{
case cdb_hr::Result::NotFound:
break;
case cdb_hr::Result::Success:
value.SetTitle(std::string(fieldValue));
break;
default:
return r;
}
}
while (scope.MoveNext(row))
{
if (scope.GetToken() == TagsToken.GetId())
{
auto [r, fieldValue] = cdb_hr::TypedArrayHybridRowSerializer<std::string, cdb_hr::Utf8HybridRowSerializer>::Read(
row, scope, false);
if (r != cdb_hr::Result::Success)
{
return r;
}
value.SetTags(std::move(fieldValue));
continue;
}
if (scope.GetToken() == OptionsToken.GetId())
{
auto [r, fieldValue] = cdb_hr::TypedArrayHybridRowSerializer<std::optional<int32_t>, cdb_hr::NullableHybridRowSerializer<std::optional<int32_t>, int32_t, cdb_hr::Int32HybridRowSerializer>>::Read(
row, scope, false);
if (r != cdb_hr::Result::Success)
{
return r;
}
value.SetOptions(std::move(fieldValue));
continue;
}
if (scope.GetToken() == RatingsToken.GetId())
{
auto [r, fieldValue] = cdb_hr::TypedArrayHybridRowSerializer<std::vector<float64_t>, cdb_hr::TypedArrayHybridRowSerializer<float64_t, cdb_hr::Float64HybridRowSerializer>>::Read(
row, scope, false);
if (r != cdb_hr::Result::Success)
{
return r;
}
value.SetRatings(std::move(fieldValue));
continue;
}
if (scope.GetToken() == SimilarsToken.GetId())
{
auto [r, fieldValue] = cdb_hr::TypedArrayHybridRowSerializer<SimilarMatch, SimilarMatchHybridRowSerializer>::Read(
row, scope, false);
if (r != cdb_hr::Result::Success)
{
return r;
}
value.SetSimilars(std::move(fieldValue));
continue;
}
if (scope.GetToken() == PriorityToken.GetId())
{
auto [r, fieldValue] = cdb_hr::TypedArrayHybridRowSerializer<std::tuple<std::string, int64_t>, cdb_hr::TypedTupleHybridRowSerializer<cdb_hr::Utf8HybridRowSerializer, cdb_hr::Int64HybridRowSerializer>>::Read(
row, scope, false);
if (r != cdb_hr::Result::Success)
{
return r;
}
value.SetPriority(std::move(fieldValue));
continue;
}
}
return cdb_hr::Result::Success;
}
class SimilarMatchHybridRowSerializer::Literal final
{
friend struct SimilarMatchHybridRowSerializer;
constexpr static std::string_view ThumbprintName{"thumbprint"sv};
constexpr static std::string_view ScoreName{"score"sv};
inline static const cdb_hr::Layout& Layout{TypedArrayHrSchema::GetLayoutResolver().Resolve(Id)};
inline static const cdb_hr::LayoutColumn& ThumbprintColumn{cdb_hr::IHybridRowSerializer::InitLayoutColumn(Layout, ThumbprintName)};
inline static const cdb_hr::LayoutColumn& ScoreColumn{cdb_hr::IHybridRowSerializer::InitLayoutColumn(Layout, ScoreName)};
static cdb_hr::Result Write(cdb_hr::RowBuffer& row, cdb_hr::RowCursor& scope, const SimilarMatch& value) noexcept;
static cdb_hr::Result Read(const cdb_hr::RowBuffer& row, cdb_hr::RowCursor& scope, SimilarMatch& value);
};
cdb_hr::Result SimilarMatchHybridRowSerializer::Write(cdb_hr::RowBuffer& row, cdb_hr::RowCursor& scope, bool isRoot,
const cdb_hr::TypeArgumentList& typeArgs, const SimilarMatch& value) noexcept
{
if (isRoot)
{
return SimilarMatchHybridRowSerializer::Literal::Write(row, scope, value);
}
auto [r, childScope] = cdb_hr::LayoutLiteral::UDT.WriteScope(row, scope, Id);
if (r != cdb_hr::Result::Success)
{
return r;
}
r = SimilarMatchHybridRowSerializer::Literal::Write(row, childScope, value);
if (r != cdb_hr::Result::Success)
{
return r;
}
scope.Skip(row, childScope);
return cdb_hr::Result::Success;
}
cdb_hr::Result SimilarMatchHybridRowSerializer::Literal::Write(cdb_hr::RowBuffer& row, cdb_hr::RowCursor& scope, const SimilarMatch& value) noexcept
{
if (!cdb_hr::IHybridRowSerializer::is_default(value.GetThumbprint()))
{
cdb_hr::Result r = cdb_hr::LayoutLiteral::Utf8.WriteFixed(
row, scope, ThumbprintColumn, cdb_hr::IHybridRowSerializer::get(value.GetThumbprint()));
if (r != cdb_hr::Result::Success)
{
return r;
}
}
if (!cdb_hr::IHybridRowSerializer::is_default(value.GetScore()))
{
cdb_hr::Result r = cdb_hr::LayoutLiteral::Float64.WriteFixed(
row, scope, ScoreColumn, cdb_hr::IHybridRowSerializer::get(value.GetScore()));
if (r != cdb_hr::Result::Success)
{
return r;
}
}
return cdb_hr::Result::Success;
}
std::tuple<cdb_hr::Result, std::unique_ptr<SimilarMatch>> SimilarMatchHybridRowSerializer::Read(const cdb_hr::RowBuffer& row, cdb_hr::RowCursor& scope, bool isRoot)
{
if (isRoot)
{
std::unique_ptr<SimilarMatch> value = std::make_unique<SimilarMatch>();
cdb_hr::Result r = SimilarMatchHybridRowSerializer::Literal::Read(row, scope, *value);
return {r, std::move(value)};
}
auto [r, childScope] = cdb_hr::LayoutLiteral::UDT.ReadScope(row, scope);
if (r != cdb_hr::Result::Success)
{
return {r, std::unique_ptr<SimilarMatch>{}};
}
std::unique_ptr<SimilarMatch> value = std::make_unique<SimilarMatch>();
r = SimilarMatchHybridRowSerializer::Literal::Read(row, childScope, *value);
if (r != cdb_hr::Result::Success)
{
return {r, std::unique_ptr<SimilarMatch>{}};
}
scope.Skip(row, childScope);
return {cdb_hr::Result::Success, std::move(value)};
}
cdb_hr::Result SimilarMatchHybridRowSerializer::Literal::Read(const cdb_hr::RowBuffer& row, cdb_hr::RowCursor& scope, SimilarMatch& value)
{
{
auto [r, fieldValue] = cdb_hr::LayoutLiteral::Utf8.ReadFixed(row, scope, ThumbprintColumn);
switch (r)
{
case cdb_hr::Result::NotFound:
break;
case cdb_hr::Result::Success:
value.SetThumbprint(std::string(fieldValue));
break;
default:
return r;
}
}
{
auto [r, fieldValue] = cdb_hr::LayoutLiteral::Float64.ReadFixed(row, scope, ScoreColumn);
switch (r)
{
case cdb_hr::Result::NotFound:
break;
case cdb_hr::Result::Success:
value.SetScore(fieldValue);
break;
default:
return r;
}
}
return cdb_hr::Result::Success;
}
}

View File

@@ -0,0 +1,97 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------
#pragma once
// ------------------------------------------------------------
// This file was generated by:
// Microsoft.Azure.Cosmos.Serialization.HybridRowCLI: 1.0.0.0
//
// This file should not be modified directly.
// ------------------------------------------------------------
namespace cdb_hr_test::typed_array
{
class Tagged;
class SimilarMatch;
struct TypedArrayHrSchema final
{
static const cdb_hr::Namespace& GetNamespace() noexcept;
static const cdb_hr::LayoutResolver& GetLayoutResolver() noexcept;
private:
class Literal;
};
class Tagged final
{
public:
[[nodiscard]] const std::string& GetTitle() const noexcept { return m_title; }
void SetTitle(std::string value) noexcept { m_title = std::move(value);}
[[nodiscard]] const std::vector<std::string>& GetTags() const noexcept { return m_tags; }
void SetTags(std::vector<std::string> value) noexcept { m_tags = std::move(value);}
[[nodiscard]] const std::vector<std::optional<int32_t>>& GetOptions() const noexcept { return m_options; }
void SetOptions(std::vector<std::optional<int32_t>> value) noexcept { m_options = std::move(value);}
[[nodiscard]] const std::vector<std::vector<float64_t>>& GetRatings() const noexcept { return m_ratings; }
void SetRatings(std::vector<std::vector<float64_t>> value) noexcept { m_ratings = std::move(value);}
[[nodiscard]] const std::vector<std::unique_ptr<SimilarMatch>>& GetSimilars() const noexcept { return m_similars; }
void SetSimilars(std::vector<std::unique_ptr<SimilarMatch>> value) noexcept { m_similars = std::move(value);}
[[nodiscard]] const std::vector<std::tuple<std::string, int64_t>>& GetPriority() const noexcept { return m_priority; }
void SetPriority(std::vector<std::tuple<std::string, int64_t>> value) noexcept { m_priority = std::move(value);}
private:
std::string m_title{};
std::vector<std::string> m_tags{};
std::vector<std::optional<int32_t>> m_options{};
std::vector<std::vector<float64_t>> m_ratings{};
std::vector<std::unique_ptr<SimilarMatch>> m_similars{};
std::vector<std::tuple<std::string, int64_t>> m_priority{};
};
class SimilarMatch final
{
public:
[[nodiscard]] const std::string& GetThumbprint() const noexcept { return m_thumbprint; }
void SetThumbprint(std::string value) noexcept { m_thumbprint = std::move(value);}
[[nodiscard]] float64_t GetScore() const noexcept { return m_score; }
void SetScore(float64_t value) noexcept { m_score = std::move(value);}
private:
std::string m_thumbprint{};
float64_t m_score{};
};
struct TaggedHybridRowSerializer final
{
using value_type = Tagged;
using owning_type = std::unique_ptr<value_type>;
constexpr static cdb_hr::SchemaId Id{1};
static cdb_hr::Result Write(cdb_hr::RowBuffer& row, cdb_hr::RowCursor& scope, bool isRoot, const cdb_hr::TypeArgumentList& typeArgs,
const Tagged& value) noexcept;
static std::tuple<cdb_hr::Result, std::unique_ptr<Tagged>>
Read(const cdb_hr::RowBuffer& row, cdb_hr::RowCursor& scope, bool isRoot);
private:
class Literal;
};
static_assert(cdb_hr::is_hybridrow_serializer_v<Tagged, TaggedHybridRowSerializer>);
struct SimilarMatchHybridRowSerializer final
{
using value_type = SimilarMatch;
using owning_type = std::unique_ptr<value_type>;
constexpr static cdb_hr::SchemaId Id{2};
static cdb_hr::Result Write(cdb_hr::RowBuffer& row, cdb_hr::RowCursor& scope, bool isRoot, const cdb_hr::TypeArgumentList& typeArgs,
const SimilarMatch& value) noexcept;
static std::tuple<cdb_hr::Result, std::unique_ptr<SimilarMatch>>
Read(const cdb_hr::RowBuffer& row, cdb_hr::RowCursor& scope, bool isRoot);
private:
class Literal;
};
static_assert(cdb_hr::is_hybridrow_serializer_v<SimilarMatch, SimilarMatchHybridRowSerializer>);
}

View File

@@ -0,0 +1,134 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------
#include "pch.h"
#include "CppUnitTest.h"
#include "CppUnitTestFramework.inl"
#include "TaggedSchema.generated.h"
#include "ResultAssert.h"
namespace cdb_hr_test
{
using namespace std::literals;
using Assert = Microsoft::VisualStudio::CppUnitTestFramework::Assert;
TEST_CLASS(TypedArrayUnitTests)
{
public:
TypedArrayUnitTests() :
m_resolver{typed_array::TypedArrayHrSchema::GetLayoutResolver()},
m_layout{m_resolver.Resolve(typed_array::TaggedHybridRowSerializer::Id)} { }
TEST_METHOD_WITH_OWNER(CreateTags, "jthunter")
{
cdb_hr::MemorySpanResizer<byte> resizer{InitialRowSize};
cdb_hr::RowBuffer row{InitialRowSize, &resizer};
row.InitLayout(cdb_hr::HybridRowVersion::V1, m_layout, &m_resolver);
std::unique_ptr<typed_array::Tagged> t1 = cdb_core::make_unique_with([&](typed_array::Tagged& o)
{
o.SetTitle("Thriller");
o.SetTags(std::vector<std::string>{"classic", "Post-disco", "funk"});
o.SetOptions(std::vector<std::optional<int32_t>>{8, {}, 9});
o.SetRatings(std::vector<std::vector<float64_t>>
{
std::vector<float64_t>{1.2, 3.0},
std::vector<float64_t>{4.1, 5.7},
std::vector<float64_t>{7.3, 8.12, 9.14},
});
o.SetSimilars(
cdb_core::make_with([](std::vector<std::unique_ptr<typed_array::SimilarMatch>>& v)
{
// ReSharper disable three StringLiteralTypo
v.emplace_back(cdb_core::make_unique_with([&](typed_array::SimilarMatch& m)
{
m.SetThumbprint("TRABACN128F425B784");
m.SetScore(0.87173699999999998);
}));
v.emplace_back(cdb_core::make_unique_with([&](typed_array::SimilarMatch& m)
{
m.SetThumbprint("TRJYGLF12903CB4952");
m.SetScore(0.75105200000000005);
}));
v.emplace_back(cdb_core::make_unique_with([&](typed_array::SimilarMatch& m)
{
m.SetThumbprint("TRWJMMB128F429D550");
m.SetScore(0.50866100000000003);
}));
}));
o.SetPriority(std::vector<std::tuple<std::string, int64_t>>
{
std::make_tuple("80's", 100L),
std::make_tuple("classics", 100L),
std::make_tuple("pop", 50L),
});
});
cdb_hr::RowCursor scope = cdb_hr::RowCursor::Create(row);
ResultAssert::IsSuccess(typed_array::TaggedHybridRowSerializer::Write(row, scope, true, {}, *t1));
scope = cdb_hr::RowCursor::Create(row);
auto [r, t2] = typed_array::TaggedHybridRowSerializer::Read(row, scope, true);
ResultAssert::IsSuccess(r);
t1->SetSimilars(
cdb_core::make_with([](std::vector<std::unique_ptr<typed_array::SimilarMatch>>& v)
{
// ReSharper disable three StringLiteralTypo
v.emplace_back(cdb_core::make_unique_with([&](typed_array::SimilarMatch& m)
{
m.SetThumbprint("TRABACN128F425B784");
m.SetScore(0.87173699999999998);
}));
v.emplace_back(cdb_core::make_unique_with([&](typed_array::SimilarMatch& m)
{
m.SetThumbprint("TRJYGLF12903CB4952");
m.SetScore(0.75105200000000005);
}));
v.emplace_back(cdb_core::make_unique_with([&](typed_array::SimilarMatch& m)
{
m.SetThumbprint("TRWJMMB128F429D550");
m.SetScore(0.50866100000000003);
}));
}));
Assert::IsTrue(cdb_core::DeepCompare(t1, t2));
}
private:
constexpr static int InitialRowSize = 2 * 1024 * 1024;
const cdb_hr::LayoutResolver& m_resolver;
const cdb_hr::Layout& m_layout;
};
}
namespace cdb_core
{
template<>
struct DeepComparer<cdb_hr_test::typed_array::Tagged>
{
using value_type = cdb_hr_test::typed_array::Tagged;
bool operator()(const value_type& x, const value_type& y) const noexcept
{
return cdb_core::DeepCompare(x.GetTitle(), y.GetTitle()) &&
cdb_core::DeepCompare(x.GetTags(), y.GetTags()) &&
cdb_core::DeepCompare(x.GetOptions(), y.GetOptions()) &&
cdb_core::DeepCompare(x.GetRatings(), y.GetRatings()) &&
cdb_core::DeepCompare(x.GetSimilars(), y.GetSimilars()) &&
cdb_core::DeepCompare(x.GetPriority(), y.GetPriority());
}
};
template<>
struct DeepComparer<cdb_hr_test::typed_array::SimilarMatch>
{
using value_type = cdb_hr_test::typed_array::SimilarMatch;
bool operator()(const value_type& x, const value_type& y) const noexcept
{
return
cdb_core::DeepCompare(x.GetThumbprint(), y.GetThumbprint()) &&
cdb_core::DeepCompare(x.GetScore(), y.GetScore());
}
};
}

View File

@@ -0,0 +1,4 @@
// Hint files help the Visual Studio IDE interpret Visual C++ identifiers
// such as names of functions and macros.
// For more information see https://go.microsoft.com/fwlink/?linkid=865984
#define TEST_METHOD_WITH_OWNER(methodName, ownerAlias) BEGIN_TEST_METHOD_ATTRIBUTE(methodName) TEST_OWNER(L##ownerAlias) END_TEST_METHOD_ATTRIBUTE() TEST_METHOD(methodName)

View File

@@ -0,0 +1,5 @@
// pch.cpp: source file corresponding to the pre-compiled header
#include "pch.h"
// When you are using pre-compiled headers, this source file is necessary for compilation to succeed.

View File

@@ -0,0 +1,7 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------
#pragma once
#include "../HybridRow.Native/HybridRow.Native.h"

View File

@@ -0,0 +1,27 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------
#pragma once
namespace cdb_hr
{
/// <summary>Describes the empty canonicalization for properties.</summary>
enum class AllowEmptyKind : unsigned char
{
/// <summary>Empty and null are treated as distinct.</summary>
None = 0,
/// <summary>Empty values are converted to null when written.</summary>
EmptyAsNull = 1,
/// <summary>Null values are converted to empty when read.</summary>
NullAsEmpty = 2,
/// <summary>
/// Empty values are converted to null when written, and null values are converted to empty
/// when read.
/// </summary>
Both = EmptyAsNull | NullAsEmpty,
};
}

View File

@@ -0,0 +1,105 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------
#pragma once
#include "IHybridRowSerializer.h"
#include "LayoutType.h"
#include "RowCursor.h"
namespace cdb_hr
{
template<typename T, typename TSerializer, typename = std::enable_if_t<is_hybridrow_serializer_v<T, TSerializer>>>
struct ArrayHybridRowSerializer final
{
struct ArrayComparer;
using value_type = std::vector<typename TSerializer::owning_type>;
using owning_type = value_type;
using const_reference = const value_type&;
using comparer_type = ArrayComparer;
static Result Write(RowBuffer& row, RowCursor& scope, bool isRoot, const TypeArgumentList& typeArgs,
const_reference value) noexcept
{
auto [r, childScope] = LayoutLiteral::Array.WriteScope(row, scope, TypeArgumentList{});
if (r != Result::Success)
{
return r;
}
for (auto& item : value)
{
r = TSerializer::Write(row, childScope, false, typeArgs[0].GetTypeArgs(), IHybridRowSerializer::get(item));
if (r != Result::Success)
{
return r;
}
childScope.MoveNext(row);
}
scope.Skip(row, childScope);
return Result::Success;
}
static std::tuple<Result, value_type> Read(const RowBuffer& row, RowCursor& scope, bool isRoot)
{
auto [r, childScope] = LayoutLiteral::Array.ReadScope(row, scope);
if (r != Result::Success)
{
return {r, value_type{}};
}
value_type items{};
while (childScope.MoveNext(row))
{
auto [r, item] = TSerializer::Read(row, childScope, isRoot);
if (r != Result::Success)
{
return {r, value_type{}};
}
items.emplace_back(std::move(item));
}
scope.Skip(row, childScope);
return {Result::Success, std::move(items)};
}
struct ArrayComparer final
{
constexpr bool operator()(const_reference x, const_reference y) const
{
if (&x == &y)
{
return true;
}
if (x.size() != y.size())
{
return false;
}
for (size_t i = 0; i < x.size(); i++)
{
if (!typename TSerializer::comparer_type{}.operator()(x[i], y[i]))
{
return false;
}
}
return true;
}
constexpr std::size_t operator()(const_reference s) const noexcept
{
cdb_core::HashCode hash{};
for (size_t i = 0; i < s.size(); i++)
{
hash.Add<T, typename TSerializer::comparer_type>(s[i]);
}
return hash.ToHashCode();
}
};
};
}

View File

@@ -0,0 +1,47 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------
#pragma once
#include <memory>
#include "ScopePropertyType.h"
namespace cdb_hr
{
/// <summary>Array properties represent an unbounded set of zero or more items.</summary>
/// <remarks>
/// Arrays may be typed or untyped. Within typed arrays, all items MUST be the same type. The
/// type of items is specified via <see cref="SetItems" />. Typed arrays may be stored more efficiently
/// than untyped arrays. When <see cref="SetItems" /> is unspecified, the array is untyped and its items
/// may be heterogeneous.
/// </remarks>
class ArrayPropertyType final : public ScopePropertyType
{
public:
ArrayPropertyType() noexcept : ScopePropertyType{TypeKind::Array}, m_items{} {}
explicit ArrayPropertyType(std::unique_ptr<PropertyType> items, bool nullable = true, bool immutable = false) noexcept :
ScopePropertyType{TypeKind::Array, nullable, immutable},
m_items{std::move(items)} {}
~ArrayPropertyType() noexcept override = default;
ArrayPropertyType(ArrayPropertyType&) = delete;
ArrayPropertyType(ArrayPropertyType&&) = delete;
ArrayPropertyType& operator=(const ArrayPropertyType&) = delete;
ArrayPropertyType& operator=(ArrayPropertyType&&) = delete;
[[nodiscard]] SchemaId GetRuntimeSchemaId() const noexcept override { return SchemaId{2147473661}; }
[[nodiscard]] PropertyKind GetKind() const noexcept override { return PropertyKind::Array; }
/// <summary>(Optional) type of the elements of the array, if a typed array, otherwise null.</summary>
[[nodiscard]] std::optional<std::reference_wrapper<const PropertyType>> GetItems() const noexcept
{
return m_items ? std::optional<std::reference_wrapper<const PropertyType>>{*m_items} : std::nullopt;
}
void SetItems(std::unique_ptr<PropertyType> value) noexcept { m_items = std::move(value); }
private:
std::unique_ptr<PropertyType> m_items;
};
}

View File

@@ -0,0 +1,55 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------
#pragma once
#include <cstdint>
namespace cdb_hr
{
/// <summary>C# DateTime type.</summary>
/// <remarks>CoreCLR DateTime definition: https://referencesource.microsoft.com/#mscorlib/system/datetime.cs </remarks>
#pragma pack(push, 1)
struct DateTime final
{
constexpr DateTime() noexcept : m_data(0) { }
~DateTime() noexcept = default;
constexpr DateTime(int64_t ticks) noexcept : m_data(static_cast<uint64_t>(ticks)) { }
DateTime(const DateTime& other) = default;
DateTime(DateTime&& other) noexcept = default;
DateTime& operator=(const DateTime& other) = default;
DateTime& operator=(DateTime&& other) noexcept = default;
[[nodiscard]] int64_t Ticks() const noexcept { return static_cast<int64_t>(m_data); }
friend bool operator==(const DateTime& lhs, const DateTime& rhs) { return lhs.m_data == rhs.m_data; }
friend bool operator!=(const DateTime& lhs, const DateTime& rhs) { return !(lhs == rhs); }
private:
friend std::hash<cdb_hr::DateTime>;
// The data is stored as an unsigned 64-bit integer
// Bits 01-62: The value of 100-nanosecond ticks where 0 represents 1/1/0001 12:00am, up until the value
// 12/31/9999 23:59:59.9999999
// Bits 63-64: A four-state value that describes the DateTimeKind value of the date time, with a 2nd
// value for the rare case where the date time is local, but is in an overlapped daylight
// savings time hour and it is in daylight savings time. This allows distinction of these
// otherwise ambiguous local times and prevents data loss when round tripping from Local to
// UTC time.
uint64_t m_data;
};
#pragma pack(pop)
}
namespace std
{
template<>
struct hash<cdb_hr::DateTime>
{
constexpr std::size_t operator()(cdb_hr::DateTime const& s) const noexcept
{
return cdb_core::HashCode::Combine(s.m_data);
}
};
}

View File

@@ -0,0 +1,81 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------
#pragma once
#include <cstdint>
#include <cstddef>
namespace cdb_hr
{
/// <summary>C# decimal type.</summary>
/// <remarks>CoreCLR Decimal definition: https://referencesource.microsoft.com/#mscorlib/system/decimal.cs </remarks>
#pragma pack(push, 1)
struct Decimal final
{
constexpr Decimal() noexcept : m_flags(0), m_hi(0), m_lo(0), m_mid(0) { }
~Decimal() noexcept = default;
constexpr Decimal(int32_t lo, int32_t mid, int32_t hi, bool isNegative, std::byte byteScale) noexcept :
m_flags((static_cast<int32_t>(byteScale) << 16) | (isNegative ? Decimal::SignMask : 0)),
m_hi(hi),
m_lo(lo),
m_mid(mid) { }
constexpr Decimal(int64_t value) noexcept :
m_flags((value >= 0) ? 0 : SignMask),
m_hi(0),
m_lo(static_cast<int32_t>(value >= 0 ? value : -value)),
m_mid(static_cast<int32_t>((value >= 0 ? value : -value) >> 32)) { }
Decimal(const Decimal& other) = default;
Decimal(Decimal&& other) noexcept = default;
Decimal& operator=(const Decimal& other) = default;
Decimal& operator=(Decimal&& other) noexcept = default;
friend bool operator==(const Decimal& lhs, const Decimal& rhs)
{
return lhs.m_flags == rhs.m_flags
&& lhs.m_hi == rhs.m_hi
&& lhs.m_lo == rhs.m_lo
&& lhs.m_mid == rhs.m_mid;
}
friend bool operator!=(const Decimal& lhs, const Decimal& rhs) { return !(lhs == rhs); }
private:
friend std::hash<cdb_hr::Decimal>;
// The lo, mid, hi, and flags fields contain the representation of the
// Decimal value. The lo, mid, and hi fields contain the 96-bit integer
// part of the Decimal. Bits 0-15 (the lower word) of the flags field are
// unused and must be zero; bits 16-23 contain must contain a value between
// 0 and 28, indicating the power of 10 to divide the 96-bit integer part
// by to produce the Decimal value; bits 24-30 are unused and must be zero;
// and finally bit 31 indicates the sign of the Decimal value, 0 meaning
// positive and 1 meaning negative.
int32_t m_flags;
int32_t m_hi;
int32_t m_lo;
int32_t m_mid;
// Sign mask for the flags field. A value of zero in this bit indicates a
// positive Decimal value, and a value of one in this bit indicates a
// negative Decimal value.
constexpr static int32_t SignMask = static_cast<int32_t>(0x80000000);
};
#pragma pack(pop)
}
namespace std
{
template<>
struct hash<cdb_hr::Decimal>
{
constexpr std::size_t operator()(cdb_hr::Decimal const& s) const noexcept
{
return cdb_core::HashCode::Combine(s.m_flags, s.m_hi, s.m_lo, s.m_mid);
}
};
}

View File

@@ -0,0 +1,65 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------
#pragma once
#include <string>
#include <vector>
#include <memory>
#include "TypeKind.h"
#include "EnumValue.h"
namespace cdb_hr
{
/// <summary>An enum schema describes a set of constrained integer values.</summary>
class EnumSchema final
{
public:
/// <summary>Initializes a new instance of the <see cref="EnumSchema" /> class.</summary>
EnumSchema() noexcept :
m_comment{},
m_name{},
m_apitype{},
m_type{TypeKind::Int32},
m_values{} {}
/// <summary>An (optional) comment describing the purpose of this enum.</summary>
/// <remarks>Comments are for documentary purpose only and do not affect the enum at runtime.</remarks>
[[nodiscard]] std::string_view GetComment() const noexcept { return m_comment; }
void SetComment(std::string_view value) noexcept { m_comment = value; }
/// <summary>The name of the enum.</summary>
/// <remarks>
/// The name of a enum MUST be unique within its namespace.
/// <para />
/// Names must begin with an alpha-numeric character and can only contain alpha-numeric characters and
/// underscores.
/// </remarks>
[[nodiscard]] std::string_view GetName() const noexcept { return m_name; }
void SetName(std::string_view value) noexcept { m_name = value; }
/// <summary>Api-specific type annotations for the property.</summary>
[[nodiscard]] std::string_view GetApiType() const noexcept { return m_apitype; }
void SetApiType(std::string_view value) noexcept { m_apitype = value; }
/// <summary>The logical base type of the enum.</summary>
/// <remarks>This must be a primitive.</remarks>
[[nodiscard]] TypeKind GetType() const noexcept { return m_type; }
void SetType(TypeKind value) noexcept { m_type = value; }
/// <summary>A list of zero or more value definitions.</summary>
/// <remarks>This field is never null.</remarks>
[[nodiscard]] const std::vector<std::unique_ptr<EnumValue>>& GetValues() const noexcept { return m_values; }
std::vector<std::unique_ptr<EnumValue>>& GetValues() noexcept { return m_values; }
void SetValues(std::vector<std::unique_ptr<EnumValue>> value) noexcept { m_values = std::move(value); }
private:
tla::string m_comment;
tla::string m_name;
tla::string m_apitype;
TypeKind m_type;
/// <summary>A list of zero or more value definitions.</summary>
std::vector<std::unique_ptr<EnumValue>> m_values;
};
}

View File

@@ -0,0 +1,41 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------
#pragma once
#include <string>
namespace cdb_hr
{
/// <summary>An enum schema describes a set of constrained integer values.</summary>
class EnumValue final
{
public:
/// <summary>Initializes a new instance of the <see cref="EnumValue" /> class.</summary>
EnumValue() noexcept : m_comment{}, m_name{}, m_value{} {}
/// <summary>An (optional) comment describing the purpose of this value.</summary>
/// <remarks>Comments are for documentary purpose only and do not affect the enum at runtime.</remarks>
[[nodiscard]] std::string_view GetComment() const noexcept { return m_comment; }
void SetComment(std::string_view value) noexcept { m_comment = value; }
/// <summary>The name of the enum value.</summary>
/// <remarks>
/// The name of a value MUST be unique within its EnumSchema.
/// <para />
/// Names must begin with an alpha-numeric character and can only contain alpha-numeric characters and
/// underscores.
/// </remarks>
[[nodiscard]] std::string_view GetName() const noexcept { return m_name; }
void SetName(std::string_view value) noexcept { m_name = value; }
/// <summary>The numerical value of the enum value.</summary>
[[nodiscard]] int64_t GetValue() const noexcept { return m_value; }
void SetValue(int64_t value) noexcept { m_value = value; }
private:
tla::string m_comment;
tla::string m_name;
int64_t m_value;
};
}

View File

@@ -0,0 +1,78 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------
#pragma once
#include <cstdint>
namespace cdb_hr
{
/// <summary>An IEEE 128-bit floating point value.</summary>
/// <remarks>
/// A binary integer decimal representation of a 128-bit decimal value, supporting 34 decimal digits of
/// significand and an exponent range of -6143 to +6144.
/// <list type="table">
/// <listheader>
/// <term>Source</term> <description>Link</description>
/// </listheader> <item>
/// <term>Wikipedia:</term>
/// <description>https://en.wikipedia.org/wiki/Decimal128_floating-point_format</description>
/// </item> <item>
/// <term>The spec:</term> <description>https://ieeexplore.ieee.org/document/4610935</description>
/// </item> <item>
/// <term>Decimal Encodings:</term> <description>http://speleotrove.com/decimal/decbits.html</description>
/// </item>
/// </list>
/// </remarks>
#pragma pack(push, 1)
struct Float128 final
{
/// <summary>The size (in bytes) of a <see cref="Float128" />.</summary>
constexpr static int Size = sizeof(long) + sizeof(long);
/// <summary>
/// The low-order 64 bits of the IEEE 754-2008 128-bit decimal floating point, using the BID
/// encoding scheme.
/// </summary>
int64_t Low;
/// <summary>
/// The high-order 64 bits of the IEEE 754-2008 128-bit decimal floating point, using the BID
/// encoding scheme.
/// </summary>
int64_t High;
/// <summary>Initializes a new instance of the <see cref="Float128" /> struct.</summary>
/// <param name="high">the high-order 64 bits.</param>
/// <param name="low">the low-order 64 bits.</param>
constexpr Float128(long high, long low) noexcept : Low(low), High(high) { }
constexpr Float128() noexcept : Low(0), High(0) { }
~Float128() = default;
Float128(const Float128& other) noexcept = default;
Float128(Float128&& other) noexcept = default;
Float128& operator=(const Float128& other) noexcept = default;
Float128& operator=(Float128&& other) noexcept = default;
friend bool operator==(const Float128& lhs, const Float128& rhs)
{
return lhs.Low == rhs.Low
&& lhs.High == rhs.High;
}
friend bool operator!=(const Float128& lhs, const Float128& rhs) { return !(lhs == rhs); }
};
#pragma pack(pop)
}
namespace std
{
template<>
struct hash<cdb_hr::Float128>
{
constexpr std::size_t operator()(cdb_hr::Float128 const& s) const noexcept
{
return cdb_core::HashCode::Combine(s.Low, s.High);
}
};
}

View File

@@ -0,0 +1,23 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------
#include "pch.h"
#include "Guid.h"
#include "winerror.h"
namespace rpc
{
#include <rpc.h>
}
namespace cdb_hr
{
Guid Guid::NewGuid() noexcept
{
Guid retval;
rpc::RPC_STATUS status = rpc::UuidCreate(&retval.m_data);
cdb_core::Contract::Requires(status == S_OK);
return retval;
}
}

View File

@@ -0,0 +1,65 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------
#pragma once
// ReSharper disable once CppUnusedIncludeDirective
#include "guiddef.h"
namespace cdb_hr
{
/// <summary>C# Guid type.</summary>
/// <remarks>CoreCLR Guid definition: https://referencesource.microsoft.com/#mscorlib/system/guid.cs </remarks>
#pragma pack(push, 1)
struct Guid final
{
constexpr static Guid Empty() noexcept { return Guid{}; }
static Guid NewGuid() noexcept;
constexpr Guid() noexcept : m_data{0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0}} { }
~Guid() noexcept = default;
constexpr Guid(uint32_t a, uint16_t b, uint16_t c, uint8_t d, uint8_t e, uint8_t f, uint8_t g, uint8_t h, uint8_t i,
uint8_t j, uint8_t k) noexcept;
Guid(const Guid& other) = default;
Guid(Guid&& other) noexcept = default;
Guid& operator=(const Guid& other) = default;
Guid& operator=(Guid&& other) noexcept = default;
friend bool operator==(const Guid& lhs, const Guid& rhs) noexcept;
friend bool operator!=(const Guid& lhs, const Guid& rhs) noexcept;
private:
friend std::hash<Guid>;
GUID m_data;
};
#pragma pack(pop)
constexpr Guid::Guid(uint32_t a, uint16_t b, uint16_t c, uint8_t d, uint8_t e, uint8_t f, uint8_t g, uint8_t h,
uint8_t i, uint8_t j, uint8_t k) noexcept :
m_data{a, b, c, {d, e, f, g, h, i, j, k}} { }
inline bool operator==(const Guid& lhs, const Guid& rhs) noexcept
{
return IsEqualGUID(lhs.m_data, rhs.m_data) != 0;
}
inline bool operator!=(const Guid& lhs, const Guid& rhs) noexcept
{
return !(lhs == rhs);
}
}
namespace std
{
template<>
struct hash<cdb_hr::Guid>
{
constexpr std::size_t operator()(cdb_hr::Guid const& s) const noexcept
{
return cdb_core::HashCode::Combine(
s.m_data.Data1, s.m_data.Data2, s.m_data.Data3, s.m_data.Data4[2], s.m_data.Data4[7]);
}
};
}

View File

@@ -0,0 +1,59 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------
#pragma once
#include "framework.h"
// Scalar Types
#include "Result.h"
#include "MemorySpanResizer.h"
// Layout Types
#include "StringTokenizer.h"
#include "LayoutType.h"
#include "LayoutType.inl"
#include "LayoutCompiler.h"
#include "LayoutResolver.h"
#include "LayoutResolverNamespace.h"
// Schema Types
#include "StorageKind.h"
#include "TypeKind.h"
#include "SchemaLanguageVersion.h"
#include "Namespace.h"
#include "Schema.h"
#include "SchemaId.h"
#include "SchemaOptions.h"
#include "Property.h"
#include "PrimarySortKey.h"
#include "PartitionKey.h"
#include "SortDirection.h"
#include "StaticKey.h"
#include "PropertyType.h"
#include "UdtPropertyType.h"
#include "TuplePropertyType.h"
#include "TaggedPropertyType.h"
#include "SetPropertyType.h"
#include "ScopePropertyType.h"
#include "PrimitivePropertyType.h"
#include "ObjectPropertyType.h"
#include "MapPropertyType.h"
#include "ArrayPropertyType.h"
#include "Segment.h"
// System Schema
#include "IHybridRowSerializer.h"
#include "SystemSchemaLiteral.h"
#include "ArrayHybridRowSerializer.h"
#include "TypedArrayHybridRowSerializer.h"
#include "TypedMapHybridRowSerializer.h"
#include "TypedTupleHybridRowSerializer.h"
#include "NullableHybridRowSerializer.h"
#include "PrimitiveHybridRowSerializer.h"
#include "SystemSchema.h"
// RecordIO
#include "RecordIOFormatter.h"
#include "RecordIOParser.h"

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<Type Name="Microsoft::Azure::Cosmos::Serialization::HybridRow::SchemaId">
<DisplayString>{{{m_id}}}</DisplayString>
</Type>
</AutoVisualizer>

View File

@@ -0,0 +1,177 @@
<?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>{D02FD209-EFF3-4D3F-B98E-E1BF9A1DD894}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>HybridRowNative</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<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>
<PreprocessToFile>false</PreprocessToFile>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
</Link>
</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>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="AllowEmptyKind.h" />
<ClInclude Include="ArrayHybridRowSerializer.h" />
<ClInclude Include="ArrayPropertyType.h" />
<ClInclude Include="DateTime.h" />
<ClInclude Include="Decimal.h" />
<ClInclude Include="EnumSchema.h" />
<ClInclude Include="EnumValue.h" />
<ClInclude Include="Float128.h" />
<ClInclude Include="framework.h" />
<ClInclude Include="Guid.h" />
<ClInclude Include="HybridRow.Native.h" />
<ClInclude Include="HybridRowHeader.h" />
<ClInclude Include="HybridRowVersion.h" />
<ClInclude Include="IHybridRowSerializer.h" />
<ClInclude Include="ISpanResizer.h" />
<ClInclude Include="Layout.h" />
<ClInclude Include="LayoutBit.h" />
<ClInclude Include="LayoutBuilder.h" />
<ClInclude Include="LayoutCode.h" />
<ClInclude Include="LayoutCodeTraits.h" />
<ClInclude Include="LayoutColumn.h" />
<ClInclude Include="LayoutCompiler.h" />
<ClInclude Include="LayoutResolver.h" />
<ClInclude Include="LayoutResolverNamespace.h" />
<ClInclude Include="LayoutType.h" />
<ClInclude Include="MapPropertyType.h" />
<ClInclude Include="MemorySpanResizer.h" />
<ClInclude Include="MongoDbObjectId.h" />
<ClInclude Include="Namespace.h" />
<ClInclude Include="NullableHybridRowSerializer.h" />
<ClInclude Include="NullValue.h" />
<ClInclude Include="ObjectPropertyType.h" />
<ClInclude Include="PartitionKey.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="PrimarySortKey.h" />
<ClInclude Include="PrimitiveHybridRowSerializer.h" />
<ClInclude Include="PrimitivePropertyType.h" />
<ClInclude Include="Property.h" />
<ClInclude Include="PropertyKind.h" />
<ClInclude Include="PropertyType.h" />
<ClInclude Include="RecordIOFormatter.h" />
<ClInclude Include="RecordIOParser.h" />
<ClInclude Include="Result.h" />
<ClInclude Include="RowBuffer.h" />
<ClInclude Include="RowCursor.h" />
<ClInclude Include="RowOptions.h" />
<ClInclude Include="RowReader.h" />
<ClInclude Include="RowWriter.h" />
<ClInclude Include="SamplingUtf8StringComparer.h" />
<ClInclude Include="Schema.h" />
<ClInclude Include="SchemaId.h" />
<ClInclude Include="SchemaLanguageVersion.h" />
<ClInclude Include="SchemaOptions.h" />
<ClInclude Include="ScopePropertyType.h" />
<ClInclude Include="Segment.h" />
<ClInclude Include="SetPropertyType.h" />
<ClInclude Include="SortDirection.h" />
<ClInclude Include="StaticKey.h" />
<ClInclude Include="StorageKind.h" />
<ClInclude Include="StringTokenizer.h" />
<ClInclude Include="SystemSchema.h" />
<ClInclude Include="SystemSchemaLiteral.h" />
<ClInclude Include="TaggedPropertyType.h" />
<ClInclude Include="TuplePropertyType.h" />
<ClInclude Include="TypeArgument.h" />
<ClInclude Include="TypeArgumentList.h" />
<ClInclude Include="TypedArrayHybridRowSerializer.h" />
<ClInclude Include="TypedMapHybridRowSerializer.h" />
<ClInclude Include="TypedTupleHybridRowSerializer.h" />
<ClInclude Include="TypeKind.h" />
<ClInclude Include="UdtPropertyType.h" />
<ClInclude Include="UnixDateTime.h" />
<ClInclude Include="UpdateOptions.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="Guid.cpp" />
<ClCompile Include="Layout.cpp" />
<ClCompile Include="LayoutBuilder.cpp" />
<ClCompile Include="LayoutColumn.cpp" />
<ClCompile Include="LayoutCompiler.cpp" />
<ClCompile Include="LayoutResolverNamespace.cpp" />
<ClCompile Include="LayoutType.cpp" />
<ClCompile Include="MongoDbObjectId.cpp" />
<ClCompile Include="Namespace.cpp" />
<ClCompile Include="RecordIOFormatter.cpp" />
<ClCompile Include="RecordIOParser.cpp" />
<ClCompile Include="RowBuffer.cpp" />
<ClCompile Include="RowCursor.cpp" />
<ClCompile Include="RowReader.cpp" />
<ClCompile Include="RowWriter.cpp" />
<ClCompile Include="Schema.cpp" />
<ClCompile Include="StringTokenizer.cpp" />
<ClCompile Include="SystemSchema.cpp" />
<ClCompile Include="TypeArgument.cpp" />
<ClCompile Include="TypeArgumentList.cpp" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="pch.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Core\Core.Native\Microsoft.Azure.Cosmos.Core.Native.vcxproj">
<Project>{eaed7d41-3de6-4c41-a0e4-40d53ea3daba}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="LayoutType.inl" />
</ItemGroup>
<ItemGroup>
<Natvis Include="HybridRow.Native.natvis" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
</Project>

View File

@@ -0,0 +1,291 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ClCompile Include="RowBuffer.cpp" />
<ClCompile Include="LayoutColumn.cpp">
<Filter>Layouts</Filter>
</ClCompile>
<ClCompile Include="Layout.cpp">
<Filter>Layouts</Filter>
</ClCompile>
<ClCompile Include="TypeArgument.cpp">
<Filter>Layouts</Filter>
</ClCompile>
<ClCompile Include="LayoutType.cpp">
<Filter>Layouts</Filter>
</ClCompile>
<ClCompile Include="TypeArgumentList.cpp">
<Filter>Layouts</Filter>
</ClCompile>
<ClCompile Include="StringTokenizer.cpp">
<Filter>Layouts</Filter>
</ClCompile>
<ClCompile Include="RowCursor.cpp" />
<ClCompile Include="Guid.cpp">
<Filter>Scalars</Filter>
</ClCompile>
<ClCompile Include="MongoDbObjectId.cpp">
<Filter>Scalars</Filter>
</ClCompile>
<ClCompile Include="LayoutBuilder.cpp">
<Filter>Layouts</Filter>
</ClCompile>
<ClCompile Include="pch.cpp">
<Filter>Layouts</Filter>
</ClCompile>
<ClCompile Include="LayoutCompiler.cpp">
<Filter>Layouts</Filter>
</ClCompile>
<ClCompile Include="RowReader.cpp">
<Filter>IO</Filter>
</ClCompile>
<ClCompile Include="RowWriter.cpp">
<Filter>IO</Filter>
</ClCompile>
<ClCompile Include="Namespace.cpp">
<Filter>Schemas</Filter>
</ClCompile>
<ClCompile Include="Schema.cpp">
<Filter>Schemas</Filter>
</ClCompile>
<ClCompile Include="LayoutResolverNamespace.cpp">
<Filter>Layouts</Filter>
</ClCompile>
<ClCompile Include="SystemSchema.cpp">
<Filter>Generated</Filter>
</ClCompile>
<ClCompile Include="RecordIOFormatter.cpp">
<Filter>RecordIO</Filter>
</ClCompile>
<ClCompile Include="RecordIOParser.cpp">
<Filter>RecordIO</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="ISpanResizer.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="RowBuffer.h" />
<ClInclude Include="LayoutResolver.h">
<Filter>Layouts</Filter>
</ClInclude>
<ClInclude Include="HybridRowHeader.h" />
<ClInclude Include="HybridRowVersion.h" />
<ClInclude Include="LayoutCode.h">
<Filter>Layouts</Filter>
</ClInclude>
<ClInclude Include="LayoutColumn.h">
<Filter>Layouts</Filter>
</ClInclude>
<ClInclude Include="Layout.h">
<Filter>Layouts</Filter>
</ClInclude>
<ClInclude Include="StorageKind.h">
<Filter>Schemas</Filter>
</ClInclude>
<ClInclude Include="TypeArgument.h">
<Filter>Layouts</Filter>
</ClInclude>
<ClInclude Include="LayoutType.h">
<Filter>Layouts</Filter>
</ClInclude>
<ClInclude Include="TypeArgumentList.h">
<Filter>Layouts</Filter>
</ClInclude>
<ClInclude Include="LayoutBit.h">
<Filter>Layouts</Filter>
</ClInclude>
<ClInclude Include="LayoutCodeTraits.h">
<Filter>Layouts</Filter>
</ClInclude>
<ClInclude Include="SamplingUtf8StringComparer.h">
<Filter>Layouts</Filter>
</ClInclude>
<ClInclude Include="StringTokenizer.h">
<Filter>Layouts</Filter>
</ClInclude>
<ClInclude Include="HybridRow.Native.h" />
<ClInclude Include="framework.h" />
<ClInclude Include="Schema.h">
<Filter>Schemas</Filter>
</ClInclude>
<ClInclude Include="SchemaId.h">
<Filter>Schemas</Filter>
</ClInclude>
<ClInclude Include="SchemaLanguageVersion.h">
<Filter>Schemas</Filter>
</ClInclude>
<ClInclude Include="SchemaOptions.h">
<Filter>Schemas</Filter>
</ClInclude>
<ClInclude Include="TypeKind.h">
<Filter>Schemas</Filter>
</ClInclude>
<ClInclude Include="Property.h">
<Filter>Schemas</Filter>
</ClInclude>
<ClInclude Include="PrimarySortKey.h">
<Filter>Schemas</Filter>
</ClInclude>
<ClInclude Include="PartitionKey.h">
<Filter>Schemas</Filter>
</ClInclude>
<ClInclude Include="SortDirection.h">
<Filter>Schemas</Filter>
</ClInclude>
<ClInclude Include="StaticKey.h">
<Filter>Schemas</Filter>
</ClInclude>
<ClInclude Include="Namespace.h">
<Filter>Schemas</Filter>
</ClInclude>
<ClInclude Include="PropertyType.h">
<Filter>Schemas</Filter>
</ClInclude>
<ClInclude Include="UdtPropertyType.h">
<Filter>Schemas</Filter>
</ClInclude>
<ClInclude Include="TuplePropertyType.h">
<Filter>Schemas</Filter>
</ClInclude>
<ClInclude Include="TaggedPropertyType.h">
<Filter>Schemas</Filter>
</ClInclude>
<ClInclude Include="SetPropertyType.h">
<Filter>Schemas</Filter>
</ClInclude>
<ClInclude Include="ScopePropertyType.h">
<Filter>Schemas</Filter>
</ClInclude>
<ClInclude Include="PrimitivePropertyType.h">
<Filter>Schemas</Filter>
</ClInclude>
<ClInclude Include="ObjectPropertyType.h">
<Filter>Schemas</Filter>
</ClInclude>
<ClInclude Include="MapPropertyType.h">
<Filter>Schemas</Filter>
</ClInclude>
<ClInclude Include="ArrayPropertyType.h">
<Filter>Schemas</Filter>
</ClInclude>
<ClInclude Include="RowCursor.h" />
<ClInclude Include="Result.h" />
<ClInclude Include="RowOptions.h" />
<ClInclude Include="Float128.h">
<Filter>Scalars</Filter>
</ClInclude>
<ClInclude Include="Decimal.h">
<Filter>Scalars</Filter>
</ClInclude>
<ClInclude Include="DateTime.h">
<Filter>Scalars</Filter>
</ClInclude>
<ClInclude Include="UnixDateTime.h">
<Filter>Scalars</Filter>
</ClInclude>
<ClInclude Include="Guid.h">
<Filter>Scalars</Filter>
</ClInclude>
<ClInclude Include="MongoDbObjectId.h">
<Filter>Scalars</Filter>
</ClInclude>
<ClInclude Include="NullValue.h">
<Filter>Scalars</Filter>
</ClInclude>
<ClInclude Include="UpdateOptions.h">
<Filter>Layouts</Filter>
</ClInclude>
<ClInclude Include="LayoutBuilder.h">
<Filter>Layouts</Filter>
</ClInclude>
<ClInclude Include="LayoutCompiler.h">
<Filter>Layouts</Filter>
</ClInclude>
<ClInclude Include="PropertyKind.h">
<Filter>Schemas</Filter>
</ClInclude>
<ClInclude Include="RowReader.h">
<Filter>IO</Filter>
</ClInclude>
<ClInclude Include="RowWriter.h">
<Filter>IO</Filter>
</ClInclude>
<ClInclude Include="IHybridRowSerializer.h">
<Filter>IO</Filter>
</ClInclude>
<ClInclude Include="AllowEmptyKind.h">
<Filter>Schemas</Filter>
</ClInclude>
<ClInclude Include="EnumSchema.h">
<Filter>Schemas</Filter>
</ClInclude>
<ClInclude Include="EnumValue.h">
<Filter>Schemas</Filter>
</ClInclude>
<ClInclude Include="MemorySpanResizer.h" />
<ClInclude Include="LayoutResolverNamespace.h">
<Filter>Layouts</Filter>
</ClInclude>
<ClInclude Include="SystemSchema.h">
<Filter>Generated</Filter>
</ClInclude>
<ClInclude Include="SystemSchemaLiteral.h">
<Filter>Layouts</Filter>
</ClInclude>
<ClInclude Include="TypedArrayHybridRowSerializer.h">
<Filter>Schemas</Filter>
</ClInclude>
<ClInclude Include="ArrayHybridRowSerializer.h">
<Filter>Schemas</Filter>
</ClInclude>
<ClInclude Include="PrimitiveHybridRowSerializer.h">
<Filter>Schemas</Filter>
</ClInclude>
<ClInclude Include="NullableHybridRowSerializer.h">
<Filter>Schemas</Filter>
</ClInclude>
<ClInclude Include="TypedTupleHybridRowSerializer.h">
<Filter>Schemas</Filter>
</ClInclude>
<ClInclude Include="Segment.h">
<Filter>RecordIO</Filter>
</ClInclude>
<ClInclude Include="RecordIOFormatter.h">
<Filter>RecordIO</Filter>
</ClInclude>
<ClInclude Include="RecordIOParser.h">
<Filter>RecordIO</Filter>
</ClInclude>
<ClInclude Include="TypedMapHybridRowSerializer.h">
<Filter>Schemas</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="Layouts">
<UniqueIdentifier>{76203f3c-5cd4-446f-96ec-15092eaec523}</UniqueIdentifier>
</Filter>
<Filter Include="Schemas">
<UniqueIdentifier>{8c7a2985-33b9-41f9-8e05-94b996ccec0f}</UniqueIdentifier>
</Filter>
<Filter Include="Scalars">
<UniqueIdentifier>{39967b35-79dc-4b38-b32f-63b700f28630}</UniqueIdentifier>
</Filter>
<Filter Include="IO">
<UniqueIdentifier>{fa535215-bc4c-447a-a95a-cb99c42bee8a}</UniqueIdentifier>
</Filter>
<Filter Include="Generated">
<UniqueIdentifier>{2a643526-2db3-45dc-aba8-230b152dc26a}</UniqueIdentifier>
</Filter>
<Filter Include="RecordIO">
<UniqueIdentifier>{27651037-e785-4e32-b0ba-3c5617a2d9f1}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<None Include="LayoutType.inl">
<Filter>Layouts</Filter>
</None>
</ItemGroup>
<ItemGroup>
<Natvis Include="HybridRow.Native.natvis" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,53 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------
#pragma once
#include "HybridRowVersion.h"
#include "SchemaId.h"
namespace cdb_hr
{
/// <summary>Describes the header the precedes all valid Hybrid Rows.</summary>
#pragma pack(push, 1)
struct HybridRowHeader final
{
/// <summary>Size (in bytes) of a serialized header.</summary>
constexpr static uint32_t Size = sizeof(HybridRowVersion) + SchemaId::Size;
/// <summary>Initializes a new instance of the <see cref="HybridRowHeader"/> struct.</summary>
constexpr HybridRowHeader() noexcept : m_version{HybridRowVersion::Invalid}, m_schemaId{} {}
~HybridRowHeader() noexcept = default;
HybridRowHeader(const HybridRowHeader& other) noexcept = default;
HybridRowHeader(HybridRowHeader&& other) noexcept = default;
HybridRowHeader& operator=(const HybridRowHeader& other) noexcept = default;
HybridRowHeader& operator=(HybridRowHeader&& other) noexcept = default;
/// <summary>Initializes a new instance of the <see cref="HybridRowHeader"/> struct.</summary>
/// <param name="version">The version of the HybridRow library used to write this row.</param>
/// <param name="schemaId">The unique identifier of the schema whose layout was used to write this row.</param>
constexpr HybridRowHeader(HybridRowVersion version, SchemaId schemaId);
/// <summary>The version of the HybridRow library used to write this row.</summary>
[[nodiscard]]
HybridRowVersion GetVersion() const noexcept;
/// <summary>The unique identifier of the schema whose layout was used to write this row.</summary>
[[nodiscard]]
SchemaId GetSchemaId() const noexcept;
private:
HybridRowVersion m_version;
SchemaId m_schemaId;
};
#pragma pack(pop)
static_assert(cdb_core::is_blittable_v<HybridRowHeader>);
constexpr HybridRowHeader::HybridRowHeader(HybridRowVersion version, SchemaId schemaId) :
m_version(version),
m_schemaId(schemaId) {}
inline HybridRowVersion HybridRowHeader::GetVersion() const noexcept { return m_version; }
inline SchemaId HybridRowHeader::GetSchemaId() const noexcept { return m_schemaId; }
}

Some files were not shown because too many files have changed in this diff Show More