mirror of
https://github.com/microsoft/HybridRow.git
synced 2026-01-21 18:33:02 +00:00
Release 1.1.0-preview 3 (#6)
Release roll-up snapshot of C#/C++ codebase at version 1.1.0-preview. This release matches the current shipping nugets.
This commit is contained in:
457
src/CodeAnalysis.ruleset
Normal file
457
src/CodeAnalysis.ruleset
Normal 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>
|
||||
75
src/Core/Core.Native.Tests.Unit/Base64UnitTests.cpp
Normal file
75
src/Core/Core.Native.Tests.Unit/Base64UnitTests.cpp
Normal file
@@ -0,0 +1,75 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#include "pch.h"
|
||||
#include <array>
|
||||
#include <random>
|
||||
#include "CppUnitTest.h"
|
||||
|
||||
namespace cdb_core_test
|
||||
{
|
||||
using namespace std::literals;
|
||||
using Assert = Microsoft::VisualStudio::CppUnitTestFramework::Assert;
|
||||
using Logger = Microsoft::VisualStudio::CppUnitTestFramework::Logger;
|
||||
|
||||
TEST_CLASS(Base64UnitTests)
|
||||
{
|
||||
public:
|
||||
|
||||
template<typename TChar>
|
||||
void Roundtrip()
|
||||
{
|
||||
std::array<byte, 200> bytes{};
|
||||
std::array<byte, 200> bytes2{};
|
||||
std::array<TChar, cdb_core::Base64::GetEncodeRequiredLength(static_cast<uint32_t>(bytes.size()))> chars{};
|
||||
|
||||
std::random_device seedGenerator{};
|
||||
std::mt19937 rand{seedGenerator()};
|
||||
const std::uniform_int_distribution<int> distribution{};
|
||||
|
||||
cdb_core::Span<int> bb = cdb_core::MemoryMarshal::Cast<byte, int>(cdb_core::Span{bytes});
|
||||
for (auto& b : bb)
|
||||
{
|
||||
b = distribution(rand);
|
||||
}
|
||||
std::fill(chars.begin(), chars.end(), '\0');
|
||||
|
||||
cdb_core::Span<TChar> encoded = cdb_core::Base64::Encode(bytes, cdb_core::Span{chars});
|
||||
Logger::WriteMessage(&encoded[0]);
|
||||
cdb_core::Span<byte> decoded = cdb_core::Base64::Decode(cdb_core::ReadOnlySpan{encoded}, cdb_core::Span{bytes2});
|
||||
|
||||
Assert::AreEqual(static_cast<uint32_t>(bytes.size()), decoded.Length());
|
||||
for (uint32_t i = 0; i < decoded.Length(); i++)
|
||||
{
|
||||
Assert::AreEqual(bytes[i], decoded[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether a byte string properly round-trips as Base64 text.
|
||||
/// </summary>
|
||||
TEST_METHOD(RoundtripA)
|
||||
{
|
||||
Roundtrip<char>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether a byte string properly round-trips as Base64 wide text.
|
||||
/// </summary>
|
||||
TEST_METHOD(RoundtripW)
|
||||
{
|
||||
Roundtrip<wchar_t>();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Additional string function for test diagnostics.
|
||||
namespace Microsoft::VisualStudio::CppUnitTestFramework
|
||||
{
|
||||
template<>
|
||||
inline std::wstring ToString<std::byte>(const std::byte& q)
|
||||
{
|
||||
return cdb_core::make_string(L"{%u}", q);
|
||||
}
|
||||
}
|
||||
45
src/Core/Core.Native.Tests.Unit/ContractUnitTests.cpp
Normal file
45
src/Core/Core.Native.Tests.Unit/ContractUnitTests.cpp
Normal file
@@ -0,0 +1,45 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#include "pch.h"
|
||||
#include "CppUnitTest.h"
|
||||
|
||||
namespace cdb_core_test
|
||||
{
|
||||
using namespace std::literals;
|
||||
using Assert = Microsoft::VisualStudio::CppUnitTestFramework::Assert;
|
||||
|
||||
TEST_CLASS(ContractUnitTests)
|
||||
{
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the string formatting in Contract works correctly for both
|
||||
/// empty and non-empty strings and views.
|
||||
/// </summary>
|
||||
TEST_METHOD(ContractFormatting)
|
||||
{
|
||||
std::wstring error = cdb_core::Contract::MakeError("Assert", "");
|
||||
Assert::AreEqual(0ull, error.find(L"Assert"s));
|
||||
error = cdb_core::Contract::MakeError("Assert", std::string_view());
|
||||
Assert::AreEqual(0ull, error.find(L"Assert"s));
|
||||
error = cdb_core::Contract::MakeError("Assert", "Some error message.");
|
||||
Assert::AreEqual(0ull, error.find(L"Assert"s));
|
||||
Assert::AreNotEqual(std::string::npos, error.find(L"Some error message."s));
|
||||
|
||||
// ReSharper disable once StringLiteralTypo
|
||||
error = cdb_core::Contract::MakeError("Assert", "Some error message \0 with a null ASDFGHJKL.");
|
||||
Assert::AreEqual(0ull, error.find(L"Assert"s));
|
||||
Assert::AreNotEqual(std::string::npos, error.find(L"Some error message "s));
|
||||
Assert::AreEqual(std::string::npos, error.find(L"ASDFGHJKL"s));
|
||||
|
||||
// ReSharper disable once StringLiteralTypo
|
||||
const std::string_view truncated = "some very long message ASDFGHJKL";
|
||||
error = cdb_core::Contract::MakeError("Assert", truncated.substr(0, 4));
|
||||
Assert::AreEqual(0ull, error.find(L"Assert"s));
|
||||
Assert::AreNotEqual(std::string::npos, error.find(L"some"s));
|
||||
Assert::AreEqual(std::string::npos, error.find(L"ASDFGHJKL"s));
|
||||
}
|
||||
};
|
||||
}
|
||||
162
src/Core/Core.Native.Tests.Unit/Crc32UnitTests.cpp
Normal file
162
src/Core/Core.Native.Tests.Unit/Crc32UnitTests.cpp
Normal file
@@ -0,0 +1,162 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#include "pch.h"
|
||||
#include <array>
|
||||
#include "CppUnitTest.h"
|
||||
|
||||
namespace cdb_core_test
|
||||
{
|
||||
using namespace std::literals;
|
||||
using Assert = Microsoft::VisualStudio::CppUnitTestFramework::Assert;
|
||||
using Logger = Microsoft::VisualStudio::CppUnitTestFramework::Logger;
|
||||
|
||||
TEST_CLASS(Crc32UnitTests)
|
||||
{
|
||||
constexpr static std::array<byte, 7> Sample1
|
||||
{
|
||||
{byte{0x45}, byte{0xB1}, byte{0xD6}, byte{0xC7}, byte{0x81}, byte{0xE1}, byte{0x3F}}
|
||||
};
|
||||
constexpr static std::array<byte, 23> Sample2
|
||||
{
|
||||
{
|
||||
byte{0xE5},
|
||||
byte{0x78},
|
||||
byte{0xBA},
|
||||
byte{0xD5},
|
||||
byte{0x00},
|
||||
byte{0xA2},
|
||||
byte{0x98},
|
||||
byte{0xFE},
|
||||
byte{0xF1},
|
||||
byte{0xEF},
|
||||
byte{0x2A},
|
||||
byte{0x90},
|
||||
byte{0x6B},
|
||||
byte{0xC9},
|
||||
byte{0x85},
|
||||
byte{0x22},
|
||||
byte{0x00},
|
||||
byte{0xA5},
|
||||
byte{0xEC},
|
||||
byte{0x20},
|
||||
byte{0x23},
|
||||
byte{0xF6},
|
||||
byte{0xB2}
|
||||
}
|
||||
};
|
||||
constexpr static std::array<byte, 23> Sample3
|
||||
{
|
||||
{
|
||||
byte{0xC3},
|
||||
byte{0x91},
|
||||
byte{0x0B},
|
||||
byte{0x50},
|
||||
byte{0xAF},
|
||||
byte{0x59},
|
||||
byte{0x5B},
|
||||
byte{0x30},
|
||||
byte{0x24},
|
||||
byte{0xDA},
|
||||
byte{0x22},
|
||||
byte{0x3C},
|
||||
byte{0x30},
|
||||
byte{0xBA},
|
||||
byte{0xDB},
|
||||
byte{0x1C},
|
||||
byte{0x18},
|
||||
byte{0x6F},
|
||||
byte{0xBB},
|
||||
byte{0xE6},
|
||||
byte{0x0B},
|
||||
byte{0x70},
|
||||
byte{0x0E}
|
||||
}
|
||||
};
|
||||
constexpr static std::array<byte, 0> Sample4{};
|
||||
constexpr static std::array<byte, 22> Sample5
|
||||
{
|
||||
byte{0xB4},
|
||||
byte{0xC7},
|
||||
byte{0xDB},
|
||||
byte{0xF4},
|
||||
byte{0x1F},
|
||||
byte{0x18},
|
||||
byte{0xEE},
|
||||
byte{0xC5},
|
||||
byte{0x67},
|
||||
byte{0x12},
|
||||
byte{0x6E},
|
||||
byte{0x96},
|
||||
byte{0x47},
|
||||
byte{0x4E},
|
||||
byte{0x98},
|
||||
byte{0x94},
|
||||
byte{0xFA},
|
||||
byte{0x6B},
|
||||
byte{0x90},
|
||||
byte{0xA6},
|
||||
byte{0x48},
|
||||
byte{0xF2}
|
||||
};
|
||||
|
||||
constexpr static std::array<cdb_core::ReadOnlySpan<byte>, 5> s_samples
|
||||
{
|
||||
{
|
||||
cdb_core::ReadOnlySpan<byte>{Sample1},
|
||||
cdb_core::ReadOnlySpan<byte>{Sample2},
|
||||
cdb_core::ReadOnlySpan<byte>{Sample3},
|
||||
cdb_core::ReadOnlySpan<byte>{Sample4},
|
||||
cdb_core::ReadOnlySpan<byte>{Sample5},
|
||||
}
|
||||
};
|
||||
|
||||
constexpr static uint32_t Expected[]
|
||||
{
|
||||
2786232081u,
|
||||
1744821187u,
|
||||
2853437495u,
|
||||
0u,
|
||||
2029626740u,
|
||||
};
|
||||
|
||||
template<size_t N>
|
||||
static int64_t MeasureLoop(std::array<cdb_core::ReadOnlySpan<byte>, N> samples)
|
||||
{
|
||||
const int outerLoopCount = 10000;
|
||||
cdb_core::Stopwatch watch{};
|
||||
|
||||
watch.Start();
|
||||
for (int j = 0; j < outerLoopCount; j++)
|
||||
{
|
||||
for (auto sample : samples)
|
||||
{
|
||||
[[maybe_unused]] int32_t crc = cdb_core::Crc32::Update(0, sample);
|
||||
}
|
||||
}
|
||||
|
||||
watch.Stop();
|
||||
return watch.ElapsedRaw();
|
||||
}
|
||||
|
||||
BEGIN_TEST_METHOD_ATTRIBUTE(Crc32Check)
|
||||
TEST_OWNER(L"jthunter")
|
||||
END_TEST_METHOD_ATTRIBUTE()
|
||||
|
||||
TEST_METHOD(Crc32Check)
|
||||
{
|
||||
// Warm up the loop and verify correctness.
|
||||
for (size_t i = 0; i < s_samples.size(); i++)
|
||||
{
|
||||
cdb_core::ReadOnlySpan<byte> sample = s_samples[i];
|
||||
uint32_t c1 = cdb_core::Crc32::Update(0, sample);
|
||||
Assert::AreEqual(Expected[i], c1);
|
||||
}
|
||||
|
||||
// Measure performance.
|
||||
int64_t ticks = MeasureLoop(s_samples);
|
||||
Logger::WriteMessage(cdb_core::make_string<std::string>("Crc32: %lld", ticks).c_str());
|
||||
}
|
||||
};
|
||||
}
|
||||
62
src/Core/Core.Native.Tests.Unit/DeepCompareUnitTests.cpp
Normal file
62
src/Core/Core.Native.Tests.Unit/DeepCompareUnitTests.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#include "pch.h"
|
||||
#include "CppUnitTest.h"
|
||||
|
||||
namespace cdb_core_test
|
||||
{
|
||||
using namespace std::literals;
|
||||
using Assert = Microsoft::VisualStudio::CppUnitTestFramework::Assert;
|
||||
|
||||
TEST_CLASS(DeepCompareUnitTests)
|
||||
{
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether deep compare works correctly on object trees.
|
||||
/// </summary>
|
||||
TEST_METHOD(ObjectTrees)
|
||||
{
|
||||
std::vector<int> v1{0, 1, 2, 3};
|
||||
Assert::IsTrue(cdb_core::DeepCompare(v1, v1));
|
||||
Assert::IsTrue(cdb_core::DeepCompare(v1, std::vector<int>{0, 1, 2, 3}));
|
||||
Assert::IsFalse(cdb_core::DeepCompare(v1, std::vector<int>{0, 1, 2, 3, 4}));
|
||||
Assert::IsFalse(cdb_core::DeepCompare(v1, std::vector<int>{0, 1, 2, 4}));
|
||||
Assert::IsFalse(cdb_core::DeepCompare(v1, std::vector<int>{9, 1, 2, 3}));
|
||||
|
||||
std::unique_ptr<int> u1 = std::make_unique<int>(42);
|
||||
Assert::IsTrue(cdb_core::DeepCompare(std::unique_ptr<int>{}, std::unique_ptr<int>{}));
|
||||
Assert::IsTrue(cdb_core::DeepCompare(u1, u1));
|
||||
Assert::IsTrue(cdb_core::DeepCompare(u1, std::make_unique<int>(42)));
|
||||
Assert::IsFalse(cdb_core::DeepCompare(u1, std::make_unique<int>(43)));
|
||||
|
||||
ObjectTree o1{std::move(v1), std::move(u1)};
|
||||
Assert::IsTrue(cdb_core::DeepCompare(o1, o1));
|
||||
Assert::IsTrue(cdb_core::DeepCompare(o1, ObjectTree{{0, 1, 2, 3}, std::make_unique<int>(42)}));
|
||||
Assert::IsFalse(cdb_core::DeepCompare(o1, ObjectTree{{0, 1, 2, 4}, std::make_unique<int>(42)}));
|
||||
Assert::IsFalse(cdb_core::DeepCompare(o1, ObjectTree{{0, 1, 2, 3}, std::make_unique<int>(43)}));
|
||||
}
|
||||
|
||||
struct ObjectTree final
|
||||
{
|
||||
std::vector<int> V;
|
||||
std::unique_ptr<int> U;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
namespace cdb_core
|
||||
{
|
||||
template<>
|
||||
struct DeepComparer<cdb_core_test::DeepCompareUnitTests::ObjectTree>
|
||||
{
|
||||
using value_type = cdb_core_test::DeepCompareUnitTests::ObjectTree;
|
||||
|
||||
bool operator()(const value_type& x, const value_type& y) const noexcept
|
||||
{
|
||||
return cdb_core::DeepCompare(x.V, y.V) && cdb_core::DeepCompare(x.U, y.U);
|
||||
}
|
||||
};
|
||||
}
|
||||
209
src/Core/Core.Native.Tests.Unit/HashCodeUnitTests.cpp
Normal file
209
src/Core/Core.Native.Tests.Unit/HashCodeUnitTests.cpp
Normal file
@@ -0,0 +1,209 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#include "pch.h"
|
||||
#include "CppUnitTest.h"
|
||||
|
||||
namespace cdb_core_test
|
||||
{
|
||||
using namespace std::literals;
|
||||
using Assert = Microsoft::VisualStudio::CppUnitTestFramework::Assert;
|
||||
|
||||
struct ConstHashCodeType {};
|
||||
|
||||
struct ConstComparer
|
||||
{
|
||||
constexpr static size_t ConstantValue = 1234;
|
||||
|
||||
std::size_t operator()(cdb_core_test::ConstHashCodeType const&) const noexcept
|
||||
{
|
||||
return ConstantValue;
|
||||
}
|
||||
};
|
||||
|
||||
TEST_CLASS(HashCodeUnitTests)
|
||||
{
|
||||
public:
|
||||
|
||||
TEST_METHOD(AddHashCode)
|
||||
{
|
||||
cdb_core::HashCode hc1{};
|
||||
hc1.Add("Hello"sv);
|
||||
|
||||
cdb_core::HashCode hc2{};
|
||||
hc2.AddHash(std::hash<std::string_view>{}.operator()("Hello"sv));
|
||||
|
||||
Assert::AreEqual(hc1.ToHashCode(), hc2.ToHashCode());
|
||||
}
|
||||
|
||||
TEST_METHOD(AddGeneric)
|
||||
{
|
||||
cdb_core::HashCode hc{};
|
||||
hc.Add(1);
|
||||
hc.Add(ConstHashCodeType{});
|
||||
|
||||
cdb_core::HashCode expected{};
|
||||
expected.Add(1);
|
||||
expected.AddHash(ConstComparer::ConstantValue);
|
||||
|
||||
Assert::AreEqual(expected.ToHashCode(), hc.ToHashCode());
|
||||
}
|
||||
|
||||
TEST_METHOD(AddNull)
|
||||
{
|
||||
cdb_core::HashCode hc{};
|
||||
hc.Add(static_cast<char*>(nullptr));
|
||||
|
||||
cdb_core::HashCode expected{};
|
||||
expected.AddHash(std::hash<char*>{}.operator()(nullptr));
|
||||
|
||||
Assert::AreEqual(expected.ToHashCode(), hc.ToHashCode());
|
||||
}
|
||||
|
||||
TEST_METHOD(AddGenericEqualityComparer)
|
||||
{
|
||||
cdb_core::HashCode hc{};
|
||||
hc.Add(1);
|
||||
hc.Add<ConstHashCodeType, ConstComparer>(ConstHashCodeType{});
|
||||
|
||||
cdb_core::HashCode expected{};
|
||||
expected.Add(1);
|
||||
expected.AddHash(ConstComparer::ConstantValue);
|
||||
|
||||
Assert::AreEqual(expected.ToHashCode(), hc.ToHashCode());
|
||||
}
|
||||
|
||||
TEST_METHOD(Combine)
|
||||
{
|
||||
std::vector<size_t> hcs =
|
||||
{
|
||||
cdb_core::HashCode::Combine(1),
|
||||
cdb_core::HashCode::Combine(1, 2),
|
||||
cdb_core::HashCode::Combine(1, 2, 3),
|
||||
cdb_core::HashCode::Combine(1, 2, 3, 4),
|
||||
cdb_core::HashCode::Combine(1, 2, 3, 4, 5),
|
||||
cdb_core::HashCode::Combine(1, 2, 3, 4, 5, 6),
|
||||
cdb_core::HashCode::Combine(1, 2, 3, 4, 5, 6, 7),
|
||||
cdb_core::HashCode::Combine(1, 2, 3, 4, 5, 6, 7, 8),
|
||||
|
||||
cdb_core::HashCode::Combine(2),
|
||||
cdb_core::HashCode::Combine(2, 3),
|
||||
cdb_core::HashCode::Combine(2, 3, 4),
|
||||
cdb_core::HashCode::Combine(2, 3, 4, 5),
|
||||
cdb_core::HashCode::Combine(2, 3, 4, 5, 6),
|
||||
cdb_core::HashCode::Combine(2, 3, 4, 5, 6, 7),
|
||||
cdb_core::HashCode::Combine(2, 3, 4, 5, 6, 7, 8),
|
||||
cdb_core::HashCode::Combine(2, 3, 4, 5, 6, 7, 8, 9),
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < hcs.size(); i++)
|
||||
{
|
||||
for (size_t j = 0; j < hcs.size(); j++)
|
||||
{
|
||||
if (i == j)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
Assert::AreNotEqual(hcs[i], hcs[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(CombineAdd1)
|
||||
{
|
||||
cdb_core::HashCode hc{};
|
||||
hc.Add(1);
|
||||
Assert::AreEqual(hc.ToHashCode(), cdb_core::HashCode::Combine(1));
|
||||
}
|
||||
|
||||
TEST_METHOD(CombineAdd2)
|
||||
{
|
||||
cdb_core::HashCode hc{};
|
||||
hc.Add(1);
|
||||
hc.Add(2);
|
||||
Assert::AreEqual(hc.ToHashCode(), cdb_core::HashCode::Combine(1, 2));
|
||||
}
|
||||
|
||||
TEST_METHOD(CombineAdd3)
|
||||
{
|
||||
cdb_core::HashCode hc{};
|
||||
hc.Add(1);
|
||||
hc.Add(2);
|
||||
hc.Add(3);
|
||||
Assert::AreEqual(hc.ToHashCode(), cdb_core::HashCode::Combine(1, 2, 3));
|
||||
}
|
||||
|
||||
TEST_METHOD(CombineAdd4)
|
||||
{
|
||||
cdb_core::HashCode hc{};
|
||||
hc.Add(1);
|
||||
hc.Add(2);
|
||||
hc.Add(3);
|
||||
hc.Add(4);
|
||||
Assert::AreEqual(hc.ToHashCode(), cdb_core::HashCode::Combine(1, 2, 3, 4));
|
||||
}
|
||||
|
||||
TEST_METHOD(CombineAdd5)
|
||||
{
|
||||
cdb_core::HashCode hc{};
|
||||
hc.Add(1);
|
||||
hc.Add(2);
|
||||
hc.Add(3);
|
||||
hc.Add(4);
|
||||
hc.Add(5);
|
||||
Assert::AreEqual(hc.ToHashCode(), cdb_core::HashCode::Combine(1, 2, 3, 4, 5));
|
||||
}
|
||||
|
||||
TEST_METHOD(CombineAdd6)
|
||||
{
|
||||
cdb_core::HashCode hc{};
|
||||
hc.Add(1);
|
||||
hc.Add(2);
|
||||
hc.Add(3);
|
||||
hc.Add(4);
|
||||
hc.Add(5);
|
||||
hc.Add(6);
|
||||
Assert::AreEqual(hc.ToHashCode(), cdb_core::HashCode::Combine(1, 2, 3, 4, 5, 6));
|
||||
}
|
||||
|
||||
TEST_METHOD(CombineAdd7)
|
||||
{
|
||||
cdb_core::HashCode hc{};
|
||||
hc.Add(1);
|
||||
hc.Add(2);
|
||||
hc.Add(3);
|
||||
hc.Add(4);
|
||||
hc.Add(5);
|
||||
hc.Add(6);
|
||||
hc.Add(7);
|
||||
Assert::AreEqual(hc.ToHashCode(), cdb_core::HashCode::Combine(1, 2, 3, 4, 5, 6, 7));
|
||||
}
|
||||
|
||||
TEST_METHOD(CombineAdd8)
|
||||
{
|
||||
cdb_core::HashCode hc{};
|
||||
hc.Add(1);
|
||||
hc.Add(2);
|
||||
hc.Add(3);
|
||||
hc.Add(4);
|
||||
hc.Add(5);
|
||||
hc.Add(6);
|
||||
hc.Add(7);
|
||||
hc.Add(8);
|
||||
Assert::AreEqual(hc.ToHashCode(), cdb_core::HashCode::Combine(1, 2, 3, 4, 5, 6, 7, 8));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
namespace std
|
||||
{
|
||||
template<>
|
||||
struct hash<cdb_core_test::ConstHashCodeType>
|
||||
{
|
||||
std::size_t operator()(cdb_core_test::ConstHashCodeType const&) const noexcept
|
||||
{
|
||||
return cdb_core_test::ConstComparer::ConstantValue;
|
||||
}
|
||||
};
|
||||
}
|
||||
230
src/Core/Core.Native.Tests.Unit/MemoryUnitTests.cpp
Normal file
230
src/Core/Core.Native.Tests.Unit/MemoryUnitTests.cpp
Normal file
@@ -0,0 +1,230 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#include "pch.h"
|
||||
#include "CppUnitTest.h"
|
||||
|
||||
namespace cdb_core_test
|
||||
{
|
||||
using Assert = Microsoft::VisualStudio::CppUnitTestFramework::Assert;
|
||||
using Logger = Microsoft::VisualStudio::CppUnitTestFramework::Logger;
|
||||
|
||||
template<class T>
|
||||
void TestRange(T m, int start, int length)
|
||||
{
|
||||
int i = 0;
|
||||
for (auto b : m.AsSpan())
|
||||
{
|
||||
Assert::AreEqual(static_cast<byte>(i + start), b);
|
||||
i++;
|
||||
}
|
||||
Assert::AreEqual(length, i);
|
||||
Assert::AreEqual(static_cast<uint32_t>(length), m.Length());
|
||||
Assert::AreEqual(length == 0, m.IsEmpty());
|
||||
}
|
||||
|
||||
TEST_CLASS(MemoryUnitTests)
|
||||
{
|
||||
public:
|
||||
|
||||
TEST_METHOD(SliceTest)
|
||||
{
|
||||
Logger::WriteMessage("Memory<byte>:");
|
||||
cdb_core::Memory<byte> m{new byte[10], 10};
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
m.AsSpan()[i] = static_cast<byte>(i);
|
||||
}
|
||||
TestRange(m, 0, 10);
|
||||
|
||||
{
|
||||
const cdb_core::ReadOnlyMemory<byte> m1 = static_cast<const cdb_core::Memory<byte>&>(m).Slice(5);
|
||||
TestRange(m1, 5, 5);
|
||||
}
|
||||
|
||||
{
|
||||
const auto m2 = m.Slice(5, 2);
|
||||
TestRange(m2, 5, 2);
|
||||
}
|
||||
|
||||
{
|
||||
const auto m3 = m.Slice(10);
|
||||
TestRange(m3, 0, 0);
|
||||
}
|
||||
|
||||
{
|
||||
const auto m4 = m.Slice(0, 0);
|
||||
TestRange(m4, 10, 0);
|
||||
}
|
||||
|
||||
{
|
||||
cdb_core::Memory<byte> m5{new byte[5], 5};
|
||||
m.Slice(5, 5).AsSpan().CopyTo(m5.AsSpan());
|
||||
TestRange(m5, 5, 5);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(CopyTest)
|
||||
{
|
||||
Logger::WriteMessage("Memory<byte>:");
|
||||
cdb_core::Memory<byte> m{new byte[10], 10};
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
m.AsSpan()[i] = static_cast<byte>(i);
|
||||
}
|
||||
TestRange(m, 0, 10);
|
||||
|
||||
{
|
||||
cdb_core::Memory<byte> m2{new byte[2], 2};
|
||||
m.Slice(5, 2).AsSpan().CopyTo(m2.AsSpan());
|
||||
TestRange(m2, 5, 2);
|
||||
}
|
||||
|
||||
{
|
||||
cdb_core::Memory<byte> m3{new byte[0], 0};
|
||||
m.Slice(10).AsSpan().CopyTo(m3.AsSpan());
|
||||
TestRange(m3, 0, 0);
|
||||
}
|
||||
|
||||
{
|
||||
cdb_core::Memory<byte> m3{nullptr, 0};
|
||||
m.Slice(10).AsSpan().CopyTo(m3.AsSpan());
|
||||
TestRange(m3, 0, 0);
|
||||
}
|
||||
|
||||
{
|
||||
cdb_core::Memory<byte> m5{new byte[5], 5};
|
||||
m.Slice(5, 5).AsSpan().CopyTo(m5.AsSpan());
|
||||
TestRange(m5, 5, 5);
|
||||
}
|
||||
|
||||
{
|
||||
cdb_core::Memory<byte> m6{m.AsSpan()};
|
||||
TestRange(m6, 0, 10);
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<byte> v{};
|
||||
v.reserve(m.Length());
|
||||
for (auto b : m.AsSpan()) { v.emplace_back(b); }
|
||||
cdb_core::Memory<byte> m7{v};
|
||||
TestRange(m7, 0, 10);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(CastTest)
|
||||
{
|
||||
const int num = 3;
|
||||
cdb_core::Memory<int32_t> m{new int32_t[num], static_cast<uint32_t>(num)};
|
||||
for (int i = 0; i < num; i++)
|
||||
{
|
||||
m.AsSpan()[i] = i + 1;
|
||||
}
|
||||
|
||||
cdb_core::Span<byte> mb = cdb_core::MemoryMarshal::Cast<int32_t, byte>(m.AsSpan());
|
||||
byte expectedBytes[] = {
|
||||
static_cast<byte>(1),
|
||||
static_cast<byte>(0),
|
||||
static_cast<byte>(0),
|
||||
static_cast<byte>(0),
|
||||
static_cast<byte>(2),
|
||||
static_cast<byte>(0),
|
||||
static_cast<byte>(0),
|
||||
static_cast<byte>(0),
|
||||
static_cast<byte>(3),
|
||||
static_cast<byte>(0),
|
||||
static_cast<byte>(0),
|
||||
static_cast<byte>(0),
|
||||
};
|
||||
for (size_t i = 0; i < std::size(expectedBytes); i++)
|
||||
{
|
||||
Assert::AreEqual(expectedBytes[i], mb[static_cast<uint32_t>(i)], cdb_core::make_string<std::wstring>(L"i = %llu", i).c_str());
|
||||
}
|
||||
Assert::AreEqual(std::size(expectedBytes), static_cast<size_t>(mb.Length()));
|
||||
|
||||
cdb_core::Span<int64_t> m64 = cdb_core::MemoryMarshal::Cast<int32_t, int64_t>(m.AsSpan());
|
||||
int64_t expected64[] = {static_cast<int64_t>(0x200000001)};
|
||||
for (size_t i = 0; i < std::size(expected64); i++)
|
||||
{
|
||||
Assert::AreEqual(expected64[i], m64[static_cast<uint32_t>(i)], cdb_core::make_string<std::wstring>(L"i = %llu", i).c_str());
|
||||
}
|
||||
Assert::AreEqual(std::size(expected64), static_cast<size_t>(m64.Length()));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void MemoryEqualityTest(T a, T b, T c, T d)
|
||||
{
|
||||
// ReSharper disable once CppIdenticalOperandsInBinaryExpression
|
||||
Assert::IsTrue(a == a);
|
||||
Assert::AreNotEqual(a, b);
|
||||
Assert::IsFalse(a == b);
|
||||
Assert::IsTrue(a != b);
|
||||
Assert::AreEqual(a, c);
|
||||
Assert::IsTrue(a == c);
|
||||
Assert::IsFalse(a != c);
|
||||
Assert::AreNotEqual(a, d);
|
||||
Assert::IsFalse(a == d);
|
||||
Assert::IsTrue(a != d);
|
||||
}
|
||||
|
||||
TEST_METHOD(EqualityTest)
|
||||
{
|
||||
cdb_core::Memory<std::byte> a = cdb_core::Memory<std::byte>{5};
|
||||
cdb_core::Memory<std::byte> b = cdb_core::Memory<std::byte>{5};
|
||||
cdb_core::Memory<std::byte> c = a.Slice(0);
|
||||
cdb_core::Memory<std::byte> d = a.Slice(1);
|
||||
|
||||
MemoryEqualityTest(a, b, c, d);
|
||||
MemoryEqualityTest(a.AsSpan(), b.AsSpan(), c.AsSpan(), d.AsSpan());
|
||||
MemoryEqualityTest(
|
||||
cdb_core::ReadOnlySpan<byte>(a.AsSpan()),
|
||||
cdb_core::ReadOnlySpan<byte>(b.AsSpan()),
|
||||
cdb_core::ReadOnlySpan<byte>(c.AsSpan()),
|
||||
cdb_core::ReadOnlySpan<byte>(d.AsSpan()));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
namespace Microsoft::VisualStudio::CppUnitTestFramework
|
||||
{
|
||||
template<> inline std::wstring ToString<std::byte>(const std::byte& q)
|
||||
{
|
||||
RETURN_WIDE_STRING(static_cast<int>(q));
|
||||
}
|
||||
|
||||
template<> inline std::wstring ToString<std::byte>(const std::byte* q)
|
||||
{
|
||||
RETURN_WIDE_STRING(q);
|
||||
}
|
||||
}
|
||||
|
||||
namespace Microsoft::VisualStudio::CppUnitTestFramework
|
||||
{
|
||||
template<>
|
||||
std::wstring ToString<cdb_core::Memory<std::byte>>(
|
||||
const cdb_core::Memory<std::byte>& q)
|
||||
{
|
||||
return cdb_core::make_string(L"{p:%p l:%u}", &q.AsSpan()[0], q.Length());
|
||||
}
|
||||
}
|
||||
|
||||
namespace Microsoft::VisualStudio::CppUnitTestFramework
|
||||
{
|
||||
template<>
|
||||
std::wstring ToString<cdb_core::Span<std::byte>>(
|
||||
const cdb_core::Span<std::byte>& q)
|
||||
{
|
||||
return cdb_core::make_string(L"{p:%p l:%u}", &q[0], q.Length());
|
||||
}
|
||||
}
|
||||
|
||||
namespace Microsoft::VisualStudio::CppUnitTestFramework
|
||||
{
|
||||
template<>
|
||||
std::wstring ToString<cdb_core::ReadOnlySpan<std::byte>>(
|
||||
const cdb_core::ReadOnlySpan<std::byte>& q)
|
||||
{
|
||||
return cdb_core::make_string(L"{p:%p l:%u}", &q[0], q.Length());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>16.0</VCProjectVersion>
|
||||
<ProjectGuid>{BE99633B-5D7D-41DA-8550-035E6689B243}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>cdb_core_tests</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
<ProjectSubType>NativeUnitTestProject</ProjectSubType>
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<UseOfMfc>false</UseOfMfc>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<UseOfMfc>false</UseOfMfc>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>$(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<AdditionalLibraryDirectories>$(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>$(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<AdditionalLibraryDirectories>$(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Base64UnitTests.cpp" />
|
||||
<ClCompile Include="ContractUnitTests.cpp" />
|
||||
<ClCompile Include="Crc32UnitTests.cpp" />
|
||||
<ClCompile Include="DeepCompareUnitTests.cpp" />
|
||||
<ClCompile Include="HashCodeUnitTests.cpp" />
|
||||
<ClCompile Include="MemoryUnitTests.cpp" />
|
||||
<ClCompile Include="TimeSpanTests.cpp" />
|
||||
<ClCompile Include="TlaAllocatorUnitTests.cpp" />
|
||||
<ClCompile Include="StringsUnitTests.cpp" />
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Core.Native\Microsoft.Azure.Cosmos.Core.Native.vcxproj">
|
||||
<Project>{eaed7d41-3de6-4c41-a0e4-40d53ea3daba}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
</Project>
|
||||
111
src/Core/Core.Native.Tests.Unit/StringsUnitTests.cpp
Normal file
111
src/Core/Core.Native.Tests.Unit/StringsUnitTests.cpp
Normal file
@@ -0,0 +1,111 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#include "pch.h"
|
||||
#include "CppUnitTest.h"
|
||||
|
||||
namespace cdb_core_test
|
||||
{
|
||||
using namespace std::literals;
|
||||
using Assert = Microsoft::VisualStudio::CppUnitTestFramework::Assert;
|
||||
using Logger = Microsoft::VisualStudio::CppUnitTestFramework::Logger;
|
||||
|
||||
TEST_CLASS(StringsUnitTests)
|
||||
{
|
||||
public:
|
||||
/// <summary>
|
||||
/// Verifies the code samples used in the Doc Comments actually compile.
|
||||
/// </summary>
|
||||
TEST_METHOD(DocCommentsExamples)
|
||||
{
|
||||
{
|
||||
std::string u8 = cdb_core::make_string("foo: %s", "some value");
|
||||
std::wstring u16 = cdb_core::string_join(L"foo: %s", L"some wide-string value");
|
||||
tla::string s = cdb_core::string_join("foo: %s", "some value");
|
||||
}
|
||||
|
||||
{
|
||||
Logger::WriteMessage(cdb_core::make_string<std::string>("foo: %s", "some value").c_str());
|
||||
Logger::WriteMessage(cdb_core::make_string<std::wstring>(L"foo: %s", L"some wide-string value").c_str());
|
||||
}
|
||||
|
||||
{
|
||||
std::string u8 = cdb_core::string_join("a", "b", "c");
|
||||
std::wstring u16 = cdb_core::string_join(L"a", L"b", L"c");
|
||||
tla::string s = cdb_core::string_join("a", "b", "c");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that make_string inserts the arguments in the proper order.
|
||||
/// </summary>
|
||||
TEST_METHOD(MakeStringOrderTest)
|
||||
{
|
||||
MakeStringOrder<std::string>("%s %s %s", "a", "b", "c", "a b c");
|
||||
MakeStringOrder<std::wstring>(L"%s %s %s", L"a", L"b", L"c", L"a b c");
|
||||
MakeStringOrder<tla::string>("%s %s %s", "a", "b", "c", "a b c");
|
||||
MakeStringOrder<tla::wstring>(L"%s %s %s", L"a", L"b", L"c", L"a b c");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that make_string properly supports string precision format specifiers.
|
||||
/// </summary>
|
||||
TEST_METHOD(MakeStringPrecisionTest)
|
||||
{
|
||||
MakeStringPrecision<std::string>("a %.*s c", "b"sv, "a b c");
|
||||
MakeStringPrecision<std::wstring>(L"a %.*s c", L"b"sv, L"a b c");
|
||||
MakeStringPrecision<tla::string>("a %.*s c", "b"sv, "a b c");
|
||||
MakeStringPrecision<tla::wstring>(L"a %.*s c", L"b"sv, L"a b c");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that string join return type inference works across various return types.
|
||||
/// </summary>
|
||||
TEST_METHOD(StringJoinTest)
|
||||
{
|
||||
StringJoin<std::string>("a", "b", "c", "abc");
|
||||
StringJoin<std::wstring>(L"a", L"b", L"c", L"abc");
|
||||
StringJoin<tla::string>("a", "b", "c", "abc");
|
||||
StringJoin<tla::wstring>(L"a", L"b", L"c", L"abc");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that string join accepts heterogeneous argument types as long as the return
|
||||
/// value has an append operator with that rvalue.
|
||||
/// </summary>
|
||||
TEST_METHOD(HeterogeneousStringJoinTest)
|
||||
{
|
||||
const std::string u8 = cdb_core::string_join("a", "b"s, "c"sv);
|
||||
Assert::AreEqual("abc", u8.c_str());
|
||||
const tla::string s = cdb_core::string_join("a", "b"s, "c"sv);
|
||||
Assert::AreEqual("abc", s.c_str());
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename TString, typename TElem = typename TString::value_type>
|
||||
void StringJoin(const TElem* a, const TElem* b, const TElem* c, const TElem* expected)
|
||||
{
|
||||
Logger::WriteMessage(cdb_core::make_string<std::string>("%s\n", typeid(TString).name()).c_str());
|
||||
TString abc = cdb_core::string_join(a, b, c);
|
||||
Assert::AreEqual(expected, abc.c_str());
|
||||
|
||||
TString abc2 = cdb_core::string_join(TString(a), TString(b), TString(c));
|
||||
Assert::AreEqual(expected, abc2.c_str());
|
||||
}
|
||||
|
||||
template<typename TString, typename TElem = typename TString::value_type>
|
||||
void MakeStringOrder(const TElem* format, const TElem* a, const TElem* b, const TElem* c, const TElem* expected)
|
||||
{
|
||||
TString s = cdb_core::make_string<TString>(format, a, b, c);
|
||||
Assert::AreEqual(expected, s.data());
|
||||
}
|
||||
|
||||
template<typename TString, typename TElem = typename TString::value_type>
|
||||
void MakeStringPrecision(const TElem* format, std::basic_string_view<TElem> arg, const TElem* expected)
|
||||
{
|
||||
TString s = cdb_core::make_string<TString>(format, arg.size(), arg.data());
|
||||
Assert::AreEqual(expected, s.data());
|
||||
}
|
||||
};
|
||||
}
|
||||
41
src/Core/Core.Native.Tests.Unit/TimeSpanTests.cpp
Normal file
41
src/Core/Core.Native.Tests.Unit/TimeSpanTests.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#include "pch.h"
|
||||
#include "CppUnitTest.h"
|
||||
|
||||
namespace cdb_core_test
|
||||
{
|
||||
using namespace std::literals;
|
||||
using Assert = Microsoft::VisualStudio::CppUnitTestFramework::Assert;
|
||||
|
||||
TEST_CLASS(TimeSpanTests)
|
||||
{
|
||||
TEST_METHOD(TestTimeSpanCreationMilliseconds)
|
||||
{
|
||||
for (int64_t i = INT16_MIN; i < INT16_MAX; i += 97)
|
||||
{
|
||||
cdb_core::TimeSpan timeSpan = cdb_core::TimeSpan::FromMilliseconds(i);
|
||||
Assert::AreEqual(i, timeSpan.GetTotalMilliseconds());
|
||||
Assert::AreEqual(i / 1000, timeSpan.GetTotalSeconds());
|
||||
|
||||
// 10,000 ticks in 1 ms
|
||||
Assert::AreEqual(i * 10000, timeSpan.GetTotalTicks());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(TestTimeSpanCreationSeconds)
|
||||
{
|
||||
for (int64_t i = INT16_MIN; i < INT16_MAX; i += 137)
|
||||
{
|
||||
cdb_core::TimeSpan timeSpan = cdb_core::TimeSpan::FromSeconds(i);
|
||||
Assert::AreEqual(i * 1000, timeSpan.GetTotalMilliseconds());
|
||||
Assert::AreEqual(i, timeSpan.GetTotalSeconds());
|
||||
|
||||
// 10,000,000 ticks in 1s
|
||||
Assert::AreEqual(i * 10000000, timeSpan.GetTotalTicks());
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
41
src/Core/Core.Native.Tests.Unit/TlaAllocatorUnitTests.cpp
Normal file
41
src/Core/Core.Native.Tests.Unit/TlaAllocatorUnitTests.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#include "pch.h"
|
||||
#include "CppUnitTest.h"
|
||||
|
||||
namespace cdb_core_test
|
||||
{
|
||||
using namespace tla::literals;
|
||||
using Assert = Microsoft::VisualStudio::CppUnitTestFramework::Assert;
|
||||
using Logger = Microsoft::VisualStudio::CppUnitTestFramework::Logger;
|
||||
|
||||
TEST_CLASS(TlaAllocatorUnitTests)
|
||||
{
|
||||
public:
|
||||
TEST_METHOD(StringTest) noexcept
|
||||
{
|
||||
const tla::string abc = cdb_core::string_join("a", "b", "c"_s);
|
||||
Assert::AreEqual("abc", abc.c_str());
|
||||
|
||||
const tla::string abc2 = cdb_core::string_join(tla::string("a"), tla::string("b"), tla::string("c"));
|
||||
Assert::AreEqual("abc", abc2.c_str());
|
||||
|
||||
const tla::wstring abc3 = cdb_core::string_join(tla::wstring(L"a"), tla::wstring(L"b"), tla::wstring(L"c"));
|
||||
Assert::AreEqual(L"abc", abc3.c_str());
|
||||
|
||||
struct T
|
||||
{
|
||||
static size_t GetHashCode() noexcept { return 0; }
|
||||
};
|
||||
|
||||
Logger::WriteMessage(typeid(&T::GetHashCode).name());
|
||||
Logger::WriteMessage("\n");
|
||||
Logger::WriteMessage(typeid(size_t (T::*)() const noexcept).name());
|
||||
Logger::WriteMessage("\n");
|
||||
const bool s = std::is_same<decltype(&T::GetHashCode), size_t (T::*)() const noexcept>::value;
|
||||
Logger::WriteMessage(cdb_core::make_string<tla::string>("%s\n", s ? "true" : "false").c_str());
|
||||
}
|
||||
};
|
||||
}
|
||||
5
src/Core/Core.Native.Tests.Unit/pch.cpp
Normal file
5
src/Core/Core.Native.Tests.Unit/pch.cpp
Normal file
@@ -0,0 +1,5 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#include "pch.h"
|
||||
11
src/Core/Core.Native.Tests.Unit/pch.h
Normal file
11
src/Core/Core.Native.Tests.Unit/pch.h
Normal file
@@ -0,0 +1,11 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
using std::byte;
|
||||
|
||||
#include "../Core.Native/Core.Native.h"
|
||||
557
src/Core/Core.Native/Base64.h
Normal file
557
src/Core/Core.Native/Base64.h
Normal file
@@ -0,0 +1,557 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
#include "Contract.h"
|
||||
#include "ReadOnlySpan.h"
|
||||
#include "Span.h"
|
||||
|
||||
namespace cdb_core
|
||||
{
|
||||
template<class T> struct ReadOnlySpan;
|
||||
template<class T> struct Span;
|
||||
|
||||
struct Base64 final
|
||||
{
|
||||
enum class Flags
|
||||
{
|
||||
None = 0,
|
||||
NoPad = 1,
|
||||
NoLinefeed = 2,
|
||||
Url = 4,
|
||||
};
|
||||
|
||||
// length functions
|
||||
constexpr static uint32_t GetEncodeRequiredLength(uint32_t srcLen, Flags flags = Flags::None) noexcept;
|
||||
template<class TElem, class TTraits>
|
||||
constexpr static uint32_t GetDecodeRequiredLength(std::basic_string_view<TElem, TTraits> src) noexcept;
|
||||
constexpr static uint32_t GetDecodeRequiredLength(uint32_t srcLen) noexcept;
|
||||
|
||||
/// <summary>
|
||||
/// Converts a sequence of bytes into a Base64 text string.
|
||||
/// </summary>
|
||||
/// <typeparam name="TChar">The kind of text element.</typeparam>
|
||||
/// <param name="src">The source bytes to convert.</param>
|
||||
/// <param name="dest">A buffer to receive the text string. This buffer MUST be at least as large as indicated by <see cref="GetEncodeRequiredLength" />.</param>
|
||||
/// <param name="flags">Optional flags</param>
|
||||
/// <returns>A sub-span over <paramref name="dest"/> containing the encoded string.</returns>
|
||||
template<typename TChar>
|
||||
constexpr static Span<TChar> Encode(ReadOnlySpan<std::byte> src, Span<TChar> dest,
|
||||
Flags flags = Flags::None) noexcept;
|
||||
|
||||
/// <summary>
|
||||
/// Converts a Base64 text string back into a sequence of bytes.
|
||||
/// </summary>
|
||||
/// <typeparam name="TChar">The kind of text element.</typeparam>
|
||||
/// <param name="src">The source Base64 string to convert.</param>
|
||||
/// <param name="dest">A buffer to receive the byte sequence. This buffer MUST be at least as large as indicated by <see cref="GetDecodeRequiredLength" />.</param>
|
||||
/// <param name="flags">Optional flags</param>
|
||||
/// <returns>A sub-span over <paramref name="dest"/> containing the encoded string.</returns>
|
||||
template<class TElem, class TTraits>
|
||||
constexpr static Span<std::byte> Decode(std::basic_string_view<TElem, TTraits> src, Span<std::byte> dest,
|
||||
Flags flags = Flags::None) noexcept;
|
||||
|
||||
/// <summary>
|
||||
/// Converts a Base64 text string back into a sequence of bytes.
|
||||
/// </summary>
|
||||
/// <typeparam name="TChar">The kind of text element.</typeparam>
|
||||
/// <param name="src">The source Base64 string to convert.</param>
|
||||
/// <param name="dest">A buffer to receive the byte sequence. This buffer MUST be at least as large as indicated by <see cref="GetDecodeRequiredLength" />.</param>
|
||||
/// <param name="flags">Optional flags</param>
|
||||
/// <returns>A sub-span over <paramref name="dest"/> containing the encoded string.</returns>
|
||||
template<typename TChar>
|
||||
constexpr static Span<std::byte> Decode(ReadOnlySpan<TChar> src, Span<std::byte> dest,
|
||||
Flags flags = Flags::None) noexcept;
|
||||
private:
|
||||
// Helper functions
|
||||
// Overloaded function to get encoding characters
|
||||
// i from 0 to 63 -> base64 encoding character
|
||||
// i = 64 '='
|
||||
// i = 65 '\r'
|
||||
// i = 66 '\n'
|
||||
// i = 67 '\0'
|
||||
template<typename TChar> constexpr static TChar EncodeBase64Char(std::byte nIndex, Flags flags) noexcept;
|
||||
template<typename TChar> constexpr static int DecodeBase64Char(TChar ch, Flags flags) noexcept;
|
||||
};
|
||||
|
||||
template<typename TChar> constexpr TChar Base64::EncodeBase64Char(std::byte nIndex, Flags flags) noexcept
|
||||
{
|
||||
// ReSharper disable once CppStaticAssertFailure
|
||||
static_assert(false, "Unknown TChar");
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<typename TChar> constexpr int Base64::DecodeBase64Char(const TChar ch, Flags flags) noexcept
|
||||
{
|
||||
// ReSharper disable once CppStaticAssertFailure
|
||||
static_assert(false, "Unknown TChar");
|
||||
return 0;
|
||||
}
|
||||
|
||||
constexpr Base64::Flags operator|(Base64::Flags a, Base64::Flags b) noexcept
|
||||
{
|
||||
return static_cast<Base64::Flags>(static_cast<int>(a) | static_cast<int>(b));
|
||||
}
|
||||
|
||||
constexpr Base64::Flags operator&(Base64::Flags a, Base64::Flags b) noexcept
|
||||
{
|
||||
return static_cast<Base64::Flags>(static_cast<int>(a) & static_cast<int>(b));
|
||||
}
|
||||
|
||||
template<typename TChar>
|
||||
constexpr Span<TChar> Base64::Encode(ReadOnlySpan<std::byte> src, Span<TChar> dest, Flags flags) noexcept
|
||||
{
|
||||
Contract::Requires(dest.Length() >= GetEncodeRequiredLength(src.Length(), flags));
|
||||
|
||||
const std::byte* pSrc = &src[0];
|
||||
int srcLen = src.Length();
|
||||
TChar* pDest = &dest[0];
|
||||
|
||||
int written(0);
|
||||
int len1((srcLen / 3) * 4);
|
||||
int len2(len1 / 76);
|
||||
int len3(19);
|
||||
|
||||
for (int i = 0; i <= len2; i++)
|
||||
{
|
||||
if (i == len2)
|
||||
{
|
||||
len3 = (len1 % 76) / 4;
|
||||
}
|
||||
|
||||
for (int j = 0; j < len3; j++)
|
||||
{
|
||||
uint32_t current(0);
|
||||
for (int n = 0; n < 3; n++)
|
||||
{
|
||||
current |= static_cast<uint32_t>(*pSrc++);
|
||||
current <<= 8;
|
||||
}
|
||||
for (int k = 0; k < 4; k++)
|
||||
{
|
||||
std::byte b = static_cast<std::byte>(current >> 26);
|
||||
*pDest = EncodeBase64Char<TChar>(b, flags);
|
||||
++pDest;
|
||||
current <<= 6;
|
||||
}
|
||||
}
|
||||
written += len3 * 4;
|
||||
|
||||
if ((flags & Flags::NoLinefeed) == Flags::None)
|
||||
{
|
||||
// Insert \r\n here
|
||||
*pDest = EncodeBase64Char<TChar>(byte{65}, Flags::None);
|
||||
++pDest;
|
||||
*pDest = EncodeBase64Char<TChar>(byte{66}, Flags::None);
|
||||
++pDest;
|
||||
written += 2;
|
||||
}
|
||||
}
|
||||
|
||||
if ((written != 0) && ((flags & Flags::NoLinefeed) == Flags::None))
|
||||
{
|
||||
pDest -= 2;
|
||||
written -= 2;
|
||||
}
|
||||
|
||||
len2 = (srcLen % 3) ? (srcLen % 3 + 1) : 0;
|
||||
if (len2)
|
||||
{
|
||||
uint32_t current(0);
|
||||
for (int n = 0; n < 3; n++)
|
||||
{
|
||||
if (n < (srcLen % 3))
|
||||
{
|
||||
current |= static_cast<uint32_t>(*pSrc++);
|
||||
}
|
||||
current <<= 8;
|
||||
}
|
||||
for (int k = 0; k < len2; k++)
|
||||
{
|
||||
std::byte b = static_cast<std::byte>(current >> 26);
|
||||
*pDest = EncodeBase64Char<TChar>(b, flags);
|
||||
++pDest;
|
||||
current <<= 6;
|
||||
}
|
||||
written += len2;
|
||||
if ((flags & Flags::NoPad) == Flags::None)
|
||||
{
|
||||
len3 = len2 ? 4 - len2 : 0;
|
||||
for (int j = 0; j < len3; j++)
|
||||
{
|
||||
// Insert '=' here
|
||||
*pDest = EncodeBase64Char<TChar>(byte{64}, flags);
|
||||
++pDest;
|
||||
}
|
||||
written += len3;
|
||||
}
|
||||
}
|
||||
|
||||
return dest.Slice(0, written);
|
||||
}
|
||||
|
||||
template<class TElem, class TTraits>
|
||||
constexpr Span<std::byte> Base64::Decode(std::basic_string_view<TElem, TTraits> src, Span<std::byte> dest,
|
||||
Flags flags) noexcept
|
||||
{
|
||||
Contract::Requires(static_cast<uint64_t>(src.size()) <= UINT32_MAX);
|
||||
return Base64::Decode(ReadOnlySpan<TElem>{src.data(), static_cast<uint32_t>(src.size())}, dest, flags);
|
||||
}
|
||||
|
||||
template<typename TChar>
|
||||
constexpr Span<std::byte> Base64::Decode(ReadOnlySpan<TChar> src, Span<std::byte> dest, Flags flags) noexcept
|
||||
{
|
||||
const TChar* pSrc = &src[0];
|
||||
std::byte* pDest = &dest[0];
|
||||
|
||||
// Walk the source buffer, each four character sequence is converted to 3 bytes.
|
||||
// CRLFs and =, and any characters not in the encoding table are skipped.
|
||||
const TChar* pEnd = pSrc + src.Length();
|
||||
uint32_t written = 0;
|
||||
while (pSrc < pEnd && (*pSrc) != 0)
|
||||
{
|
||||
uint32_t current = 0;
|
||||
uint32_t bits = 0;
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
if (pSrc >= pEnd)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
int ch = DecodeBase64Char<TChar>(*pSrc, flags);
|
||||
++pSrc;
|
||||
if (ch == -1)
|
||||
{
|
||||
// skip this char
|
||||
// TODO: Change signature of method to support failure.
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
current <<= 6;
|
||||
current |= ch;
|
||||
bits += 6;
|
||||
}
|
||||
|
||||
Contract::Invariant(written + (bits / 8) <= dest.Length());
|
||||
|
||||
// current has the 3 bytes to write to the output buffer, left to right
|
||||
current <<= 24 - bits;
|
||||
for (uint32_t i = 0; i < bits / 8; i++)
|
||||
{
|
||||
*pDest = static_cast<std::byte>((current & 0x00ff0000) >> 16);
|
||||
pDest++;
|
||||
current <<= 8;
|
||||
written++;
|
||||
}
|
||||
}
|
||||
|
||||
return dest.Slice(0, written);
|
||||
}
|
||||
|
||||
constexpr uint32_t Base64::GetEncodeRequiredLength(const uint32_t srcLen, const Flags flags) noexcept
|
||||
{
|
||||
uint32_t nSrcLen4 = srcLen * 4;
|
||||
uint32_t retval = nSrcLen4 / 3;
|
||||
if ((flags & Flags::NoPad) == Flags::None)
|
||||
{
|
||||
retval += srcLen % 3;
|
||||
}
|
||||
|
||||
uint32_t numLinefeed = retval / 76 + 1;
|
||||
uint32_t onLastLine = retval % 76;
|
||||
|
||||
if (onLastLine && onLastLine % 4)
|
||||
{
|
||||
retval += 4 - (onLastLine % 4);
|
||||
}
|
||||
|
||||
numLinefeed *= 2;
|
||||
if ((flags & Flags::NoLinefeed) == Flags::None)
|
||||
{
|
||||
retval += numLinefeed;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
template<class TElem, class TTraits>
|
||||
constexpr uint32_t Base64::GetDecodeRequiredLength(std::basic_string_view<TElem, TTraits> src) noexcept
|
||||
{
|
||||
Contract::Requires(static_cast<uint64_t>(src.size()) <= UINT32_MAX);
|
||||
return static_cast<uint32_t>(src.size());
|
||||
}
|
||||
|
||||
constexpr uint32_t Base64::GetDecodeRequiredLength(const uint32_t srcLen) noexcept
|
||||
{
|
||||
return srcLen;
|
||||
}
|
||||
|
||||
constexpr char Base64CharEncodingTable[68] = {
|
||||
'A',
|
||||
'B',
|
||||
'C',
|
||||
'D',
|
||||
'E',
|
||||
'F',
|
||||
'G',
|
||||
'H',
|
||||
'I',
|
||||
'J',
|
||||
'K',
|
||||
'L',
|
||||
'M',
|
||||
'N',
|
||||
'O',
|
||||
'P',
|
||||
'Q',
|
||||
'R',
|
||||
'S',
|
||||
'T',
|
||||
'U',
|
||||
'V',
|
||||
'W',
|
||||
'X',
|
||||
'Y',
|
||||
'Z',
|
||||
'a',
|
||||
'b',
|
||||
'c',
|
||||
'd',
|
||||
'e',
|
||||
'f',
|
||||
'g',
|
||||
'h',
|
||||
'i',
|
||||
'j',
|
||||
'k',
|
||||
'l',
|
||||
'm',
|
||||
'n',
|
||||
'o',
|
||||
'p',
|
||||
'q',
|
||||
'r',
|
||||
's',
|
||||
't',
|
||||
'u',
|
||||
'v',
|
||||
'w',
|
||||
'x',
|
||||
'y',
|
||||
'z',
|
||||
'0',
|
||||
'1',
|
||||
'2',
|
||||
'3',
|
||||
'4',
|
||||
'5',
|
||||
'6',
|
||||
'7',
|
||||
'8',
|
||||
'9',
|
||||
'+',
|
||||
'/',
|
||||
'=',
|
||||
'\r',
|
||||
'\n',
|
||||
'\0'
|
||||
};
|
||||
|
||||
template<>
|
||||
constexpr char Base64::EncodeBase64Char<char>(std::byte index, const Flags flags) noexcept
|
||||
{
|
||||
char retval = Base64CharEncodingTable[static_cast<int>(index)];
|
||||
if ((flags & Flags::Url) != Flags::None)
|
||||
{
|
||||
if (retval == '+')
|
||||
{
|
||||
retval = '-';
|
||||
}
|
||||
if (retval == '/')
|
||||
{
|
||||
retval = '_';
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
constexpr wchar_t Base64WideEncodingTable[68] = {
|
||||
L'A',
|
||||
L'B',
|
||||
L'C',
|
||||
L'D',
|
||||
L'E',
|
||||
L'F',
|
||||
L'G',
|
||||
L'H',
|
||||
L'I',
|
||||
L'J',
|
||||
L'K',
|
||||
L'L',
|
||||
L'M',
|
||||
L'N',
|
||||
L'O',
|
||||
L'P',
|
||||
L'Q',
|
||||
L'R',
|
||||
L'S',
|
||||
L'T',
|
||||
L'U',
|
||||
L'V',
|
||||
L'W',
|
||||
L'X',
|
||||
L'Y',
|
||||
L'Z',
|
||||
L'a',
|
||||
L'b',
|
||||
L'c',
|
||||
L'd',
|
||||
L'e',
|
||||
L'f',
|
||||
L'g',
|
||||
L'h',
|
||||
L'i',
|
||||
L'j',
|
||||
L'k',
|
||||
L'l',
|
||||
L'm',
|
||||
L'n',
|
||||
L'o',
|
||||
L'p',
|
||||
L'q',
|
||||
L'r',
|
||||
L's',
|
||||
L't',
|
||||
L'u',
|
||||
L'v',
|
||||
L'w',
|
||||
L'x',
|
||||
L'y',
|
||||
L'z',
|
||||
L'0',
|
||||
L'1',
|
||||
L'2',
|
||||
L'3',
|
||||
L'4',
|
||||
L'5',
|
||||
L'6',
|
||||
L'7',
|
||||
L'8',
|
||||
L'9',
|
||||
L'+',
|
||||
L'/',
|
||||
L'=',
|
||||
L'\r',
|
||||
L'\n',
|
||||
L'\0'
|
||||
};
|
||||
|
||||
template<>
|
||||
constexpr wchar_t Base64::EncodeBase64Char<wchar_t>(std::byte index, const Flags flags) noexcept
|
||||
{
|
||||
wchar_t retval = Base64WideEncodingTable[static_cast<int>(index)];
|
||||
|
||||
if ((flags & Flags::Url) != Flags::None)
|
||||
{
|
||||
if (retval == L'+')
|
||||
{
|
||||
retval = L'-';
|
||||
}
|
||||
if (retval == L'/')
|
||||
{
|
||||
retval = L'_';
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
template<>
|
||||
constexpr int Base64::DecodeBase64Char<char>(const char ch, const Flags flags) noexcept
|
||||
{
|
||||
// returns -1 if the character is invalid
|
||||
// or should be skipped
|
||||
// otherwise, returns the 6-bit code for the character
|
||||
// from the encoding table
|
||||
if (ch >= 'A' && ch <= 'Z')
|
||||
{
|
||||
return ch - 'A' + 0; // 0 range starts at 'A'
|
||||
}
|
||||
if (ch >= 'a' && ch <= 'z')
|
||||
{
|
||||
return ch - 'a' + 26; // 26 range starts at 'a'
|
||||
}
|
||||
if (ch >= '0' && ch <= '9')
|
||||
{
|
||||
return ch - '0' + 52; // 52 range starts at '0'
|
||||
}
|
||||
|
||||
if ((flags & Flags::Url) != Flags::None)
|
||||
{
|
||||
if (ch == '-')
|
||||
{
|
||||
return 62; // base64url '+' -> '-'
|
||||
}
|
||||
if (ch == '_')
|
||||
{
|
||||
return 63; // base64url '/' -> '_'
|
||||
}
|
||||
}
|
||||
|
||||
if (ch == '+')
|
||||
{
|
||||
return 62; // base64 '+' is 62
|
||||
}
|
||||
if (ch == '/')
|
||||
{
|
||||
return 63; // base64 '/' is 63
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
template<>
|
||||
constexpr int Base64::DecodeBase64Char<wchar_t>(const wchar_t ch, const Flags flags) noexcept
|
||||
{
|
||||
// returns -1 if the character is invalid
|
||||
// or should be skipped
|
||||
// otherwise, returns the 6-bit code for the character
|
||||
// from the encoding table
|
||||
if (ch >= L'A' && ch <= L'Z')
|
||||
{
|
||||
return ch - L'A' + 0; // 0 range starts at 'A'
|
||||
}
|
||||
if (ch >= L'a' && ch <= L'z')
|
||||
{
|
||||
return ch - L'a' + 26; // 26 range starts at 'a'
|
||||
}
|
||||
if (ch >= L'0' && ch <= L'9')
|
||||
{
|
||||
return ch - L'0' + 52; // 52 range starts at '0'
|
||||
}
|
||||
|
||||
if ((flags & Flags::Url) != Flags::None)
|
||||
{
|
||||
if (ch == L'-')
|
||||
{
|
||||
return 62; // base64url '+' -> '-'
|
||||
}
|
||||
if (ch == L'_')
|
||||
{
|
||||
return 63; // base64url '/' -> '_'
|
||||
}
|
||||
}
|
||||
|
||||
if (ch == L'+')
|
||||
{
|
||||
return 62; // base64 '+' is 62
|
||||
}
|
||||
if (ch == L'/')
|
||||
{
|
||||
return 63; // base64 '/' is 63
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
19
src/Core/Core.Native/Blittable.h
Normal file
19
src/Core/Core.Native/Blittable.h
Normal file
@@ -0,0 +1,19 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
namespace cdb_core
|
||||
{
|
||||
template<typename C>
|
||||
struct is_blittable
|
||||
{
|
||||
constexpr static bool value =
|
||||
std::is_nothrow_default_constructible_v<C> &&
|
||||
std::is_nothrow_copy_constructible_v<C> &&
|
||||
std::is_nothrow_copy_assignable_v<C>;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
inline constexpr bool is_blittable_v = is_blittable<T>::value;
|
||||
}
|
||||
26
src/Core/Core.Native/Contract.cpp
Normal file
26
src/Core/Core.Native/Contract.cpp
Normal file
@@ -0,0 +1,26 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#include "pch.h"
|
||||
#include "Contract.h"
|
||||
#include "Strings.h"
|
||||
|
||||
namespace cdb_core
|
||||
{
|
||||
std::wstring Contract::MakeError(std::string_view api, std::string_view message)
|
||||
{
|
||||
if (message.empty())
|
||||
{
|
||||
return make_string(L"%.*S", api.size(), api.data());
|
||||
}
|
||||
return make_string(L"%.*S Failure: %.*S", api.size(), api.data(), message.size(), message.data());
|
||||
}
|
||||
|
||||
[[noreturn]] void Contract::Fail(std::string_view api, std::string_view message)
|
||||
{
|
||||
std::wstring error = MakeError(api, message);
|
||||
_ASSERT_EXPR(false, error.c_str());
|
||||
std::terminate();
|
||||
}
|
||||
}
|
||||
92
src/Core/Core.Native/Contract.h
Normal file
92
src/Core/Core.Native/Contract.h
Normal file
@@ -0,0 +1,92 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
// Forward Declarations
|
||||
namespace cdb_core_test
|
||||
{
|
||||
class ContractUnitTests;
|
||||
}
|
||||
|
||||
namespace cdb_core
|
||||
{
|
||||
class Contract final
|
||||
{
|
||||
public:
|
||||
Contract() = delete;
|
||||
~Contract() = delete;
|
||||
Contract(const Contract& other) = delete;
|
||||
Contract(Contract&& other) noexcept = delete;
|
||||
Contract& operator=(const Contract& other) = delete;
|
||||
Contract& operator=(Contract&& other) noexcept = delete;
|
||||
|
||||
constexpr static void Assert([[maybe_unused]] bool condition)
|
||||
{
|
||||
#if _DEBUG
|
||||
if (!condition)
|
||||
{
|
||||
Fail("Assert", std::string_view());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
constexpr static void Assert([[maybe_unused]] bool condition, std::string_view message)
|
||||
{
|
||||
#if _DEBUG
|
||||
if (!condition)
|
||||
{
|
||||
Fail("Assert", message);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
constexpr static void Requires(bool condition)
|
||||
{
|
||||
if (!condition)
|
||||
{
|
||||
Fail("Requires", std::string_view());
|
||||
}
|
||||
}
|
||||
|
||||
constexpr static void Requires(bool condition, std::string_view message)
|
||||
{
|
||||
if (!condition)
|
||||
{
|
||||
Fail("Requires", message);
|
||||
}
|
||||
}
|
||||
|
||||
constexpr static void Invariant(bool condition)
|
||||
{
|
||||
if (!condition)
|
||||
{
|
||||
Fail("Invariant", std::string_view());
|
||||
}
|
||||
}
|
||||
|
||||
constexpr static void Invariant(bool condition, std::string_view message)
|
||||
{
|
||||
if (!condition)
|
||||
{
|
||||
Fail("Invariant", message);
|
||||
}
|
||||
}
|
||||
|
||||
[[noreturn]] static void Fail()
|
||||
{
|
||||
Fail("Fail", std::string_view());
|
||||
}
|
||||
|
||||
[[noreturn]] static void Fail(std::string_view message)
|
||||
{
|
||||
Fail("Fail", message);
|
||||
}
|
||||
|
||||
private:
|
||||
friend class cdb_core_test::ContractUnitTests;
|
||||
[[noreturn]] static void Fail(std::string_view api, std::string_view message);
|
||||
[[nodiscard]] static std::wstring MakeError(std::string_view api, std::string_view message);
|
||||
};
|
||||
}
|
||||
33
src/Core/Core.Native/Core.Native.h
Normal file
33
src/Core/Core.Native/Core.Native.h
Normal file
@@ -0,0 +1,33 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "framework.h"
|
||||
|
||||
#include "Contract.h"
|
||||
#include "HashCode.h"
|
||||
#include "tla.h"
|
||||
#include "Endian.h"
|
||||
#include "Blittable.h"
|
||||
#include "IsAllSame.h"
|
||||
#include "Span.h"
|
||||
#include "ReadOnlySpan.h"
|
||||
#include "MemoryMarshal.h"
|
||||
#include "Memory.h"
|
||||
#include "ReadOnlyMemory.h"
|
||||
#include "Strings.h"
|
||||
#include "make_unique.h"
|
||||
#include "Stringable.h"
|
||||
#include "EqualityComparable.h"
|
||||
#include "DeepCompare.h"
|
||||
#include "Hashable.h"
|
||||
#include "Utf8Span.h"
|
||||
#include "ref_ptr.h"
|
||||
#include "Failure.h"
|
||||
#include "Result.h"
|
||||
#include "TimeSpan.h"
|
||||
#include "Base64.h"
|
||||
#include "Stopwatch.h"
|
||||
#include "Crc32.h"
|
||||
2159
src/Core/Core.Native/Crc32.cpp
Normal file
2159
src/Core/Core.Native/Crc32.cpp
Normal file
File diff suppressed because it is too large
Load Diff
37
src/Core/Core.Native/Crc32.h
Normal file
37
src/Core/Core.Native/Crc32.h
Normal file
@@ -0,0 +1,37 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace cdb_core
|
||||
{
|
||||
template<typename T> struct ReadOnlySpan;
|
||||
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
//
|
||||
// File implements Slicing-by-8 CRC Generation, as described in
|
||||
// "Novel Table Lookup-Based Algorithms for High-Performance CRC Generation"
|
||||
// IEEE TRANSACTIONS ON COMPUTERS, VOL. 57, NO. 11, NOVEMBER 2008
|
||||
//
|
||||
// Copyright(c) 2004-2006 Intel Corporation - All Rights Reserved
|
||||
//
|
||||
// This software program is licensed subject to the BSD License,
|
||||
// available at http://www.opensource.org/licenses/bsd-license.html.
|
||||
|
||||
/// <summary>CRC Generator.</summary>
|
||||
struct Crc32 final
|
||||
{
|
||||
// Static Class
|
||||
[[nodiscard]] Crc32() = delete;
|
||||
~Crc32() = delete;
|
||||
Crc32(const Crc32& other) = delete;
|
||||
Crc32(Crc32&& other) noexcept = delete;
|
||||
Crc32& operator=(const Crc32& other) = delete;
|
||||
Crc32& operator=(Crc32&& other) noexcept = delete;
|
||||
|
||||
[[nodiscard]] static uint32_t Update(uint32_t crc32, ReadOnlySpan<byte> span) noexcept;
|
||||
};
|
||||
}
|
||||
61
src/Core/Core.Native/DeepCompare.h
Normal file
61
src/Core/Core.Native/DeepCompare.h
Normal file
@@ -0,0 +1,61 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
namespace cdb_core
|
||||
{
|
||||
template<typename T>
|
||||
struct DeepComparer final
|
||||
{
|
||||
bool operator()(const T& x, const T& y) const noexcept { return x == y; }
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
static bool DeepCompare(const T& x, const T& y) noexcept
|
||||
{
|
||||
return DeepComparer<T>{}(x, y);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
struct DeepComparer<std::vector<T>> final
|
||||
{
|
||||
bool operator()(const std::vector<T>& x, const std::vector<T>& y) const noexcept
|
||||
{
|
||||
if (x.size() != y.size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < x.size(); i++)
|
||||
{
|
||||
if (!DeepCompare(x[i], y[i]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct DeepComparer<std::unique_ptr<T>> final
|
||||
{
|
||||
bool operator()(const std::unique_ptr<T>& x, const std::unique_ptr<T>& y) const noexcept
|
||||
{
|
||||
if ((x == nullptr) && (y == nullptr))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if ((x == nullptr) || (y == nullptr))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return DeepCompare(*x, *y);
|
||||
}
|
||||
};
|
||||
}
|
||||
33
src/Core/Core.Native/Endian.h
Normal file
33
src/Core/Core.Native/Endian.h
Normal file
@@ -0,0 +1,33 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace cdb_core
|
||||
{
|
||||
struct Endian final
|
||||
{
|
||||
// Static Class
|
||||
Endian() = delete;
|
||||
~Endian() = delete;
|
||||
Endian(const Endian& other) = delete;
|
||||
Endian(Endian&& other) noexcept = delete;
|
||||
Endian& operator=(const Endian& other) = delete;
|
||||
Endian& operator=(Endian&& other) noexcept = delete;
|
||||
|
||||
constexpr static bool IsLittleEndian() noexcept;
|
||||
constexpr static bool IsBigEndian() noexcept;
|
||||
};
|
||||
|
||||
constexpr bool Endian::IsLittleEndian() noexcept
|
||||
{
|
||||
// TODO: this should use std::endian when we move to C++ v20.
|
||||
return true;
|
||||
}
|
||||
|
||||
constexpr bool Endian::IsBigEndian() noexcept
|
||||
{
|
||||
return !Endian::IsLittleEndian();
|
||||
}
|
||||
}
|
||||
23
src/Core/Core.Native/EqualityComparable.h
Normal file
23
src/Core/Core.Native/EqualityComparable.h
Normal file
@@ -0,0 +1,23 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
#include <string>
|
||||
|
||||
namespace cdb_core
|
||||
{
|
||||
template<typename T, typename = void>
|
||||
struct is_equality_comparable : std::false_type { };
|
||||
|
||||
/// <summary>
|
||||
/// Check for operator==.
|
||||
/// </summary>
|
||||
// ReSharper disable once CppIdenticalOperandsInBinaryExpression
|
||||
template<typename T>
|
||||
struct is_equality_comparable<T, typename std::enable_if<true, decltype(std::declval<T&>() == std::declval<T&>(), (
|
||||
void)0)>::type> : std::true_type { };
|
||||
|
||||
template<typename T>
|
||||
inline constexpr bool is_equality_comparable_v = is_equality_comparable<T>::value;
|
||||
}
|
||||
24
src/Core/Core.Native/Failure.cpp
Normal file
24
src/Core/Core.Native/Failure.cpp
Normal file
@@ -0,0 +1,24 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#include "pch.h"
|
||||
#include "Failure.h"
|
||||
|
||||
namespace cdb_core
|
||||
{
|
||||
const Failure Failure::None = Failure();
|
||||
|
||||
Failure& Failure::operator=(const Failure& other)
|
||||
{
|
||||
if (this == &other)
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
m_failureCode = other.m_failureCode;
|
||||
m_hResult = other.m_hResult;
|
||||
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
72
src/Core/Core.Native/Failure.h
Normal file
72
src/Core/Core.Native/Failure.h
Normal file
@@ -0,0 +1,72 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
#include "Contract.h"
|
||||
|
||||
namespace cdb_core
|
||||
{
|
||||
/// <summary>
|
||||
/// Failure is a wrapper around Failed states in the system.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Encapsulates a Failure Code specific to an application
|
||||
/// and an associated Win32 HResult if the failure originated
|
||||
/// from a windows API call.
|
||||
/// </remarks>
|
||||
class [[nodiscard]] Failure
|
||||
{
|
||||
public:
|
||||
static const Failure None;
|
||||
constexpr static uint32_t NoneCode = 0;
|
||||
|
||||
template<typename TEnum, typename = std::enable_if_t<std::is_enum<TEnum>::value, int>>
|
||||
Failure(const TEnum failureCode) noexcept;
|
||||
template<typename TEnum, typename = std::enable_if_t<std::is_enum<TEnum>::value, int>>
|
||||
Failure(const TEnum failureCode, const HRESULT hResult) noexcept;
|
||||
~Failure() = default;
|
||||
Failure(const Failure&) = default;
|
||||
Failure(Failure&&) = default;
|
||||
|
||||
Failure& operator=(const Failure& other);
|
||||
Failure& operator=(Failure&&) = default;
|
||||
|
||||
[[nodiscard]] bool IsFailed() const noexcept;
|
||||
|
||||
[[nodiscard]] uint32_t GetFailureCode() const noexcept;
|
||||
|
||||
template<typename TEnum, typename = std::enable_if_t<std::is_enum<TEnum>::value, int>>
|
||||
[[nodiscard]] TEnum GetFailureCode() const noexcept;
|
||||
|
||||
[[nodiscard]] HRESULT GetHResult() const noexcept;
|
||||
|
||||
private:
|
||||
|
||||
constexpr explicit Failure() : m_failureCode(NoneCode), m_hResult{0} { }
|
||||
|
||||
uint32_t m_failureCode;
|
||||
HRESULT m_hResult;
|
||||
};
|
||||
|
||||
template<typename TEnum, typename>
|
||||
Failure::Failure(const TEnum failureCode) noexcept : Failure(failureCode, S_OK) { }
|
||||
|
||||
template<typename TEnum, typename>
|
||||
Failure::Failure(const TEnum failureCode, const HRESULT hResult) noexcept :
|
||||
m_failureCode(static_cast<uint32_t>(failureCode)),
|
||||
m_hResult(hResult)
|
||||
{
|
||||
Contract::Requires(m_failureCode != 0,
|
||||
"Failure constructor should not use FailureCode::None. Use Failure::None instead");
|
||||
}
|
||||
|
||||
inline bool Failure::IsFailed() const noexcept { return m_failureCode != NoneCode; }
|
||||
|
||||
inline uint32_t Failure::GetFailureCode() const noexcept { return m_failureCode; }
|
||||
|
||||
template<typename TEnum, typename>
|
||||
TEnum Failure::GetFailureCode() const noexcept { return static_cast<TEnum>(m_failureCode); }
|
||||
|
||||
inline HRESULT Failure::GetHResult() const noexcept { return m_hResult; }
|
||||
}
|
||||
498
src/Core/Core.Native/HashCode.h
Normal file
498
src/Core/Core.Native/HashCode.h
Normal file
@@ -0,0 +1,498 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
// ReSharper disable four CommentTypo
|
||||
/*
|
||||
|
||||
This xxHash64 implementation is based on the Netcore xxHash32 C# port which was in turn
|
||||
based on the xxHash32 code published by Yann Collet:
|
||||
|
||||
https://raw.githubusercontent.com/Cyan4973/xxHash/5c174cfa4e45a42f94082dc0d4539b39696afea1/xxhash.c
|
||||
|
||||
xxHash - Fast Hash algorithm
|
||||
Copyright (C) 2012-2016, Yann Collet
|
||||
|
||||
BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
You can contact the author at :
|
||||
- xxHash homepage: http://www.xxhash.com
|
||||
- xxHash source repository : https://github.com/Cyan4973/xxHash
|
||||
|
||||
*/
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace cdb_core
|
||||
{
|
||||
/// <summary>Utility functions for combining hash codes..</summary>
|
||||
struct HashCode final
|
||||
{
|
||||
template<typename T1, typename H1 = std::hash<T1>>
|
||||
constexpr static size_t Combine(const T1& value1)
|
||||
{
|
||||
// Provide a way of diffusing bits from something with a limited
|
||||
// input hash space. For example, many enums only have a few
|
||||
// possible hashes, only using the bottom few bits of the code. Some
|
||||
// collections are built on the assumption that hashes are spread
|
||||
// over a larger space, so diffusing the bits may help the
|
||||
// collection work more efficiently.
|
||||
|
||||
uint64_t hc1 = static_cast<uint64_t>(H1{}.operator()(value1));
|
||||
|
||||
uint64_t hash = MixEmptyState();
|
||||
hash += 8;
|
||||
|
||||
hash = QueueRound(hash, hc1);
|
||||
|
||||
hash = MixFinal(hash);
|
||||
return static_cast<size_t>(hash);
|
||||
}
|
||||
|
||||
template<typename T1, typename T2, typename H1 = std::hash<T1>, typename H2 = std::hash<T2>>
|
||||
constexpr static size_t Combine(const T1& value1, const T2& value2)
|
||||
{
|
||||
uint64_t hc1 = static_cast<uint64_t>(H1{}.operator()(value1));
|
||||
uint64_t hc2 = static_cast<uint64_t>(H2{}.operator()(value2));
|
||||
|
||||
uint64_t hash = MixEmptyState();
|
||||
hash += 16;
|
||||
|
||||
hash = QueueRound(hash, hc1);
|
||||
hash = QueueRound(hash, hc2);
|
||||
|
||||
hash = MixFinal(hash);
|
||||
return static_cast<size_t>(hash);
|
||||
}
|
||||
|
||||
template<
|
||||
typename T1,
|
||||
typename T2,
|
||||
typename T3,
|
||||
typename H1 = std::hash<T1>,
|
||||
typename H2 = std::hash<T2>,
|
||||
typename H3 = std::hash<T3>>
|
||||
constexpr static size_t Combine(const T1& value1, const T2& value2, const T3& value3)
|
||||
{
|
||||
uint64_t hc1 = static_cast<uint64_t>(H1{}.operator()(value1));
|
||||
uint64_t hc2 = static_cast<uint64_t>(H2{}.operator()(value2));
|
||||
uint64_t hc3 = static_cast<uint64_t>(H3{}.operator()(value3));
|
||||
|
||||
uint64_t hash = MixEmptyState();
|
||||
hash += 24;
|
||||
|
||||
hash = QueueRound(hash, hc1);
|
||||
hash = QueueRound(hash, hc2);
|
||||
hash = QueueRound(hash, hc3);
|
||||
|
||||
hash = MixFinal(hash);
|
||||
return static_cast<size_t>(hash);
|
||||
}
|
||||
|
||||
template<
|
||||
typename T1,
|
||||
typename T2,
|
||||
typename T3,
|
||||
typename T4,
|
||||
typename H1 = std::hash<T1>,
|
||||
typename H2 = std::hash<T2>,
|
||||
typename H3 = std::hash<T3>,
|
||||
typename H4 = std::hash<T4>>
|
||||
constexpr static size_t Combine(const T1& value1, const T2& value2, const T3& value3, const T4& value4)
|
||||
{
|
||||
uint64_t hc1 = static_cast<uint64_t>(H1{}.operator()(value1));
|
||||
uint64_t hc2 = static_cast<uint64_t>(H2{}.operator()(value2));
|
||||
uint64_t hc3 = static_cast<uint64_t>(H3{}.operator()(value3));
|
||||
uint64_t hc4 = static_cast<uint64_t>(H4{}.operator()(value4));
|
||||
|
||||
uint64_t v1;
|
||||
uint64_t v2;
|
||||
uint64_t v3;
|
||||
uint64_t v4;
|
||||
Initialize(v1, v2, v3, v4);
|
||||
|
||||
v1 = Round(v1, hc1);
|
||||
v2 = Round(v2, hc2);
|
||||
v3 = Round(v3, hc3);
|
||||
v4 = Round(v4, hc4);
|
||||
|
||||
uint64_t hash = MixState(v1, v2, v3, v4);
|
||||
hash += 32;
|
||||
|
||||
hash = MixFinal(hash);
|
||||
return static_cast<size_t>(hash);
|
||||
}
|
||||
|
||||
template<
|
||||
typename T1,
|
||||
typename T2,
|
||||
typename T3,
|
||||
typename T4,
|
||||
typename T5,
|
||||
typename H1 = std::hash<T1>,
|
||||
typename H2 = std::hash<T2>,
|
||||
typename H3 = std::hash<T3>,
|
||||
typename H4 = std::hash<T4>,
|
||||
typename H5 = std::hash<T5>>
|
||||
constexpr static size_t Combine(const T1& value1, const T2& value2, const T3& value3, const T4& value4,
|
||||
const T5& value5)
|
||||
{
|
||||
uint64_t hc1 = static_cast<uint64_t>(H1{}.operator()(value1));
|
||||
uint64_t hc2 = static_cast<uint64_t>(H2{}.operator()(value2));
|
||||
uint64_t hc3 = static_cast<uint64_t>(H3{}.operator()(value3));
|
||||
uint64_t hc4 = static_cast<uint64_t>(H4{}.operator()(value4));
|
||||
uint64_t hc5 = static_cast<uint64_t>(H5{}.operator()(value5));
|
||||
|
||||
uint64_t v1;
|
||||
uint64_t v2;
|
||||
uint64_t v3;
|
||||
uint64_t v4;
|
||||
Initialize(v1, v2, v3, v4);
|
||||
|
||||
v1 = Round(v1, hc1);
|
||||
v2 = Round(v2, hc2);
|
||||
v3 = Round(v3, hc3);
|
||||
v4 = Round(v4, hc4);
|
||||
|
||||
uint64_t hash = MixState(v1, v2, v3, v4);
|
||||
hash += 40;
|
||||
|
||||
hash = QueueRound(hash, hc5);
|
||||
|
||||
hash = MixFinal(hash);
|
||||
return static_cast<size_t>(hash);
|
||||
}
|
||||
|
||||
template<
|
||||
typename T1,
|
||||
typename T2,
|
||||
typename T3,
|
||||
typename T4,
|
||||
typename T5,
|
||||
typename T6,
|
||||
typename H1 = std::hash<T1>,
|
||||
typename H2 = std::hash<T2>,
|
||||
typename H3 = std::hash<T3>,
|
||||
typename H4 = std::hash<T4>,
|
||||
typename H5 = std::hash<T5>,
|
||||
typename H6 = std::hash<T6>>
|
||||
constexpr static size_t Combine(const T1& value1, const T2& value2, const T3& value3, const T4& value4,
|
||||
const T5& value5, const T6& value6)
|
||||
{
|
||||
uint64_t hc1 = static_cast<uint64_t>(H1{}.operator()(value1));
|
||||
uint64_t hc2 = static_cast<uint64_t>(H2{}.operator()(value2));
|
||||
uint64_t hc3 = static_cast<uint64_t>(H3{}.operator()(value3));
|
||||
uint64_t hc4 = static_cast<uint64_t>(H4{}.operator()(value4));
|
||||
uint64_t hc5 = static_cast<uint64_t>(H5{}.operator()(value5));
|
||||
uint64_t hc6 = static_cast<uint64_t>(H6{}.operator()(value6));
|
||||
|
||||
uint64_t v1;
|
||||
uint64_t v2;
|
||||
uint64_t v3;
|
||||
uint64_t v4;
|
||||
Initialize(v1, v2, v3, v4);
|
||||
|
||||
v1 = Round(v1, hc1);
|
||||
v2 = Round(v2, hc2);
|
||||
v3 = Round(v3, hc3);
|
||||
v4 = Round(v4, hc4);
|
||||
|
||||
uint64_t hash = MixState(v1, v2, v3, v4);
|
||||
hash += 48;
|
||||
|
||||
hash = QueueRound(hash, hc5);
|
||||
hash = QueueRound(hash, hc6);
|
||||
|
||||
hash = MixFinal(hash);
|
||||
return static_cast<size_t>(hash);
|
||||
}
|
||||
|
||||
template<
|
||||
typename T1,
|
||||
typename T2,
|
||||
typename T3,
|
||||
typename T4,
|
||||
typename T5,
|
||||
typename T6,
|
||||
typename T7,
|
||||
typename H1 = std::hash<T1>,
|
||||
typename H2 = std::hash<T2>,
|
||||
typename H3 = std::hash<T3>,
|
||||
typename H4 = std::hash<T4>,
|
||||
typename H5 = std::hash<T5>,
|
||||
typename H6 = std::hash<T6>,
|
||||
typename H7 = std::hash<T7>>
|
||||
constexpr static size_t Combine(const T1& value1, const T2& value2, const T3& value3, const T4& value4,
|
||||
const T5& value5, const T6& value6, const T7& value7)
|
||||
{
|
||||
uint64_t hc1 = static_cast<uint64_t>(H1{}.operator()(value1));
|
||||
uint64_t hc2 = static_cast<uint64_t>(H2{}.operator()(value2));
|
||||
uint64_t hc3 = static_cast<uint64_t>(H3{}.operator()(value3));
|
||||
uint64_t hc4 = static_cast<uint64_t>(H4{}.operator()(value4));
|
||||
uint64_t hc5 = static_cast<uint64_t>(H5{}.operator()(value5));
|
||||
uint64_t hc6 = static_cast<uint64_t>(H6{}.operator()(value6));
|
||||
uint64_t hc7 = static_cast<uint64_t>(H7{}.operator()(value7));
|
||||
|
||||
uint64_t v1;
|
||||
uint64_t v2;
|
||||
uint64_t v3;
|
||||
uint64_t v4;
|
||||
Initialize(v1, v2, v3, v4);
|
||||
|
||||
v1 = Round(v1, hc1);
|
||||
v2 = Round(v2, hc2);
|
||||
v3 = Round(v3, hc3);
|
||||
v4 = Round(v4, hc4);
|
||||
|
||||
uint64_t hash = MixState(v1, v2, v3, v4);
|
||||
hash += 56;
|
||||
|
||||
hash = QueueRound(hash, hc5);
|
||||
hash = QueueRound(hash, hc6);
|
||||
hash = QueueRound(hash, hc7);
|
||||
|
||||
hash = MixFinal(hash);
|
||||
return static_cast<size_t>(hash);
|
||||
}
|
||||
|
||||
template<
|
||||
typename T1,
|
||||
typename T2,
|
||||
typename T3,
|
||||
typename T4,
|
||||
typename T5,
|
||||
typename T6,
|
||||
typename T7,
|
||||
typename T8,
|
||||
typename H1 = std::hash<T1>,
|
||||
typename H2 = std::hash<T2>,
|
||||
typename H3 = std::hash<T3>,
|
||||
typename H4 = std::hash<T4>,
|
||||
typename H5 = std::hash<T5>,
|
||||
typename H6 = std::hash<T6>,
|
||||
typename H7 = std::hash<T7>,
|
||||
typename H8 = std::hash<T8>>
|
||||
constexpr static size_t Combine(const T1& value1, const T2& value2, const T3& value3, const T4& value4,
|
||||
const T5& value5, const T6& value6, const T7& value7, const T8& value8)
|
||||
{
|
||||
uint64_t hc1 = static_cast<uint64_t>(H1{}.operator()(value1));
|
||||
uint64_t hc2 = static_cast<uint64_t>(H2{}.operator()(value2));
|
||||
uint64_t hc3 = static_cast<uint64_t>(H3{}.operator()(value3));
|
||||
uint64_t hc4 = static_cast<uint64_t>(H4{}.operator()(value4));
|
||||
uint64_t hc5 = static_cast<uint64_t>(H5{}.operator()(value5));
|
||||
uint64_t hc6 = static_cast<uint64_t>(H6{}.operator()(value6));
|
||||
uint64_t hc7 = static_cast<uint64_t>(H7{}.operator()(value7));
|
||||
uint64_t hc8 = static_cast<uint64_t>(H8{}.operator()(value8));
|
||||
|
||||
uint64_t v1;
|
||||
uint64_t v2;
|
||||
uint64_t v3;
|
||||
uint64_t v4;
|
||||
Initialize(v1, v2, v3, v4);
|
||||
|
||||
v1 = Round(v1, hc1);
|
||||
v2 = Round(v2, hc2);
|
||||
v3 = Round(v3, hc3);
|
||||
v4 = Round(v4, hc4);
|
||||
|
||||
v1 = Round(v1, hc5);
|
||||
v2 = Round(v2, hc6);
|
||||
v3 = Round(v3, hc7);
|
||||
v4 = Round(v4, hc8);
|
||||
|
||||
uint64_t hash = MixState(v1, v2, v3, v4);
|
||||
hash += 64;
|
||||
|
||||
hash = MixFinal(hash);
|
||||
return static_cast<size_t>(hash);
|
||||
}
|
||||
|
||||
template<typename T1, typename H1 = std::hash<T1>>
|
||||
constexpr void Add(const T1& value) noexcept
|
||||
{
|
||||
AddHash(static_cast<uint64_t>(H1{}.operator()(value)));
|
||||
}
|
||||
|
||||
constexpr void AddHash(uint64_t value) noexcept
|
||||
{
|
||||
// The original xxHash works as follows:
|
||||
// 0. Initialize immediately. We can't do this in a struct (no
|
||||
// default ctor).
|
||||
// 1. Accumulate blocks of length 32 (4 uints) into 4 accumulators.
|
||||
// 2. Accumulate remaining blocks of length 4 (1 uint64_t) into the
|
||||
// hash.
|
||||
// 3. Accumulate remaining blocks of length 1 into the hash.
|
||||
|
||||
// There is no need for #3 as this type only accepts int64s. _queue1,
|
||||
// _queue2 and _queue3 are basically a buffer so that when
|
||||
// ToHashCode is called we can execute #2 correctly.
|
||||
|
||||
// We need to initialize the xxHash64 state (_v1 to _v4) lazily (see
|
||||
// #0) and the last place that can be done if you look at the
|
||||
// original code is just before the first block of 32 bytes is mixed
|
||||
// in. The xxHash64 state is never used for streams containing fewer
|
||||
// than 64 bytes.
|
||||
|
||||
// To see what's really going on here, have a look at the Combine
|
||||
// methods.
|
||||
|
||||
// Storing the value of _length locally shaves of quite a few bytes
|
||||
// in the resulting machine code.
|
||||
uint64_t previousLength = m_length++;
|
||||
uint64_t position = previousLength % 4;
|
||||
|
||||
// Switch can't be inlined.
|
||||
|
||||
if (position == 0)
|
||||
{
|
||||
m_queue1 = value;
|
||||
}
|
||||
else if (position == 1)
|
||||
{
|
||||
m_queue2 = value;
|
||||
}
|
||||
else if (position == 2)
|
||||
{
|
||||
m_queue3 = value;
|
||||
}
|
||||
else // position == 3
|
||||
{
|
||||
if (previousLength == 3)
|
||||
{
|
||||
Initialize(m_v1, m_v2, m_v3, m_v4);
|
||||
}
|
||||
|
||||
m_v1 = Round(m_v1, m_queue1);
|
||||
m_v2 = Round(m_v2, m_queue2);
|
||||
m_v3 = Round(m_v3, m_queue3);
|
||||
m_v4 = Round(m_v4, value);
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr size_t ToHashCode() const noexcept
|
||||
{
|
||||
// Storing the value of _length locally shaves of quite a few bytes
|
||||
// in the resulting machine code.
|
||||
uint64_t length = m_length;
|
||||
|
||||
// position refers to the *next* queue position in this method, so
|
||||
// position == 1 means that _queue1 is populated; _queue2 would have
|
||||
// been populated on the next call to Add.
|
||||
uint64_t position = length % 4;
|
||||
|
||||
// If the length is less than 4, _v1 to _v4 don't contain anything
|
||||
// yet. xxHash64 treats this differently.
|
||||
|
||||
uint64_t hash = length < 4 ? MixEmptyState() : MixState(m_v1, m_v2, m_v3, m_v4);
|
||||
|
||||
// _length is incremented once per Add(uint64_t) and is therefore 8
|
||||
// times too small (xxHash length is in bytes, not int64s).
|
||||
|
||||
hash += length * 8;
|
||||
|
||||
// Mix what remains in the queue
|
||||
|
||||
// Switch can't be inlined right now, so use as few branches as
|
||||
// possible by manually excluding impossible scenarios (position > 1
|
||||
// is always false if position is not > 0).
|
||||
if (position > 0)
|
||||
{
|
||||
hash = QueueRound(hash, m_queue1);
|
||||
if (position > 1)
|
||||
{
|
||||
hash = QueueRound(hash, m_queue2);
|
||||
if (position > 2)
|
||||
{
|
||||
hash = QueueRound(hash, m_queue3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hash = MixFinal(hash);
|
||||
return static_cast<size_t>(hash);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
constexpr static void Initialize(uint64_t& v1, uint64_t& v2, uint64_t& v3, uint64_t& v4)
|
||||
{
|
||||
v1 = s_seed + Prime1 + Prime2;
|
||||
v2 = s_seed + Prime2;
|
||||
v3 = s_seed;
|
||||
v4 = s_seed - Prime1;
|
||||
}
|
||||
|
||||
constexpr static uint64_t RotateLeft(uint64_t x, int r)
|
||||
{
|
||||
return ((x << r) | (x >> (64 - r)));
|
||||
}
|
||||
|
||||
constexpr static uint64_t Round(uint64_t hash, uint64_t input)
|
||||
{
|
||||
return RotateLeft(hash + input * Prime2, 31) * Prime1;
|
||||
}
|
||||
|
||||
constexpr static uint64_t QueueRound(uint64_t hash, uint64_t queuedValue)
|
||||
{
|
||||
return RotateLeft(hash ^ Round(0, queuedValue), 27) * Prime1 + Prime4;
|
||||
}
|
||||
|
||||
constexpr static uint64_t MixState(uint64_t v1, uint64_t v2, uint64_t v3, uint64_t v4)
|
||||
{
|
||||
return RotateLeft(v1, 1) + RotateLeft(v2, 7) + RotateLeft(v3, 12) + RotateLeft(v4, 18);
|
||||
}
|
||||
|
||||
constexpr static uint64_t MixEmptyState()
|
||||
{
|
||||
return s_seed + Prime5;
|
||||
}
|
||||
|
||||
constexpr static uint64_t MixFinal(uint64_t hash)
|
||||
{
|
||||
hash ^= hash >> 33;
|
||||
hash *= Prime2;
|
||||
hash ^= hash >> 29;
|
||||
hash *= Prime3;
|
||||
hash ^= hash >> 32;
|
||||
return hash;
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr uint64_t s_seed = 0x9FB21C651E98DF25ULL;
|
||||
|
||||
static constexpr uint64_t Prime1 = 11400714785074694791ULL;
|
||||
static constexpr uint64_t Prime2 = 14029467366897019727ULL;
|
||||
static constexpr uint64_t Prime3 = 1609587929392839161ULL;
|
||||
static constexpr uint64_t Prime4 = 9650029242287828579ULL;
|
||||
static constexpr uint64_t Prime5 = 2870177450012600261ULL;
|
||||
|
||||
uint64_t m_v1{0}, m_v2{0}, m_v3{0}, m_v4{0};
|
||||
uint64_t m_queue1{0}, m_queue2{0}, m_queue3{0};
|
||||
uint64_t m_length{0};
|
||||
};
|
||||
}
|
||||
33
src/Core/Core.Native/Hashable.h
Normal file
33
src/Core/Core.Native/Hashable.h
Normal file
@@ -0,0 +1,33 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace cdb_core
|
||||
{
|
||||
/// <summary>Check for a GetHashCode method.</summary>
|
||||
/// <remarks>Method must have this signature:
|
||||
/// <code>size_t GetHashCode() const noexcept</code>
|
||||
/// </remarks>
|
||||
template<typename C>
|
||||
struct is_hashable
|
||||
{
|
||||
private:
|
||||
template<typename T>
|
||||
constexpr static typename std::is_same<decltype(&T::GetHashCode), size_t (T::*)() const noexcept>::type Check(T*)
|
||||
{
|
||||
return std::true_type{};
|
||||
}
|
||||
|
||||
template<typename>
|
||||
constexpr static std::false_type Check(...) { return std::false_type{}; }
|
||||
|
||||
public:
|
||||
constexpr static bool value = decltype(
|
||||
Check<std::remove_const_t<std::remove_reference_t<std::remove_pointer_t<C>>>>(nullptr))::value;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
inline constexpr bool is_hashable_v = is_hashable<T>::value;
|
||||
}
|
||||
25
src/Core/Core.Native/IsAllSame.h
Normal file
25
src/Core/Core.Native/IsAllSame.h
Normal file
@@ -0,0 +1,25 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
#include <type_traits>
|
||||
|
||||
namespace cdb_core
|
||||
{
|
||||
template<typename L, typename R, typename... Rest>
|
||||
struct is_all_same
|
||||
{
|
||||
constexpr static bool value =
|
||||
std::is_same<L, R>::value && is_all_same<L, Rest...>::value;
|
||||
};
|
||||
|
||||
template<typename L, typename R>
|
||||
struct is_all_same<L, R>
|
||||
{
|
||||
constexpr static bool value = std::is_same<L, R>::value;
|
||||
};
|
||||
|
||||
template<typename L, typename... R>
|
||||
inline constexpr bool is_all_same_v = is_all_same<L, R...>::value;
|
||||
}
|
||||
171
src/Core/Core.Native/Memory.h
Normal file
171
src/Core/Core.Native/Memory.h
Normal file
@@ -0,0 +1,171 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
|
||||
#include "Blittable.h"
|
||||
#include "Contract.h"
|
||||
#include "Span.h"
|
||||
|
||||
namespace cdb_core
|
||||
{
|
||||
template<typename T> struct ReadOnlyMemory;
|
||||
|
||||
/// <summary>
|
||||
/// Memory represents a owned (possibly shared) contiguous region of arbitrary memory.
|
||||
/// </summary>
|
||||
template<typename T>
|
||||
struct Memory final
|
||||
{
|
||||
static_assert(is_blittable_v<T>, "Only blittable types can be used in a Memory<T>.");
|
||||
|
||||
constexpr Memory() noexcept;
|
||||
~Memory() noexcept = default;
|
||||
explicit constexpr Memory(uint32_t length) noexcept;
|
||||
explicit constexpr Memory(const ReadOnlySpan<T>& buffer) noexcept;
|
||||
constexpr Memory(T* buffer, uint32_t length) noexcept;
|
||||
template<class Destructor,
|
||||
std::enable_if_t<std::conjunction_v<std::is_move_constructible<Destructor>,
|
||||
std::is_nothrow_invocable<Destructor&, T*&>>, int> = 0>
|
||||
Memory(T* buffer, uint32_t length, Destructor destructor);
|
||||
|
||||
Memory(const Memory<T>&) noexcept = default;
|
||||
Memory(Memory<T>&&) noexcept = default;
|
||||
Memory<T>& operator=(const Memory<T>& other) noexcept = default;
|
||||
Memory<T>& operator=(Memory<T>&& other) noexcept = default;
|
||||
|
||||
/// <summary>The number of items in the Memory.</summary>
|
||||
[[nodiscard]] uint32_t Length() const noexcept;
|
||||
|
||||
[[nodiscard]] operator ReadOnlyMemory<T>() const noexcept;
|
||||
|
||||
[[nodiscard]] ReadOnlySpan<T> AsSpan() const noexcept;
|
||||
|
||||
[[nodiscard]] Span<T> AsSpan() noexcept;
|
||||
|
||||
/// <summary>Returns true if length is 0.</summary>
|
||||
[[nodiscard]] bool IsEmpty() const noexcept;
|
||||
|
||||
[[nodiscard]] ReadOnlyMemory<T> Slice(uint32_t start) const noexcept;
|
||||
[[nodiscard]] Memory<T> Slice(uint32_t start) noexcept;
|
||||
|
||||
[[nodiscard]] ReadOnlyMemory<T> Slice(uint32_t start, uint32_t length) const noexcept;
|
||||
[[nodiscard]] Memory<T> Slice(uint32_t start, uint32_t length) noexcept;
|
||||
|
||||
bool operator==(const Memory<T>& rhs) const noexcept;
|
||||
bool operator!=(const Memory<T>& rhs) const noexcept;
|
||||
|
||||
private:
|
||||
Memory(std::shared_ptr<std::byte[]> buffer, uint32_t index, uint32_t length) noexcept;
|
||||
|
||||
std::shared_ptr<std::byte[]> m_buffer;
|
||||
uint32_t m_index;
|
||||
uint32_t m_length;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
constexpr Memory<T>::Memory() noexcept :
|
||||
m_buffer(),
|
||||
m_index(0),
|
||||
m_length(0) {}
|
||||
|
||||
template<typename T>
|
||||
constexpr Memory<T>::Memory(uint32_t length) noexcept :
|
||||
m_buffer(reinterpret_cast<std::byte*>(new T[length])),
|
||||
m_index(0),
|
||||
m_length(length) {}
|
||||
|
||||
template<typename T>
|
||||
constexpr Memory<T>::Memory(const ReadOnlySpan<T>& buffer) noexcept :
|
||||
m_buffer(buffer.Length() == 0 ? nullptr : reinterpret_cast<std::byte*>(new T[buffer.Length()])),
|
||||
m_index(0),
|
||||
m_length(buffer.Length())
|
||||
{
|
||||
buffer.CopyTo(AsSpan());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr Memory<T>::Memory(T* buffer, uint32_t length) noexcept :
|
||||
m_buffer(reinterpret_cast<std::byte*>(buffer)),
|
||||
m_index(0),
|
||||
m_length(length) {}
|
||||
|
||||
template<typename T>
|
||||
template<class Destructor,
|
||||
std::enable_if_t<std::conjunction_v<std::is_move_constructible<Destructor>,
|
||||
std::is_nothrow_invocable<Destructor&, T*&>>, int>>
|
||||
Memory<T>::Memory(T* buffer, uint32_t length, Destructor destructor) : m_buffer(reinterpret_cast<std::byte*>(buffer),
|
||||
destructor),
|
||||
m_index(0),
|
||||
m_length(length) {}
|
||||
|
||||
template<typename T>
|
||||
Memory<T>::Memory(std::shared_ptr<std::byte[]> buffer, uint32_t index, uint32_t length) noexcept :
|
||||
m_buffer(std::move(buffer)),
|
||||
m_index(index),
|
||||
m_length(length) {}
|
||||
|
||||
template<typename T>
|
||||
uint32_t Memory<T>::Length() const noexcept { return m_length; }
|
||||
|
||||
template<typename T>
|
||||
Memory<T>::operator ReadOnlyMemory<T>() const noexcept
|
||||
{
|
||||
return ReadOnlyMemory<T>(reinterpret_cast<T*>(m_buffer.get()) + m_index, m_length);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ReadOnlySpan<T> Memory<T>::AsSpan() const noexcept
|
||||
{
|
||||
return ReadOnlySpan<T>(reinterpret_cast<T*>(m_buffer.get()) + m_index, m_length);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Span<T> Memory<T>::AsSpan() noexcept
|
||||
{
|
||||
return Span<T>(reinterpret_cast<T*>(m_buffer.get()) + m_index, m_length);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool Memory<T>::IsEmpty() const noexcept { return m_length == 0; }
|
||||
|
||||
template<typename T>
|
||||
ReadOnlyMemory<T> Memory<T>::Slice(uint32_t start, uint32_t length) const noexcept
|
||||
{
|
||||
Contract::Requires(static_cast<uint64_t>(start) + static_cast<uint64_t>(length) <= static_cast<uint64_t>(m_length));
|
||||
return ReadOnlyMemory<T>(m_buffer, m_index + start, length);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Memory<T> Memory<T>::Slice(uint32_t start, uint32_t length) noexcept
|
||||
{
|
||||
Contract::Requires(static_cast<uint64_t>(start) + static_cast<uint64_t>(length) <= static_cast<uint64_t>(m_length));
|
||||
return Memory<T>(m_buffer, m_index + start, length);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ReadOnlyMemory<T> Memory<T>::Slice(uint32_t start) const noexcept
|
||||
{
|
||||
Contract::Requires(start <= m_length);
|
||||
return ReadOnlyMemory<T>(m_buffer, m_index + start, m_length - start);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Memory<T> Memory<T>::Slice(uint32_t start) noexcept
|
||||
{
|
||||
Contract::Requires(start <= m_length);
|
||||
return Memory<T>(m_buffer, m_index + start, m_length - start);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool Memory<T>::operator==(const Memory<T>& rhs) const noexcept
|
||||
{
|
||||
return m_buffer == rhs.m_buffer && m_index == rhs.m_index && m_length == rhs.m_length;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool Memory<T>::operator!=(const Memory<T>& rhs) const noexcept { return !operator==(rhs); }
|
||||
}
|
||||
62
src/Core/Core.Native/MemoryMarshal.h
Normal file
62
src/Core/Core.Native/MemoryMarshal.h
Normal file
@@ -0,0 +1,62 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Memory.h"
|
||||
#include "Span.h"
|
||||
#include "ReadOnlySpan.h"
|
||||
|
||||
namespace cdb_core
|
||||
{
|
||||
struct MemoryMarshal final
|
||||
{
|
||||
MemoryMarshal() = delete;
|
||||
|
||||
template<typename T, typename U>
|
||||
static Span<U> Cast(Span<T> source)
|
||||
{
|
||||
const size_t lengthU = (source.m_length * sizeof(T)) / sizeof(U);
|
||||
return Span<U>(reinterpret_cast<U*>(source.m_pointer), static_cast<uint32_t>(lengthU));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void Write(Span<std::byte> destination, const T& value)
|
||||
{
|
||||
static_assert(is_blittable_v<T>, "Only blittable types can be used in a Span<T>.");
|
||||
Contract::Requires(destination.m_length >= sizeof(T));
|
||||
*reinterpret_cast<T*>(destination.m_pointer) = value;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static const T& Read(const Span<std::byte>& source)
|
||||
{
|
||||
static_assert(is_blittable_v<T>, "Only blittable types can be used in a Span<T>.");
|
||||
Contract::Requires(source.m_length >= sizeof(T));
|
||||
return *reinterpret_cast<T*>(source.m_pointer);
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
static ReadOnlySpan<U> Cast(const ReadOnlySpan<T> source)
|
||||
{
|
||||
const size_t lengthU = (source.m_length * sizeof(T)) / sizeof(U);
|
||||
return ReadOnlySpan<U>(reinterpret_cast<const U*>(source.m_pointer), static_cast<uint32_t>(lengthU));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static const T& Read(const ReadOnlySpan<std::byte>& source)
|
||||
{
|
||||
static_assert(is_blittable_v<T>, "Only blittable types can be used in a ReadOnlySpan<T>.");
|
||||
Contract::Requires(source.m_length >= sizeof(T));
|
||||
return *reinterpret_cast<const T*>(source.m_pointer);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]] static Memory<T> AsMemory(const ReadOnlyMemory<T>& source) noexcept
|
||||
{
|
||||
static_assert(is_blittable_v<T>, "Only blittable types can be used in a ReadOnlySpan<T>.");
|
||||
return Memory<T>(reinterpret_cast<T*>(source.m_buffer.get()) + source.m_index, source.m_length);
|
||||
}
|
||||
};
|
||||
}
|
||||
107
src/Core/Core.Native/Microsoft.Azure.Cosmos.Core.Native.vcxproj
Normal file
107
src/Core/Core.Native/Microsoft.Azure.Cosmos.Core.Native.vcxproj
Normal file
@@ -0,0 +1,107 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>16.0</VCProjectVersion>
|
||||
<ProjectGuid>{EAED7D41-3DE6-4C41-A0E4-40D53EA3DABA}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>cdb_core</RootNamespace>
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
</Link>
|
||||
<Lib>
|
||||
<AdditionalDependencies>rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
</Link>
|
||||
<Lib>
|
||||
<AdditionalDependencies>rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Base64.h" />
|
||||
<ClInclude Include="Blittable.h" />
|
||||
<ClInclude Include="Contract.h" />
|
||||
<ClInclude Include="Core.Native.h" />
|
||||
<ClInclude Include="Crc32.h" />
|
||||
<ClInclude Include="DeepCompare.h" />
|
||||
<ClInclude Include="Endian.h" />
|
||||
<ClInclude Include="EqualityComparable.h" />
|
||||
<ClInclude Include="Failure.h" />
|
||||
<ClInclude Include="framework.h" />
|
||||
<ClInclude Include="Hashable.h" />
|
||||
<ClInclude Include="HashCode.h" />
|
||||
<ClInclude Include="IsAllSame.h" />
|
||||
<ClInclude Include="make_unique.h" />
|
||||
<ClInclude Include="MemoryMarshal.h" />
|
||||
<ClInclude Include="ReadOnlyMemory.h" />
|
||||
<ClInclude Include="ReadOnlySpan.h" />
|
||||
<ClInclude Include="Result.h" />
|
||||
<ClInclude Include="Stopwatch.h" />
|
||||
<ClInclude Include="TimeSpan.h" />
|
||||
<ClInclude Include="tla.h" />
|
||||
<ClInclude Include="Stringable.h" />
|
||||
<ClInclude Include="Memory.h" />
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="ref_ptr.h" />
|
||||
<ClInclude Include="Span.h" />
|
||||
<ClInclude Include="Strings.h" />
|
||||
<ClInclude Include="Utf8Span.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Contract.cpp" />
|
||||
<ClCompile Include="Crc32.cpp" />
|
||||
<ClCompile Include="Failure.cpp" />
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Stopwatch.cpp" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
</Project>
|
||||
132
src/Core/Core.Native/ReadOnlyMemory.h
Normal file
132
src/Core/Core.Native/ReadOnlyMemory.h
Normal file
@@ -0,0 +1,132 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
|
||||
#include "Blittable.h"
|
||||
#include "Contract.h"
|
||||
#include "ReadOnlySpan.h"
|
||||
|
||||
namespace cdb_core
|
||||
{
|
||||
struct MemoryMarshal;
|
||||
|
||||
/// <summary>
|
||||
/// ReadOnlyMemory represents a read-only view of a owned (possibly shared) contiguous region of arbitrary memory.
|
||||
/// </summary>
|
||||
template<typename T>
|
||||
struct ReadOnlyMemory final
|
||||
{
|
||||
static_assert(is_blittable_v<T>, "Only blittable types can be used in a ReadOnlyMemory<T>.");
|
||||
|
||||
constexpr ReadOnlyMemory() noexcept;
|
||||
~ReadOnlyMemory() noexcept = default;
|
||||
explicit constexpr ReadOnlyMemory(const ReadOnlySpan<T>& buffer) noexcept;
|
||||
template<class Destructor,
|
||||
std::enable_if_t<std::conjunction_v<std::is_move_constructible<Destructor>,
|
||||
std::is_nothrow_invocable<Destructor&, T*&>>, int> = 0>
|
||||
ReadOnlyMemory(T* buffer, uint32_t length, Destructor destructor);
|
||||
|
||||
ReadOnlyMemory(const ReadOnlyMemory<T>&) noexcept = default;
|
||||
ReadOnlyMemory(ReadOnlyMemory<T>&&) noexcept = default;
|
||||
ReadOnlyMemory<T>& operator=(const ReadOnlyMemory<T>& other) noexcept = default;
|
||||
ReadOnlyMemory<T>& operator=(ReadOnlyMemory<T>&& other) noexcept = default;
|
||||
|
||||
/// <summary>The number of items in the ReadOnlyMemory.</summary>
|
||||
[[nodiscard]]
|
||||
uint32_t Length() const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
ReadOnlySpan<T> AsSpan() const noexcept;
|
||||
|
||||
/// <summary>Returns true if length is 0.</summary>
|
||||
[[nodiscard]]
|
||||
bool IsEmpty() const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
ReadOnlyMemory<T> Slice(uint32_t start) const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
ReadOnlyMemory<T> Slice(uint32_t start, uint32_t length) const noexcept;
|
||||
|
||||
bool operator==(const ReadOnlyMemory<T>& rhs) const noexcept;
|
||||
bool operator!=(const ReadOnlyMemory<T>& rhs) const noexcept;
|
||||
|
||||
private:
|
||||
friend struct Memory<T>;
|
||||
friend struct MemoryMarshal;
|
||||
ReadOnlyMemory(std::shared_ptr<std::byte[]> buffer, uint32_t index, uint32_t length) noexcept;
|
||||
|
||||
std::shared_ptr<std::byte[]> m_buffer;
|
||||
uint32_t m_index;
|
||||
uint32_t m_length;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
constexpr ReadOnlyMemory<T>::ReadOnlyMemory() noexcept :
|
||||
m_buffer(),
|
||||
m_index(0),
|
||||
m_length(0) {}
|
||||
|
||||
template<typename T>
|
||||
constexpr ReadOnlyMemory<T>::ReadOnlyMemory(const ReadOnlySpan<T>& buffer) noexcept :
|
||||
m_buffer(buffer.Length() == 0 ? nullptr : reinterpret_cast<std::byte*>(new T[buffer.Length()])),
|
||||
m_index(0),
|
||||
m_length(buffer.Length())
|
||||
{
|
||||
buffer.CopyTo(AsSpan());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
template<class Destructor,
|
||||
std::enable_if_t<std::conjunction_v<std::is_move_constructible<Destructor>,
|
||||
std::is_nothrow_invocable<Destructor&, T*&>>, int>>
|
||||
ReadOnlyMemory<T>::ReadOnlyMemory(T* buffer, uint32_t length, Destructor destructor) : m_buffer(reinterpret_cast<std::byte*>(buffer),
|
||||
destructor),
|
||||
m_index(0),
|
||||
m_length(length) {}
|
||||
|
||||
template<typename T>
|
||||
ReadOnlyMemory<T>::ReadOnlyMemory(std::shared_ptr<std::byte[]> buffer, uint32_t index, uint32_t length) noexcept :
|
||||
m_buffer(std::move(buffer)),
|
||||
m_index(index),
|
||||
m_length(length) {}
|
||||
|
||||
template<typename T>
|
||||
uint32_t ReadOnlyMemory<T>::Length() const noexcept { return m_length; }
|
||||
|
||||
template<typename T>
|
||||
ReadOnlySpan<T> ReadOnlyMemory<T>::AsSpan() const noexcept
|
||||
{
|
||||
return ReadOnlySpan<T>(reinterpret_cast<T*>(m_buffer.get()) + m_index, m_length);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool ReadOnlyMemory<T>::IsEmpty() const noexcept { return m_length == 0; }
|
||||
|
||||
template<typename T>
|
||||
ReadOnlyMemory<T> ReadOnlyMemory<T>::Slice(uint32_t start, uint32_t length) const noexcept
|
||||
{
|
||||
Contract::Requires(static_cast<uint64_t>(start) + static_cast<uint64_t>(length) <= static_cast<uint64_t>(m_length));
|
||||
return ReadOnlyMemory<T>(m_buffer, m_index + start, length);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ReadOnlyMemory<T> ReadOnlyMemory<T>::Slice(uint32_t start) const noexcept
|
||||
{
|
||||
Contract::Requires(start <= m_length);
|
||||
return ReadOnlyMemory<T>(m_buffer, m_index + start, m_length - start);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool ReadOnlyMemory<T>::operator==(const ReadOnlyMemory<T>& rhs) const noexcept
|
||||
{
|
||||
return m_buffer == rhs.m_buffer && m_index == rhs.m_index && m_length == rhs.m_length;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool ReadOnlyMemory<T>::operator!=(const ReadOnlyMemory<T>& rhs) const noexcept { return !operator==(rhs); }
|
||||
}
|
||||
173
src/Core/Core.Native/ReadOnlySpan.h
Normal file
173
src/Core/Core.Native/ReadOnlySpan.h
Normal file
@@ -0,0 +1,173 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include "Blittable.h"
|
||||
|
||||
namespace cdb_core
|
||||
{
|
||||
template<typename T> struct Span;
|
||||
|
||||
/// <summary>A readonly, bounds-checked, pointer to an buffer.</summary>
|
||||
/// <remarks></remarks>
|
||||
template<class T>
|
||||
struct ReadOnlySpan
|
||||
{
|
||||
static_assert(is_blittable_v<T>, "Only blittable types can be used in a ReadOnlySpan<T>.");
|
||||
|
||||
constexpr ReadOnlySpan() noexcept;
|
||||
~ReadOnlySpan() noexcept = default;
|
||||
constexpr ReadOnlySpan(const T* pointer, uint32_t length) noexcept;
|
||||
template<size_t N> constexpr ReadOnlySpan(const std::array<T, N>&) noexcept;
|
||||
constexpr ReadOnlySpan(const std::vector<T>&) noexcept;
|
||||
constexpr ReadOnlySpan(const Span<T>&) noexcept;
|
||||
ReadOnlySpan(const ReadOnlySpan<T>&) noexcept = default;
|
||||
ReadOnlySpan(ReadOnlySpan<T>&&) noexcept = default;
|
||||
ReadOnlySpan<T>& operator=(const ReadOnlySpan<T>& other) noexcept = default;
|
||||
ReadOnlySpan<T>& operator=(ReadOnlySpan<T>&& other) noexcept = default;
|
||||
|
||||
/// <summary>Returns a reference to specified element of the ReadOnlySpan.</summary>
|
||||
constexpr const T& operator[](uint32_t index) const noexcept;
|
||||
|
||||
/// <summary>The number of items in the span.</summary>
|
||||
[[nodiscard]]
|
||||
constexpr uint32_t Length() const noexcept;
|
||||
|
||||
/// <summary>Returns true if Length is 0.</summary>
|
||||
[[nodiscard]]
|
||||
constexpr bool IsEmpty() const noexcept;
|
||||
|
||||
bool operator==(const ReadOnlySpan<T>& rhs) const noexcept;
|
||||
bool operator!=(const ReadOnlySpan<T>& rhs) const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
ReadOnlySpan<T> Slice(uint32_t start) const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
ReadOnlySpan<T> Slice(uint32_t start, uint32_t length) const noexcept;
|
||||
|
||||
void CopyTo(Span<T> destination) const noexcept;
|
||||
|
||||
template<typename = std::enable_if_t<std::is_same_v<std::byte, T>>>
|
||||
[[nodiscard]] constexpr int SequenceCompareTo(ReadOnlySpan<T> other) const noexcept;
|
||||
|
||||
template<typename = std::enable_if_t<std::is_same_v<std::byte, T>>>
|
||||
[[nodiscard]] constexpr bool SequenceEqual(ReadOnlySpan<T> other) const noexcept;
|
||||
|
||||
struct ReadOnlySpanIter
|
||||
{
|
||||
bool operator==(const ReadOnlySpanIter& other) const noexcept { return m_pointer == other.m_pointer; }
|
||||
bool operator!=(const ReadOnlySpanIter& other) const noexcept { return m_pointer != other.m_pointer; }
|
||||
|
||||
const ReadOnlySpanIter& operator++() noexcept
|
||||
{
|
||||
++m_pointer;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const T& operator*() const noexcept { return *m_pointer; }
|
||||
private:
|
||||
explicit ReadOnlySpanIter(const T* p) : m_pointer(p) {}
|
||||
friend struct ReadOnlySpan<T>;
|
||||
const T* m_pointer;
|
||||
};
|
||||
|
||||
[[nodiscard]] ReadOnlySpanIter begin() const noexcept { return ReadOnlySpanIter{m_pointer}; }
|
||||
[[nodiscard]] ReadOnlySpanIter end() const noexcept { return ReadOnlySpanIter{m_pointer + m_length}; }
|
||||
|
||||
private:
|
||||
friend struct MemoryMarshal;
|
||||
template<typename U> friend struct Span;
|
||||
|
||||
const T* m_pointer;
|
||||
uint32_t m_length;
|
||||
};
|
||||
|
||||
template<class T> constexpr ReadOnlySpan<T>::ReadOnlySpan() noexcept : m_pointer(nullptr), m_length(0) {}
|
||||
|
||||
template<class T> constexpr ReadOnlySpan<T>::ReadOnlySpan(const T* pointer, uint32_t length) noexcept :
|
||||
m_pointer(pointer),
|
||||
m_length(length) { }
|
||||
|
||||
template<class T> template<size_t N> constexpr ReadOnlySpan<T>::ReadOnlySpan(const std::array<T, N>& arr) noexcept :
|
||||
m_pointer{arr.data()},
|
||||
m_length{static_cast<uint32_t>(arr.size())} { }
|
||||
|
||||
// Class Template Argument Deduction (CTAD) hint
|
||||
template<typename T, size_t N> ReadOnlySpan(std::array<T, N>&) noexcept -> ReadOnlySpan<T>;
|
||||
|
||||
template<class T> constexpr ReadOnlySpan<T>::ReadOnlySpan(const std::vector<T>& arr) noexcept :
|
||||
m_pointer{arr.data()},
|
||||
m_length{static_cast<uint32_t>(arr.size())} { }
|
||||
|
||||
// Class Template Argument Deduction (CTAD) hint
|
||||
template<typename T> ReadOnlySpan(const std::vector<T>& arr) noexcept -> ReadOnlySpan<T>;
|
||||
|
||||
template<class T> constexpr ReadOnlySpan<T>::ReadOnlySpan(const Span<T>& span) noexcept :
|
||||
m_pointer(span.m_pointer),
|
||||
m_length(span.m_length) { }
|
||||
|
||||
// Class Template Argument Deduction (CTAD) hint
|
||||
template<typename T> ReadOnlySpan(const Span<T>& span) noexcept -> ReadOnlySpan<T>;
|
||||
|
||||
template<class T> constexpr const T& ReadOnlySpan<T>::operator[](uint32_t index) const noexcept
|
||||
{
|
||||
Contract::Assert(index < m_length);
|
||||
return m_pointer[index];
|
||||
}
|
||||
|
||||
template<class T> constexpr uint32_t ReadOnlySpan<T>::Length() const noexcept { return m_length; }
|
||||
|
||||
template<class T> constexpr bool ReadOnlySpan<T>::IsEmpty() const noexcept { return m_length == 0; }
|
||||
|
||||
template<typename T>
|
||||
bool ReadOnlySpan<T>::operator==(const ReadOnlySpan<T>& rhs) const noexcept
|
||||
{
|
||||
return m_pointer == rhs.m_pointer && m_length == rhs.m_length;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool ReadOnlySpan<T>::operator!=(const ReadOnlySpan<T>& rhs) const noexcept { return !operator==(rhs); }
|
||||
|
||||
template<typename T>
|
||||
ReadOnlySpan<T> ReadOnlySpan<T>::Slice(uint32_t start, uint32_t length) const noexcept
|
||||
{
|
||||
Contract::Requires(static_cast<uint64_t>(start) + static_cast<uint64_t>(length) <= static_cast<uint64_t>(m_length));
|
||||
return ReadOnlySpan<T>(m_pointer + start, length);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ReadOnlySpan<T> ReadOnlySpan<T>::Slice(uint32_t start) const noexcept
|
||||
{
|
||||
Contract::Requires(start <= m_length);
|
||||
return ReadOnlySpan<T>(m_pointer + start, m_length - start);
|
||||
}
|
||||
|
||||
template<typename T> void ReadOnlySpan<T>::CopyTo(Span<T> destination) const noexcept
|
||||
{
|
||||
Contract::Requires(m_length <= destination.m_length);
|
||||
::memmove_s(static_cast<void*>(destination.m_pointer), destination.m_length * sizeof(T),
|
||||
static_cast<void*>(const_cast<T*>(m_pointer)), m_length * sizeof(T));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
template<typename>
|
||||
constexpr int ReadOnlySpan<T>::SequenceCompareTo(ReadOnlySpan<T> other) const noexcept
|
||||
{
|
||||
uint32_t len = std::min<uint32_t>(m_length, other.m_length);
|
||||
int cmp = ::memcmp(m_pointer, other.m_pointer, len * sizeof(T));
|
||||
return (cmp == 0) ? static_cast<int>(m_length - other.m_length) : cmp;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
template<typename>
|
||||
constexpr bool ReadOnlySpan<T>::SequenceEqual(ReadOnlySpan<T> other) const noexcept
|
||||
{
|
||||
return m_length == other.m_length &&
|
||||
::memcmp(m_pointer, other.m_pointer, m_length * sizeof(T)) == 0;
|
||||
}
|
||||
}
|
||||
98
src/Core/Core.Native/Result.h
Normal file
98
src/Core/Core.Native/Result.h
Normal file
@@ -0,0 +1,98 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
#include "Failure.h"
|
||||
#include <variant>
|
||||
|
||||
namespace cdb_core
|
||||
{
|
||||
/// <summary>
|
||||
/// Result is analogous to an "Either" - holds a successful
|
||||
/// result or a failure. Result<> is a value type.
|
||||
/// </summary>
|
||||
template<typename TResult>
|
||||
class [[nodiscard]] Result
|
||||
{
|
||||
public:
|
||||
constexpr Result(TResult result) noexcept;
|
||||
constexpr Result(Failure failure) noexcept;
|
||||
Result(Result<TResult>&&) noexcept = default;
|
||||
~Result() = default;
|
||||
Result& operator=(Result<TResult>&&) noexcept = default;
|
||||
|
||||
[[nodiscard]] bool IsSuccess() const;
|
||||
TResult GetResult();
|
||||
|
||||
[[nodiscard]] Failure GetFailure() const noexcept;
|
||||
|
||||
private:
|
||||
std::variant<TResult, Failure> m_value;
|
||||
};
|
||||
|
||||
template<typename TResult>
|
||||
constexpr Result<TResult>::Result(TResult result) noexcept : m_value{ std::in_place_index<0>, std::move(result) } { }
|
||||
|
||||
template<typename TResult>
|
||||
constexpr Result<TResult>::Result(Failure failure) noexcept : m_value{ std::in_place_index<1>, std::move(failure) } { }
|
||||
|
||||
template<typename TResult>
|
||||
TResult Result<TResult>::GetResult()
|
||||
{
|
||||
Contract::Requires(IsSuccess());
|
||||
return std::move(std::get<0>(m_value));
|
||||
}
|
||||
|
||||
template<typename TResult>
|
||||
bool Result<TResult>::IsSuccess() const
|
||||
{
|
||||
return m_value.index() == 0;
|
||||
}
|
||||
|
||||
template<typename TResult>
|
||||
Failure Result<TResult>::GetFailure() const noexcept
|
||||
{
|
||||
Contract::Requires(!IsSuccess());
|
||||
return std::move(std::get<1>(m_value));
|
||||
}
|
||||
|
||||
template<>
|
||||
class [[nodiscard]] Result<void>
|
||||
{
|
||||
public:
|
||||
Result();
|
||||
explicit Result(Failure failure);
|
||||
Result(const Result<void>&) = default;
|
||||
Result(Result<void>&&) = default;
|
||||
Result<void>& operator=(const Result<void>&) = default;
|
||||
Result<void>& operator=(Result<void>&&) = default;
|
||||
|
||||
[[nodiscard]]
|
||||
bool IsSuccess() const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
Failure GetFailure() const noexcept;
|
||||
|
||||
private:
|
||||
Failure m_failure;
|
||||
};
|
||||
|
||||
inline Result<void>::Result() : m_failure(Failure::None) { }
|
||||
|
||||
inline Result<void>::Result(Failure failure) : m_failure(failure)
|
||||
{
|
||||
Contract::Requires(failure.IsFailed(),
|
||||
"Result constructed with Failure must be failed. Use Result() if no failure occurred");
|
||||
}
|
||||
|
||||
inline bool Result<void>::IsSuccess() const noexcept
|
||||
{
|
||||
return !m_failure.IsFailed();
|
||||
}
|
||||
|
||||
inline Failure Result<void>::GetFailure() const noexcept
|
||||
{
|
||||
return m_failure;
|
||||
}
|
||||
}
|
||||
192
src/Core/Core.Native/Span.h
Normal file
192
src/Core/Core.Native/Span.h
Normal file
@@ -0,0 +1,192 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include "Blittable.h"
|
||||
|
||||
namespace cdb_core
|
||||
{
|
||||
template<typename T> struct ReadOnlySpan;
|
||||
|
||||
/// <summary>A bounds-checked, pointer to an buffer.</summary>
|
||||
/// <remarks></remarks>
|
||||
template<class T>
|
||||
struct Span
|
||||
{
|
||||
static_assert(is_blittable_v<T>, "Only blittable types can be used in a Span<T>.");
|
||||
|
||||
constexpr Span() noexcept;
|
||||
~Span() noexcept = default;
|
||||
constexpr Span(T* pointer, uint32_t length) noexcept;
|
||||
template<size_t N> constexpr Span(std::array<T, N>&) noexcept;
|
||||
constexpr Span(std::vector<T>&) noexcept;
|
||||
Span(const Span<T>&) noexcept = default;
|
||||
Span(Span<T>&&) noexcept = default;
|
||||
Span<T>& operator=(const Span<T>& other) noexcept = default;
|
||||
Span<T>& operator=(Span<T>&& other) noexcept = default;
|
||||
|
||||
/// <summary>Returns a reference to specified element of the Span.</summary>
|
||||
const T& operator[](uint32_t index) const noexcept;
|
||||
T& operator[](uint32_t index) noexcept;
|
||||
|
||||
/// <summary>The number of items in the span.</summary>
|
||||
[[nodiscard]] uint32_t Length() const noexcept;
|
||||
|
||||
/// <summary>Returns true if Length is 0.</summary>
|
||||
[[nodiscard]] bool IsEmpty() const noexcept;
|
||||
|
||||
bool operator==(const Span<T>& rhs) const noexcept;
|
||||
bool operator!=(const Span<T>& rhs) const noexcept;
|
||||
|
||||
[[nodiscard]] ReadOnlySpan<T> Slice(uint32_t start) const noexcept;
|
||||
[[nodiscard]] Span<T> Slice(uint32_t start) noexcept;
|
||||
|
||||
[[nodiscard]] ReadOnlySpan<T> Slice(uint32_t start, uint32_t length) const noexcept;
|
||||
[[nodiscard]] Span<T> Slice(uint32_t start, uint32_t length) noexcept;
|
||||
|
||||
void CopyTo(Span<T> destination) const noexcept;
|
||||
|
||||
template<typename = std::enable_if_t<std::is_same_v<std::byte, T>>>
|
||||
void Fill(std::byte value) noexcept;
|
||||
|
||||
template<typename = std::enable_if_t<std::is_same_v<std::byte, T>>>
|
||||
[[nodiscard]] constexpr int SequenceCompareTo(ReadOnlySpan<T> other) const noexcept;
|
||||
|
||||
template<typename = std::enable_if_t<std::is_same_v<std::byte, T>>>
|
||||
[[nodiscard]] constexpr bool SequenceEqual(ReadOnlySpan<T> other) const noexcept;
|
||||
|
||||
struct SpanIter
|
||||
{
|
||||
bool operator==(const SpanIter& other) const noexcept { return m_pointer == other.m_pointer; }
|
||||
bool operator!=(const SpanIter& other) const noexcept { return m_pointer != other.m_pointer; }
|
||||
|
||||
const SpanIter& operator++() noexcept
|
||||
{
|
||||
++m_pointer;
|
||||
return *this;
|
||||
}
|
||||
|
||||
T& operator*() const noexcept { return *m_pointer; }
|
||||
private:
|
||||
explicit SpanIter(T* p) : m_pointer(p) {}
|
||||
friend struct Span<T>;
|
||||
T* m_pointer;
|
||||
};
|
||||
|
||||
[[nodiscard]] SpanIter begin() const noexcept { return SpanIter{m_pointer}; }
|
||||
[[nodiscard]] SpanIter end() const noexcept { return SpanIter{m_pointer + m_length}; }
|
||||
|
||||
private:
|
||||
friend struct MemoryMarshal;
|
||||
template<typename U> friend struct ReadOnlySpan;
|
||||
|
||||
T* m_pointer;
|
||||
uint32_t m_length;
|
||||
};
|
||||
|
||||
template<class T> constexpr Span<T>::Span() noexcept : m_pointer(nullptr), m_length(0) {}
|
||||
|
||||
template<class T>
|
||||
constexpr Span<T>::Span(T* pointer, uint32_t length) noexcept : m_pointer(pointer), m_length(length) { }
|
||||
|
||||
template<class T> template<size_t N> constexpr Span<T>::Span(std::array<T, N>& arr) noexcept :
|
||||
m_pointer{arr.data()},
|
||||
m_length{static_cast<uint32_t>(arr.size())} { }
|
||||
|
||||
// Class Template Argument Deduction (CTAD) hint
|
||||
template<typename T, size_t N> Span(std::array<T, N>&) noexcept -> Span<T>;
|
||||
|
||||
template<class T> constexpr Span<T>::Span(std::vector<T>& arr) noexcept :
|
||||
m_pointer{arr.data()},
|
||||
m_length{static_cast<uint32_t>(arr.size())} { }
|
||||
|
||||
// Class Template Argument Deduction (CTAD) hint
|
||||
template<typename T, size_t N> Span(std::vector<T>&) noexcept -> Span<T>;
|
||||
|
||||
template<class T> const T& Span<T>::operator[](uint32_t index) const noexcept
|
||||
{
|
||||
Contract::Assert(index < m_length);
|
||||
return m_pointer[index];
|
||||
}
|
||||
|
||||
template<class T> T& Span<T>::operator[](uint32_t index) noexcept
|
||||
{
|
||||
Contract::Assert(index < m_length);
|
||||
return m_pointer[index];
|
||||
}
|
||||
|
||||
template<class T> uint32_t Span<T>::Length() const noexcept { return m_length; }
|
||||
|
||||
template<class T> bool Span<T>::IsEmpty() const noexcept { return m_length == 0; }
|
||||
|
||||
template<typename T>
|
||||
bool Span<T>::operator==(const Span<T>& rhs) const noexcept
|
||||
{
|
||||
return m_pointer == rhs.m_pointer && m_length == rhs.m_length;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool Span<T>::operator!=(const Span<T>& rhs) const noexcept { return !operator==(rhs); }
|
||||
|
||||
template<typename T>
|
||||
ReadOnlySpan<T> Span<T>::Slice(uint32_t start, uint32_t length) const noexcept
|
||||
{
|
||||
Contract::Requires(static_cast<uint64_t>(start) + static_cast<uint64_t>(length) <= static_cast<uint64_t>(m_length));
|
||||
return Span<T>(m_pointer + start, length);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Span<T> Span<T>::Slice(uint32_t start, uint32_t length) noexcept
|
||||
{
|
||||
Contract::Requires(static_cast<uint64_t>(start) + static_cast<uint64_t>(length) <= static_cast<uint64_t>(m_length));
|
||||
return Span<T>(m_pointer + start, length);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ReadOnlySpan<T> Span<T>::Slice(uint32_t start) const noexcept
|
||||
{
|
||||
Contract::Requires(start <= m_length);
|
||||
return ReadOnlySpan<T>(m_pointer + start, m_length - start);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Span<T> Span<T>::Slice(uint32_t start) noexcept
|
||||
{
|
||||
Contract::Requires(start <= m_length);
|
||||
return Span<T>(m_pointer + start, m_length - start);
|
||||
}
|
||||
|
||||
template<typename T> void Span<T>::CopyTo(Span<T> destination) const noexcept
|
||||
{
|
||||
Contract::Requires(m_length <= destination.m_length);
|
||||
::memmove_s(static_cast<void*>(destination.m_pointer), destination.m_length * sizeof(T),
|
||||
static_cast<void*>(m_pointer), m_length * sizeof(T));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
template<typename>
|
||||
void Span<T>::Fill(std::byte value) noexcept
|
||||
{
|
||||
::memset(m_pointer, static_cast<int>(value), m_length * sizeof(T));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
template<typename>
|
||||
constexpr int Span<T>::SequenceCompareTo(ReadOnlySpan<T> other) const noexcept
|
||||
{
|
||||
uint32_t len = std::min<uint32_t>(m_length, other.m_length);
|
||||
int cmp = ::memcmp(m_pointer, other.m_pointer, len * sizeof(T));
|
||||
return (cmp == 0) ? static_cast<int>(m_length - other.m_length) : cmp;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
template<typename>
|
||||
constexpr bool Span<T>::SequenceEqual(ReadOnlySpan<T> other) const noexcept
|
||||
{
|
||||
return m_length == other.m_length &&
|
||||
::memcmp(m_pointer, other.m_pointer, m_length * sizeof(T)) == 0;
|
||||
}
|
||||
}
|
||||
45
src/Core/Core.Native/Stopwatch.cpp
Normal file
45
src/Core/Core.Native/Stopwatch.cpp
Normal file
@@ -0,0 +1,45 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#include "pch.h"
|
||||
#include "Stopwatch.h"
|
||||
|
||||
namespace cdb_core
|
||||
{
|
||||
static LARGE_INTEGER InitFrequency() noexcept
|
||||
{
|
||||
LARGE_INTEGER freq;
|
||||
QueryPerformanceFrequency(&freq);
|
||||
return freq;
|
||||
}
|
||||
|
||||
LARGE_INTEGER Stopwatch::s_frequency = InitFrequency();
|
||||
|
||||
void Stopwatch::Start() noexcept
|
||||
{
|
||||
if (m_running)
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_running = true;
|
||||
QueryPerformanceCounter(&m_start);
|
||||
}
|
||||
|
||||
void Stopwatch::Stop() noexcept
|
||||
{
|
||||
if (!m_running)
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_running = false;
|
||||
LARGE_INTEGER end;
|
||||
QueryPerformanceCounter(&end);
|
||||
m_elapsed.QuadPart += end.QuadPart - m_start.QuadPart;
|
||||
}
|
||||
|
||||
void Stopwatch::Reset() noexcept
|
||||
{
|
||||
m_elapsed.QuadPart = 0;
|
||||
}
|
||||
}
|
||||
48
src/Core/Core.Native/Stopwatch.h
Normal file
48
src/Core/Core.Native/Stopwatch.h
Normal file
@@ -0,0 +1,48 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "TimeSpan.h"
|
||||
|
||||
namespace cdb_core
|
||||
{
|
||||
class Stopwatch final
|
||||
{
|
||||
public:
|
||||
Stopwatch() noexcept = default;
|
||||
~Stopwatch() noexcept = default;
|
||||
Stopwatch(const Stopwatch& other) = default;
|
||||
Stopwatch(Stopwatch&& other) noexcept = default;
|
||||
Stopwatch& operator=(const Stopwatch& other) = default;
|
||||
Stopwatch& operator=(Stopwatch&& other) noexcept = default;
|
||||
|
||||
void Start() noexcept;
|
||||
void Stop() noexcept;
|
||||
void Reset() noexcept;
|
||||
[[nodiscard]] TimeSpan Elapsed() const noexcept;
|
||||
|
||||
[[nodiscard]] int64_t ElapsedRaw() const noexcept;
|
||||
[[nodiscard]] static TimeSpan AsTimeSpan(int64_t raw) noexcept;
|
||||
|
||||
private:
|
||||
bool m_running;
|
||||
LARGE_INTEGER m_start;
|
||||
LARGE_INTEGER m_elapsed;
|
||||
|
||||
static LARGE_INTEGER s_frequency;
|
||||
};
|
||||
|
||||
inline int64_t Stopwatch::ElapsedRaw() const noexcept { return m_elapsed.QuadPart; }
|
||||
|
||||
inline TimeSpan Stopwatch::Elapsed() const noexcept
|
||||
{
|
||||
return Stopwatch::AsTimeSpan(m_elapsed.QuadPart);
|
||||
}
|
||||
|
||||
inline TimeSpan Stopwatch::AsTimeSpan(int64_t raw) noexcept
|
||||
{
|
||||
return TimeSpan::FromTicks((raw * 10000000) / s_frequency.QuadPart);
|
||||
}
|
||||
}
|
||||
33
src/Core/Core.Native/Stringable.h
Normal file
33
src/Core/Core.Native/Stringable.h
Normal file
@@ -0,0 +1,33 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
#include <string>
|
||||
|
||||
namespace cdb_core
|
||||
{
|
||||
/// <summary>Check for a ToString method.</summary>
|
||||
/// <remarks>Method must have this signature:
|
||||
/// <code>TString ToString() const noexcept</code>
|
||||
/// </remarks>
|
||||
template<typename C, typename TString = std::string>
|
||||
struct is_stringable
|
||||
{
|
||||
private:
|
||||
template<typename T>
|
||||
constexpr static typename std::is_same<decltype(&T::ToString), TString (T::*)() const noexcept>::type Check(T*)
|
||||
{
|
||||
return std::true_type{};
|
||||
};
|
||||
|
||||
template<typename>
|
||||
constexpr static std::false_type Check(...) { return std::false_type{}; };
|
||||
public:
|
||||
constexpr static bool value = decltype(Check<std::remove_const_t<std::remove_reference_t<std::remove_pointer_t<C>>>
|
||||
>(nullptr))::value;
|
||||
};
|
||||
|
||||
template<typename T, typename TString = std::string>
|
||||
inline constexpr bool is_stringable_v = is_stringable<T, TString>::value;
|
||||
}
|
||||
226
src/Core/Core.Native/Strings.h
Normal file
226
src/Core/Core.Native/Strings.h
Normal file
@@ -0,0 +1,226 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "tla.h"
|
||||
|
||||
namespace cdb_core
|
||||
{
|
||||
/// <summary>
|
||||
/// These types are not meant to be used directly but instead exist to facilitate return type inference.
|
||||
/// </summary>
|
||||
namespace Internal
|
||||
{
|
||||
// Forward references.
|
||||
template<typename TFormat, typename... Args> struct StringFormat;
|
||||
template<typename... Args> struct Joiner;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Makes a string using printf style format specifiers.
|
||||
/// </summary>
|
||||
/// <typeparam name="TFormat">The type of the format string.</typeparam>
|
||||
/// <typeparam name="TArgs">The types of the arguments to the format specifiers.</typeparam>
|
||||
/// <param name="format">The format string.</param>
|
||||
/// <param name="args">The arguments to the format specifiers.</param>
|
||||
/// <returns>A string object.</returns>
|
||||
/// <example>
|
||||
/// The return type of this method is not meant to be used directly, but instead to facilitate return type
|
||||
/// inference. E.g.
|
||||
/// <code>
|
||||
/// std::string u8 = make_string("foo: %s", "some value");
|
||||
/// std::wstring u16 = string_join(L"foo: %s", L"some wide-string value");
|
||||
/// tla::string s = string_join("foo: %s", "some value");
|
||||
/// </code>
|
||||
/// </example>
|
||||
template<class TFormat, typename... TArgs>
|
||||
Internal::StringFormat<TFormat, TArgs...> make_string(TFormat format, TArgs ... args)
|
||||
{
|
||||
return Internal::StringFormat<TFormat, TArgs...>(format, args...);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Makes a string using <c>printf</c> style format specifiers.
|
||||
/// </summary>
|
||||
/// <remarks>This is an explicit version of the above method for return type inference.</remarks>
|
||||
/// <typeparam name="TFormat">The type of the format string.</typeparam>
|
||||
/// <typeparam name="TArgs">The types of the arguments to the format specifiers.</typeparam>
|
||||
/// <param name="format">The format string.</param>
|
||||
/// <param name="args">The arguments to the format specifiers.</param>
|
||||
/// <returns>A string object.</returns>
|
||||
/// <example>
|
||||
/// This overload allows you to specify the return type explicitly for use in expressions where
|
||||
/// the return value will be consumed instead of bound to an lvalue.
|
||||
/// <code>
|
||||
/// Logger::WriteMessage(make_string<std::string>("foo: %s", "some value").c_str());
|
||||
/// Logger::WriteMessage(make_string<std::wstring>(L"foo: %s", L"some wide-string value").c_str());
|
||||
/// </code>
|
||||
/// </example>
|
||||
template<class TResult, class TFormat, typename... TArgs>
|
||||
TResult make_string(TFormat format, TArgs ... args)
|
||||
{
|
||||
return Internal::StringFormat<TFormat, TArgs...>(format, args...);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Makes a string by combining existing (possibly heterogeneous) string elements together.
|
||||
/// </summary>
|
||||
/// <typeparam name="TArgs">The types of the string elements to join.</typeparam>
|
||||
/// <param name="args">The string elements to join.</param>
|
||||
/// <returns>A string object.</returns>
|
||||
/// <example>
|
||||
/// The return type of this method is not meant to be used directly, but instead to facilitate return type
|
||||
/// inference. E.g.
|
||||
/// <code>
|
||||
/// std::string u8 = string_join("a", "b", "c");
|
||||
/// std::wstring u16 = string_join(L"a", L"b", L"c");
|
||||
/// tla::string s = string_join("a", "b", "c");
|
||||
/// </code>
|
||||
/// </example>
|
||||
template<typename... Args>
|
||||
Internal::Joiner<Args...> string_join(Args ... args)
|
||||
{
|
||||
return Internal::Joiner<Args...>{args...};
|
||||
}
|
||||
|
||||
namespace Internal
|
||||
{
|
||||
template<typename... Args>
|
||||
struct StringFormatter {};
|
||||
|
||||
template<>
|
||||
struct StringFormatter<>
|
||||
{
|
||||
[[nodiscard]] constexpr StringFormatter() noexcept = default;
|
||||
~StringFormatter() noexcept = default;
|
||||
StringFormatter(const StringFormatter<>& other) noexcept = delete;
|
||||
StringFormatter(StringFormatter<>&& other) noexcept = default;
|
||||
StringFormatter<>& operator=(const StringFormatter<>& other) noexcept = delete;
|
||||
StringFormatter<>& operator=(StringFormatter<>&& other) noexcept = delete;
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning( disable: 4840 )
|
||||
template<typename TResult, typename TFormat, typename... TFormatArgs>
|
||||
std::enable_if_t<std::is_same_v<typename TResult::value_type, char>, TResult> Format(
|
||||
TFormat format, TFormatArgs ... args)
|
||||
{
|
||||
const size_t size = snprintf(nullptr, 0, &format[0], args ...) + 1; // Extra space for '\0'
|
||||
std::unique_ptr<char[]> buf(new char[size]);
|
||||
snprintf(buf.get(), size, &format[0], args...);
|
||||
return TResult(buf.get(), buf.get() + size - 1); // exclude terminating null
|
||||
}
|
||||
|
||||
#pragma warning( disable: 4996 )
|
||||
template<typename TResult, typename TFormat, typename... TFormatArgs>
|
||||
std::enable_if_t<std::is_same_v<typename TResult::value_type, wchar_t>, TResult> Format(
|
||||
TFormat format, TFormatArgs ... args)
|
||||
{
|
||||
const size_t size = _snwprintf(nullptr, 0, &format[0], args ...) + 1; // Extra space for '\0'
|
||||
std::unique_ptr<wchar_t[]> buf(new wchar_t[size]);
|
||||
_snwprintf(buf.get(), size, &format[0], args...);
|
||||
return TResult(buf.get(), buf.get() + size - 1); // exclude terminating null
|
||||
}
|
||||
#pragma warning( pop )
|
||||
};
|
||||
|
||||
template<typename T, typename... Args>
|
||||
struct StringFormatter<T, Args...> : private StringFormatter<Args...>
|
||||
{
|
||||
using Base = StringFormatter<Args...>;
|
||||
|
||||
[[nodiscard]] constexpr StringFormatter(T first, Args ... args) noexcept : Base{args...}, m_first{first} {}
|
||||
~StringFormatter() noexcept = default;
|
||||
StringFormatter(const StringFormatter& other) noexcept = delete;
|
||||
StringFormatter(StringFormatter&& other) noexcept = default;
|
||||
StringFormatter& operator=(const StringFormatter& other) noexcept = delete;
|
||||
StringFormatter& operator=(StringFormatter&& other) noexcept = delete;
|
||||
|
||||
protected:
|
||||
template<typename TResult, typename TFormat, typename... TFormatArgs>
|
||||
TResult Format(TFormat format, TFormatArgs ... args)
|
||||
{
|
||||
return Base::template Format<TResult>(format, args..., m_first);
|
||||
}
|
||||
|
||||
private:
|
||||
T m_first;
|
||||
};
|
||||
|
||||
template<typename TFormat, typename... Args>
|
||||
struct StringFormat : private StringFormatter<Args...>
|
||||
{
|
||||
using Base = StringFormatter<Args...>;
|
||||
|
||||
[[nodiscard]] constexpr StringFormat(TFormat format, Args ... args) noexcept : Base{args...}, m_format{format} {}
|
||||
~StringFormat() noexcept = default;
|
||||
StringFormat(const StringFormat& other) noexcept = delete;
|
||||
StringFormat(StringFormat&& other) noexcept = default;
|
||||
StringFormat& operator=(const StringFormat& other) noexcept = delete;
|
||||
StringFormat& operator=(StringFormat&& other) noexcept = delete;
|
||||
|
||||
// ReSharper disable once CppNonExplicitConversionOperator
|
||||
template<class TElem, class TTraits, class TAlloc>
|
||||
operator std::basic_string<TElem, TTraits, TAlloc>()
|
||||
{
|
||||
return Base::template Format<std::basic_string<TElem, TTraits, TAlloc>>(m_format);
|
||||
}
|
||||
|
||||
private:
|
||||
TFormat m_format;
|
||||
};
|
||||
|
||||
template<typename... Args>
|
||||
struct Joiner {};
|
||||
|
||||
template<>
|
||||
struct Joiner<>
|
||||
{
|
||||
[[nodiscard]] constexpr Joiner() noexcept = default;
|
||||
~Joiner() noexcept = default;
|
||||
Joiner(const Joiner<>& other) noexcept = delete;
|
||||
Joiner(Joiner<>&& other) noexcept = default;
|
||||
Joiner<>& operator=(const Joiner<>& other) noexcept = delete;
|
||||
Joiner<>& operator=(Joiner<>&& other) noexcept = delete;
|
||||
|
||||
template<typename TResult>
|
||||
static void Add(TResult& ret) { }
|
||||
};
|
||||
|
||||
template<typename T, typename... Args>
|
||||
struct Joiner<T, Args...> : private Joiner<Args...>
|
||||
{
|
||||
using Base = Joiner<Args...>;
|
||||
|
||||
[[nodiscard]] constexpr Joiner(T first, Args ... args) noexcept : Base{args...}, m_first{first} {}
|
||||
~Joiner() noexcept = default;
|
||||
Joiner(const Joiner& other) noexcept = delete;
|
||||
Joiner(Joiner&& other) noexcept = default;
|
||||
Joiner& operator=(const Joiner& other) noexcept = delete;
|
||||
Joiner& operator=(Joiner&& other) noexcept = delete;
|
||||
|
||||
// ReSharper disable once CppNonExplicitConversionOperator
|
||||
template<typename TResult>
|
||||
operator TResult()
|
||||
{
|
||||
TResult ret{m_first};
|
||||
Base::Add(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
protected:
|
||||
template<typename TResult>
|
||||
void Add(TResult& ret)
|
||||
{
|
||||
ret += m_first;
|
||||
Base::Add(ret);
|
||||
}
|
||||
|
||||
private:
|
||||
T m_first;
|
||||
};
|
||||
}
|
||||
}
|
||||
83
src/Core/Core.Native/TimeSpan.h
Normal file
83
src/Core/Core.Native/TimeSpan.h
Normal file
@@ -0,0 +1,83 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
|
||||
namespace cdb_core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a time interval.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// TimeSpan mimics the behavior of C# TimeSpan and provides APIs for getting time intervals
|
||||
/// in similar APIs for various durations.
|
||||
/// </remarks>
|
||||
struct TimeSpan final
|
||||
{
|
||||
public:
|
||||
static TimeSpan FromTicks(int64_t oneHundredNanosecondTicks) noexcept;
|
||||
static TimeSpan FromMilliseconds(int64_t milliseconds) noexcept;
|
||||
static TimeSpan FromSeconds(int64_t seconds) noexcept;
|
||||
|
||||
[[nodiscard]] int64_t GetTotalMilliseconds() const noexcept;
|
||||
[[nodiscard]] int64_t GetTotalSeconds() const noexcept;
|
||||
[[nodiscard]] int64_t GetTotalTicks() const noexcept;
|
||||
|
||||
[[nodiscard]] double GetMilliseconds() const noexcept;
|
||||
[[nodiscard]] double GetMicroseconds() const noexcept;
|
||||
|
||||
private:
|
||||
using durationTicks = std::chrono::duration<int64_t, std::ratio<1, 10000000>>;
|
||||
constexpr TimeSpan(durationTicks ticks);
|
||||
durationTicks m_ticks;
|
||||
};
|
||||
|
||||
constexpr TimeSpan::TimeSpan(durationTicks ticks) : m_ticks(ticks) { }
|
||||
|
||||
inline TimeSpan TimeSpan::FromTicks(int64_t oneHundredNanosecondTicks) noexcept
|
||||
{
|
||||
durationTicks durationTicks{oneHundredNanosecondTicks};
|
||||
return {durationTicks};
|
||||
}
|
||||
|
||||
inline TimeSpan TimeSpan::FromMilliseconds(int64_t milliseconds) noexcept
|
||||
{
|
||||
std::chrono::milliseconds durationMs{milliseconds};
|
||||
return {std::chrono::duration_cast<durationTicks>(durationMs)};
|
||||
}
|
||||
|
||||
inline TimeSpan TimeSpan::FromSeconds(int64_t seconds) noexcept
|
||||
{
|
||||
std::chrono::seconds durationSec{seconds};
|
||||
return {std::chrono::duration_cast<durationTicks>(durationSec)};
|
||||
}
|
||||
|
||||
[[nodiscard]] inline int64_t TimeSpan::GetTotalMilliseconds() const noexcept
|
||||
{
|
||||
return std::chrono::duration_cast<std::chrono::milliseconds>(m_ticks).count();
|
||||
}
|
||||
|
||||
[[nodiscard]] inline int64_t TimeSpan::GetTotalSeconds() const noexcept
|
||||
{
|
||||
return std::chrono::duration_cast<std::chrono::seconds>(m_ticks).count();
|
||||
}
|
||||
|
||||
[[nodiscard]] inline int64_t TimeSpan::GetTotalTicks() const noexcept
|
||||
{
|
||||
return m_ticks.count();
|
||||
}
|
||||
|
||||
[[nodiscard]] inline double TimeSpan::GetMilliseconds() const noexcept
|
||||
{
|
||||
return
|
||||
std::chrono::duration_cast<std::chrono::duration<double, std::chrono::milliseconds::period>>(m_ticks).count();
|
||||
}
|
||||
|
||||
[[nodiscard]] inline double TimeSpan::GetMicroseconds() const noexcept
|
||||
{
|
||||
return std::chrono::duration_cast<std::chrono::duration<double, std::chrono::microseconds::period>>(m_ticks).count();
|
||||
}
|
||||
}
|
||||
44
src/Core/Core.Native/Utf8Span.h
Normal file
44
src/Core/Core.Native/Utf8Span.h
Normal file
@@ -0,0 +1,44 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
#include "ReadOnlySpan.h"
|
||||
|
||||
namespace cdb_core
|
||||
{
|
||||
struct Utf8Span final
|
||||
{
|
||||
Utf8Span() = delete;
|
||||
~Utf8Span() = delete;
|
||||
Utf8Span(const Utf8Span& other) = delete;
|
||||
Utf8Span(Utf8Span&& other) noexcept = delete;
|
||||
Utf8Span& operator=(const Utf8Span& other) = delete;
|
||||
Utf8Span& operator=(Utf8Span&& other) noexcept = delete;
|
||||
|
||||
/// <summary>Creates a <see cref="Utf8Span" /> without validating the underlying bytes.</summary>
|
||||
/// <param name="utf8Bytes">The bytes claiming to be UTF8.</param>
|
||||
/// <returns>A <see cref="Utf8Span" /> wrapping <paramref name="utf8Bytes" />.</returns>
|
||||
/// <remarks>
|
||||
/// This method is dangerous as consumers of the <see cref="Utf8Span" /> must assume the
|
||||
/// underlying bytes are indeed valid UTF8. The method should <bold>only</bold> be used when the UTF8
|
||||
/// sequence has already been externally valid or is known to be valid by construction.
|
||||
/// </remarks>
|
||||
constexpr static std::string_view UnsafeFromUtf8BytesNoValidation(const ReadOnlySpan<std::byte>& utf8Bytes) noexcept
|
||||
{
|
||||
return (utf8Bytes.IsEmpty())
|
||||
? std::string_view{}
|
||||
: std::string_view{reinterpret_cast<const char*>(&utf8Bytes[0]), utf8Bytes.Length()};
|
||||
}
|
||||
|
||||
/// <summary>The UTF8 byte sequence.</summary>
|
||||
constexpr static ReadOnlySpan<std::byte> GetSpan(const std::string_view utf8)
|
||||
{
|
||||
// ReSharper disable once CppCStyleCast
|
||||
return ReadOnlySpan<std::byte>{
|
||||
(const std::byte*)(utf8.data()), // NOLINT(clang-diagnostic-old-style-cast)
|
||||
static_cast<uint32_t>(utf8.size())
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
20
src/Core/Core.Native/framework.h
Normal file
20
src/Core/Core.Native/framework.h
Normal file
@@ -0,0 +1,20 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#define STRICT
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#define ENABLE_INTSAFE_SIGNED_FUNCTIONS
|
||||
#define NOMINMAX
|
||||
#include "Windows.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
using std::byte;
|
||||
64
src/Core/Core.Native/make_unique.h
Normal file
64
src/Core/Core.Native/make_unique.h
Normal file
@@ -0,0 +1,64 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
#include <memory>
|
||||
|
||||
namespace cdb_core
|
||||
{
|
||||
// forward declaration
|
||||
template<class F>
|
||||
struct make_unique_with_traits;
|
||||
|
||||
// function pointer
|
||||
template<class R, class Arg>
|
||||
struct make_unique_with_traits<R(*)(Arg)>
|
||||
{
|
||||
using return_type = R;
|
||||
using arg_type = Arg;
|
||||
};
|
||||
|
||||
// member function pointer
|
||||
template<class C, class R, class Arg>
|
||||
struct make_unique_with_traits<R(C::*)(Arg)> : make_unique_with_traits<R(*)(Arg)> {};
|
||||
|
||||
// const member function pointer
|
||||
template<class C, class R, class Arg>
|
||||
struct make_unique_with_traits<R(C::*)(Arg) const> : make_unique_with_traits<R(*)(Arg)> {};
|
||||
|
||||
/// <summary>Trait to derive return type and argument type from a generic callable.</summary>
|
||||
template<class TCallable>
|
||||
struct make_unique_with_traits
|
||||
{
|
||||
using call_type = make_unique_with_traits<decltype(&TCallable::operator())>;
|
||||
using return_type = typename call_type::return_type;
|
||||
using arg_type = std::remove_reference_t<typename call_type::arg_type>;
|
||||
};
|
||||
|
||||
// ReSharper disable once CppInconsistentNaming
|
||||
/// <summary>Makes a new object that uses delayed initialization.</summary>
|
||||
template<typename TCallable,
|
||||
typename Traits = make_unique_with_traits<TCallable>,
|
||||
typename T = typename Traits::arg_type,
|
||||
typename = std::enable_if_t<std::is_invocable_r_v<void, TCallable, T&>>>
|
||||
static std::unique_ptr<T> make_unique_with(TCallable&& initializer)
|
||||
{
|
||||
std::unique_ptr<T> p = std::make_unique<T>();
|
||||
initializer(*p);
|
||||
return std::move(p);
|
||||
}
|
||||
|
||||
// ReSharper disable once CppInconsistentNaming
|
||||
/// <summary>Makes a new object that uses delayed initialization.</summary>
|
||||
template<typename TCallable,
|
||||
typename Traits = make_unique_with_traits<TCallable>,
|
||||
typename T = typename Traits::arg_type,
|
||||
typename = std::enable_if_t<std::is_invocable_r_v<void, TCallable, T&>>>
|
||||
static T make_with(TCallable&& initializer)
|
||||
{
|
||||
T retval = T();
|
||||
initializer(retval);
|
||||
return std::move(retval);
|
||||
}
|
||||
}
|
||||
5
src/Core/Core.Native/pch.cpp
Normal file
5
src/Core/Core.Native/pch.cpp
Normal file
@@ -0,0 +1,5 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#include "pch.h"
|
||||
7
src/Core/Core.Native/pch.h
Normal file
7
src/Core/Core.Native/pch.h
Normal file
@@ -0,0 +1,7 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "framework.h"
|
||||
206
src/Core/Core.Native/ref_ptr.h
Normal file
206
src/Core/Core.Native/ref_ptr.h
Normal file
@@ -0,0 +1,206 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace cdb_core
|
||||
{
|
||||
// ReSharper disable once CppInconsistentNaming
|
||||
template<typename T>
|
||||
class ref_ptr final
|
||||
{
|
||||
public:
|
||||
|
||||
template<class... ArgsType>
|
||||
// ReSharper disable once CppInconsistentNaming
|
||||
[[nodiscard]] constexpr static ref_ptr<T> make(ArgsType&&... args);
|
||||
|
||||
constexpr ref_ptr() noexcept
|
||||
{
|
||||
m_p = nullptr;
|
||||
}
|
||||
|
||||
constexpr ref_ptr(T* lp) noexcept
|
||||
{
|
||||
m_p = lp;
|
||||
|
||||
if (m_p != nullptr)
|
||||
{
|
||||
m_p->AddRef();
|
||||
}
|
||||
}
|
||||
|
||||
constexpr ref_ptr(const ref_ptr<T>& lp) noexcept
|
||||
{
|
||||
m_p = lp.m_p;
|
||||
|
||||
if (m_p != nullptr)
|
||||
{
|
||||
m_p->AddRef();
|
||||
}
|
||||
}
|
||||
|
||||
constexpr ref_ptr(ref_ptr<T>&& lp) noexcept
|
||||
{
|
||||
m_p = lp.m_p;
|
||||
lp.m_p = nullptr;
|
||||
}
|
||||
|
||||
~ref_ptr() noexcept
|
||||
{
|
||||
if (m_p != nullptr)
|
||||
{
|
||||
m_p->Release();
|
||||
m_p = nullptr; // Make sure we AV in case someone is using CComObjectPtr after Release
|
||||
}
|
||||
}
|
||||
|
||||
constexpr const T& operator*() const noexcept
|
||||
{
|
||||
return *m_p;
|
||||
}
|
||||
|
||||
constexpr T& operator*() noexcept
|
||||
{
|
||||
return *m_p;
|
||||
}
|
||||
|
||||
constexpr const T* operator->() const noexcept
|
||||
{
|
||||
return m_p;
|
||||
}
|
||||
|
||||
constexpr T* operator->() noexcept
|
||||
{
|
||||
return m_p;
|
||||
}
|
||||
|
||||
constexpr bool operator!() const noexcept
|
||||
{
|
||||
return (m_p == nullptr);
|
||||
}
|
||||
|
||||
constexpr bool operator<(T* pT) const noexcept
|
||||
{
|
||||
return (m_p < pT);
|
||||
}
|
||||
|
||||
constexpr bool operator>(T* pT) const noexcept
|
||||
{
|
||||
return (m_p < pT);
|
||||
}
|
||||
|
||||
constexpr bool operator<=(T* pT) const noexcept
|
||||
{
|
||||
return (m_p <= pT);
|
||||
}
|
||||
|
||||
constexpr bool operator>=(T* pT) const noexcept
|
||||
{
|
||||
return (m_p < pT);
|
||||
}
|
||||
|
||||
constexpr bool operator==(T* pT) const noexcept
|
||||
{
|
||||
return (m_p == pT);
|
||||
}
|
||||
|
||||
constexpr bool operator!=(T* pT) const noexcept
|
||||
{
|
||||
return (m_p != pT);
|
||||
}
|
||||
|
||||
constexpr bool operator==(ref_ptr<T> pT) const noexcept
|
||||
{
|
||||
return (m_p == pT.m_p);
|
||||
}
|
||||
|
||||
constexpr bool operator!=(ref_ptr<T> pT) const noexcept
|
||||
{
|
||||
return (m_p != pT.m_p);
|
||||
}
|
||||
|
||||
constexpr void reset(T* lp) noexcept
|
||||
{
|
||||
if (m_p != lp)
|
||||
{
|
||||
if (lp != nullptr)
|
||||
{
|
||||
lp->AddRef();
|
||||
}
|
||||
|
||||
if (m_p != nullptr)
|
||||
{
|
||||
m_p->Release();
|
||||
}
|
||||
|
||||
m_p = lp;
|
||||
}
|
||||
}
|
||||
|
||||
// Release the interface and set to nullptr
|
||||
constexpr void reset() noexcept
|
||||
{
|
||||
T* pTemp = m_p;
|
||||
if (pTemp != nullptr)
|
||||
{
|
||||
m_p = nullptr;
|
||||
pTemp->Release();
|
||||
}
|
||||
}
|
||||
|
||||
constexpr ref_ptr& operator=(T* lp) noexcept
|
||||
{
|
||||
reset(lp);
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr ref_ptr& operator=(const ref_ptr<T>& lp) noexcept // NOLINT(bugprone-unhandled-self-assignment, cert-oop54-cpp)
|
||||
{
|
||||
reset(lp.m_p);
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr ref_ptr& operator=(ref_ptr<T>&& lp) noexcept
|
||||
{
|
||||
reset();
|
||||
m_p = lp.m_p;
|
||||
lp.m_p = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Attach to an existing interface (does not AddRef)
|
||||
constexpr void attach(T* p2) noexcept
|
||||
{
|
||||
if (m_p != nullptr)
|
||||
{
|
||||
m_p->Release();
|
||||
}
|
||||
m_p = p2;
|
||||
}
|
||||
|
||||
// Detach the interface (does not Release)
|
||||
constexpr T* release() noexcept
|
||||
{
|
||||
T* pt = m_p;
|
||||
m_p = nullptr;
|
||||
return pt;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr T* get() const noexcept
|
||||
{
|
||||
return m_p;
|
||||
}
|
||||
|
||||
private:
|
||||
T* m_p;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
template<class... ArgsType>
|
||||
[[nodiscard]] constexpr ref_ptr<T> ref_ptr<T>::make(ArgsType&&... args)
|
||||
{
|
||||
return std::move(ref_ptr<T>(new T(std::forward<ArgsType>(args)...)));
|
||||
}
|
||||
}
|
||||
142
src/Core/Core.Native/tla.h
Normal file
142
src/Core/Core.Native/tla.h
Normal file
@@ -0,0 +1,142 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
#include <type_traits>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include <forward_list>
|
||||
#include <list>
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <unordered_set>
|
||||
#include <unordered_map>
|
||||
#include <stack>
|
||||
#include "Contract.h"
|
||||
|
||||
namespace tla
|
||||
{
|
||||
template<typename T>
|
||||
struct allocator final : private std::allocator<T>
|
||||
{
|
||||
static_assert(!std::is_const_v<T>, "The C++ Standard forbids containers of const elements "
|
||||
"because allocator<const T> is ill-formed.");
|
||||
|
||||
using value_type = T;
|
||||
using pointer = T*;
|
||||
using const_pointer = const T*;
|
||||
using reference = T&;
|
||||
using const_reference = const T&;
|
||||
|
||||
using size_type = size_t;
|
||||
using difference_type = ptrdiff_t;
|
||||
|
||||
using propagate_on_container_move_assignment = std::true_type;
|
||||
using is_always_equal = std::true_type;
|
||||
|
||||
[[nodiscard]] constexpr allocator() noexcept = default;
|
||||
~allocator() noexcept = default;
|
||||
[[nodiscard]] constexpr allocator(const allocator& other) noexcept = default;
|
||||
[[nodiscard]] constexpr allocator(allocator&& other) noexcept = default;
|
||||
constexpr allocator& operator=(const allocator& other) noexcept = default;
|
||||
constexpr allocator& operator=(allocator&& other) noexcept = default;
|
||||
|
||||
template<class U>
|
||||
struct rebind
|
||||
{
|
||||
using other = allocator<U>;
|
||||
};
|
||||
|
||||
template<class U>
|
||||
[[nodiscard]] constexpr allocator(const allocator<U>&) noexcept {}
|
||||
|
||||
[[nodiscard]] T* allocate(const size_t n) noexcept
|
||||
{
|
||||
try
|
||||
{
|
||||
return std::allocator<T>::allocate(n);
|
||||
}
|
||||
catch (std::bad_alloc&)
|
||||
{
|
||||
cdb_core::Contract::Fail("Out of memory.");
|
||||
}
|
||||
}
|
||||
|
||||
void deallocate(T* const p, const size_t n) noexcept
|
||||
{
|
||||
std::allocator<T>::deallocate(p, n);
|
||||
}
|
||||
};
|
||||
|
||||
using string = std::basic_string<char, std::char_traits<char>, tla::allocator<char>>;
|
||||
using string_view = std::string_view;
|
||||
using wstring = std::basic_string<wchar_t, std::char_traits<wchar_t>, tla::allocator<wchar_t>>;
|
||||
using wstring_view = std::wstring_view;
|
||||
|
||||
inline namespace literals
|
||||
{
|
||||
inline namespace string_literals
|
||||
{
|
||||
[[nodiscard]] inline string operator"" _s(const char* _Str, size_t _Len)
|
||||
{
|
||||
return string(_Str, _Len);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline wstring operator"" _s(const wchar_t* _Str, size_t _Len)
|
||||
{
|
||||
return wstring(_Str, _Len);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr string_view operator"" _sv(const char* _Str, size_t _Len) noexcept
|
||||
{
|
||||
return string_view(_Str, _Len);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr wstring_view operator"" _sv(const wchar_t* _Str, size_t _Len) noexcept
|
||||
{
|
||||
return wstring_view(_Str, _Len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
using vector = std::vector<T, tla::allocator<T>>;
|
||||
|
||||
template<typename T>
|
||||
using deque = std::deque<T, tla::allocator<T>>;
|
||||
|
||||
template<typename T, typename Container = deque<T>>
|
||||
using stack = std::stack<T, Container>;
|
||||
|
||||
template<typename T>
|
||||
using forward_list = std::forward_list<T, tla::allocator<T>>;
|
||||
|
||||
template<typename T>
|
||||
using list = std::list<T, tla::allocator<T>>;
|
||||
|
||||
template<class Key, class Compare = std::less<Key>>
|
||||
using set = std::set<Key, Compare, tla::allocator<Key>>;
|
||||
|
||||
template<class Key, class T, class Compare = std::less<Key>>
|
||||
using map = std::map<Key, T, Compare, tla::allocator<std::pair<const Key, T>>>;
|
||||
|
||||
template<class Key, class Compare = std::less<Key>>
|
||||
using multiset = std::multiset<Key, Compare, tla::allocator<Key>>;
|
||||
|
||||
template<class Key, class T, class Compare = std::less<Key>>
|
||||
using multimap = std::multimap<Key, T, Compare, tla::allocator<std::pair<const Key, T>>>;
|
||||
|
||||
template<class Key, class Hash = std::hash<Key>, class Pred = std::equal_to<Key>>
|
||||
using unordered_set = std::unordered_set<Key, Hash, Pred, tla::allocator<Key>>;
|
||||
|
||||
template<class Key, class T, class Hash = std::hash<Key>, class Pred = std::equal_to<Key>>
|
||||
using unordered_map = std::unordered_map<Key, T, Hash, Pred, tla::allocator<std::pair<const Key, T>>>;
|
||||
|
||||
template<class Key, class Hash = std::hash<Key>, class Pred = std::equal_to<Key>>
|
||||
using unordered_multiset = std::unordered_multiset<Key, Hash, Pred, tla::allocator<Key>>;
|
||||
|
||||
template<class Key, class T, class Hash = std::hash<Key>, class Pred = std::equal_to<Key>>
|
||||
using unordered_multimap = std::unordered_multimap<Key, T, Hash, Pred, tla::allocator<std::pair<const Key, T>>>;
|
||||
}
|
||||
65
src/Core/Core/ClockResolution.cs
Normal file
65
src/Core/Core/ClockResolution.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Azure.Cosmos.Core
|
||||
{
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
|
||||
public sealed class ClockResolution : IDisposable
|
||||
{
|
||||
private static readonly Timecaps TimeCapabilities;
|
||||
private readonly TimeSpan period;
|
||||
private int disposed;
|
||||
|
||||
static ClockResolution()
|
||||
{
|
||||
int result = ClockResolution.TimeGetDevCaps(ref ClockResolution.TimeCapabilities, Marshal.SizeOf(typeof(Timecaps)));
|
||||
Contract.Assert(result == 0);
|
||||
}
|
||||
|
||||
public ClockResolution(TimeSpan period)
|
||||
{
|
||||
int millis = Convert.ToInt32(period.TotalMilliseconds);
|
||||
millis = Math.Min(Math.Max(ClockResolution.TimeCapabilities.PeriodMin, millis), ClockResolution.TimeCapabilities.PeriodMax);
|
||||
|
||||
int result = ClockResolution.TimeBeginPeriod(millis);
|
||||
Contract.Assert(result == 0);
|
||||
this.period = TimeSpan.FromMilliseconds(millis);
|
||||
}
|
||||
|
||||
public static TimeSpan MinimumPeriod => TimeSpan.FromMilliseconds(ClockResolution.TimeCapabilities.PeriodMin);
|
||||
|
||||
public static TimeSpan MaximumPeriod => TimeSpan.FromMilliseconds(ClockResolution.TimeCapabilities.PeriodMax);
|
||||
|
||||
public TimeSpan Period => this.period;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (Interlocked.CompareExchange(ref this.disposed, 1, 0) == 0)
|
||||
{
|
||||
int millis = Convert.ToInt32(this.period.TotalMilliseconds);
|
||||
_ = ClockResolution.TimeEndPeriod(millis);
|
||||
}
|
||||
}
|
||||
|
||||
[DllImport("winmm.dll", EntryPoint = "timeGetDevCaps")]
|
||||
private static extern int TimeGetDevCaps(ref Timecaps ptc, int cbtc);
|
||||
|
||||
[DllImport("winmm.dll", EntryPoint = "timeBeginPeriod")]
|
||||
private static extern int TimeBeginPeriod(int uPeriod);
|
||||
|
||||
[DllImport("winmm.dll", EntryPoint = "timeEndPeriod")]
|
||||
private static extern int TimeEndPeriod(int uPeriod);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct Timecaps
|
||||
{
|
||||
public readonly int PeriodMin;
|
||||
|
||||
public readonly int PeriodMax;
|
||||
}
|
||||
}
|
||||
}
|
||||
281
src/Core/Core/Collections/Trie.cs
Normal file
281
src/Core/Core/Collections/Trie.cs
Normal file
@@ -0,0 +1,281 @@
|
||||
namespace Microsoft.Azure.Cosmos.Core.Collections
|
||||
{
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
/// <summary>A trie, otherwise known as a prefix tree.</summary>
|
||||
/// <remarks>
|
||||
/// A trie is a map from key to value. Keys are formed by a sequence of symbols in some alphabet.
|
||||
/// Values are merely round-tripped by the data structure and are not otherwise interpreted.
|
||||
/// <p>
|
||||
/// Keys and symbols are compared using the binary collation (i.e. memcmp). Symbols must therefore
|
||||
/// be an unmanaged type (i.e. convertible to byte).
|
||||
/// </p>
|
||||
/// <p>See https://en.wikipedia.org/wiki/Trie for a detailed description of the Trie data structure.</p>
|
||||
/// </remarks>
|
||||
/// <typeparam name="TSymbol">The type of the individual elements that form a key.</typeparam>
|
||||
/// <typeparam name="TValue">The type of the value.</typeparam>
|
||||
public class Trie<TSymbol, TValue>
|
||||
where TSymbol : unmanaged
|
||||
{
|
||||
/// <summary>The smallest capacity allocated.</summary>
|
||||
private const int MinCapacity = 20;
|
||||
|
||||
/// <summary>All available allocated storage space.</summary>
|
||||
private Memory<Node> capacity;
|
||||
|
||||
/// <summary>
|
||||
/// A view of a subset of <see cref="capacity" /> that forms the current active nodes of the
|
||||
/// tree.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The trie is encoded as a variable branching n-ary tree of <see cref="Node" />'s. The tree begins
|
||||
/// at the root (which has no symbol). Each non-root node encodes a single symbol. The path from the
|
||||
/// root to a node encodes a unique sequence of symbols. If the sequence represents a key within the
|
||||
/// trie then its terminal flag is set and it also stores a value. Interior nodes of the tree may be
|
||||
/// terminal (implying that two keys, one of which is a proper prefix of the other, both exist within
|
||||
/// the trie).
|
||||
/// <p>
|
||||
/// Children of a node form a linked list, the head of which is pointed to by the
|
||||
/// <see cref="Node.Child" /> and the siblings of which are linked together via
|
||||
/// <see cref="Node.Next" />.
|
||||
/// </p>
|
||||
/// <p>
|
||||
/// The 0'th element of this view is always the root node which will store the value for the empty
|
||||
/// sequence (if it has been added). Both the children linked list and the n-ary tree left nodes are
|
||||
/// terminated by the address 0, which can never be a valid child because the root is always in
|
||||
/// position 0.
|
||||
/// </p>
|
||||
/// </remarks>
|
||||
private Memory<Node> tree;
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="Trie{TSymbol, TValue}" /> class.</summary>
|
||||
/// <param name="initialCapacity">The (optional) initial capacity.</param>
|
||||
public Trie(int initialCapacity = 0)
|
||||
{
|
||||
Contract.Requires(initialCapacity >= 0);
|
||||
|
||||
// Allocate some initial capacity.
|
||||
this.capacity = new Node[initialCapacity < Trie<TSymbol, TValue>.MinCapacity ? Trie<TSymbol, TValue>.MinCapacity : initialCapacity];
|
||||
|
||||
// Insert the root. The root will store the value for the empty sequence.
|
||||
this.tree = this.capacity.Slice(0, 1);
|
||||
}
|
||||
|
||||
/// <summary>Add an item to the trie.</summary>
|
||||
/// <param name="key">The key to be added.</param>
|
||||
/// <param name="value">The value to be associated with the key.</param>
|
||||
/// <returns>True if the item was added, false if the key already exists in the trie.</returns>
|
||||
public bool TryAdd(ReadOnlySpan<TSymbol> key, TValue value)
|
||||
{
|
||||
int cur = 0;
|
||||
foreach (TSymbol s in key)
|
||||
{
|
||||
if (this.FindChild(s, cur, out int child))
|
||||
{
|
||||
cur = child;
|
||||
}
|
||||
else
|
||||
{
|
||||
int address = this.AllocNode(s);
|
||||
if (child == 0)
|
||||
{
|
||||
this.tree.Span[cur].Child = address;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.tree.Span[child].Next = address;
|
||||
}
|
||||
|
||||
cur = address;
|
||||
}
|
||||
}
|
||||
|
||||
if ((this.tree.Span[cur].Flags & Flags.Terminal) != 0)
|
||||
{
|
||||
// Already exists.
|
||||
return false;
|
||||
}
|
||||
|
||||
this.tree.Span[cur].Flags |= Flags.Terminal;
|
||||
this.tree.Span[cur].Value = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>Add an item to the trie. if the key exists, replace the existing item.</summary>
|
||||
/// <param name="key">The key to be added.</param>
|
||||
/// <param name="value">The value to be associated with the key.</param>
|
||||
public void AddOrUpdate(ReadOnlySpan<TSymbol> key, TValue value)
|
||||
{
|
||||
int cur = 0;
|
||||
foreach (TSymbol s in key)
|
||||
{
|
||||
if (this.FindChild(s, cur, out int child))
|
||||
{
|
||||
cur = child;
|
||||
}
|
||||
else
|
||||
{
|
||||
int address = this.AllocNode(s);
|
||||
if (child == 0)
|
||||
{
|
||||
this.tree.Span[cur].Child = address;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.tree.Span[child].Next = address;
|
||||
}
|
||||
|
||||
cur = address;
|
||||
}
|
||||
}
|
||||
|
||||
this.tree.Span[cur].Flags |= Flags.Terminal;
|
||||
this.tree.Span[cur].Value = value;
|
||||
}
|
||||
|
||||
/// <summary>Find an item within a trie.</summary>
|
||||
/// <param name="key">The key to be added.</param>
|
||||
/// <param name="value">
|
||||
/// If <paramref name="key" /> was found then the value to be associated with the
|
||||
/// key, otherwise default.
|
||||
/// </param>
|
||||
/// <returns>True if the item was found, false otherwise.</returns>
|
||||
public bool TryGetValue(ReadOnlySpan<TSymbol> key, out TValue value)
|
||||
{
|
||||
int cur = 0;
|
||||
foreach (TSymbol s in key)
|
||||
{
|
||||
if (!this.FindChild(s, cur, out cur))
|
||||
{
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ((this.tree.Span[cur].Flags & Flags.Terminal) == 0)
|
||||
{
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
value = this.tree.Span[cur].Value;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>Safe memory compare of <see cref="TSymbol" />.</summary>
|
||||
/// <param name="left">The left argument.</param>
|
||||
/// <param name="right">The right argument.</param>
|
||||
/// <returns>
|
||||
/// True if the byte sequence of <paramref name="left" /> exactly matches the byte sequence of
|
||||
/// <paramref name="right" />.
|
||||
/// </returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static unsafe bool SymbolEquals(TSymbol left, TSymbol right)
|
||||
{
|
||||
ReadOnlySpan<byte> p = new ReadOnlySpan<byte>(&left, sizeof(TSymbol));
|
||||
ReadOnlySpan<byte> q = new ReadOnlySpan<byte>(&right, sizeof(TSymbol));
|
||||
return p.SequenceEqual(q);
|
||||
}
|
||||
|
||||
/// <summary>Add a new unattached node to the tree.</summary>
|
||||
/// <param name="s">The symbol for the node.</param>
|
||||
/// <returns>The address of the new node.</returns>
|
||||
private int AllocNode(TSymbol s)
|
||||
{
|
||||
int node = this.tree.Length;
|
||||
if (this.tree.Length == this.capacity.Length)
|
||||
{
|
||||
this.capacity = new Node[this.capacity.Length * 2];
|
||||
this.tree.CopyTo(this.capacity);
|
||||
}
|
||||
|
||||
this.tree = this.capacity.Slice(0, this.tree.Length + 1);
|
||||
|
||||
this.tree.Span[node] = new Node(Flags.None, s, 0, 0, default);
|
||||
return node;
|
||||
}
|
||||
|
||||
/// <summary>Find a child node of <paramref name="parent" /> with a matching symbol.</summary>
|
||||
/// <param name="s">The symbol to match.</param>
|
||||
/// <param name="parent">The address of the parent.</param>
|
||||
/// <param name="child">
|
||||
/// If successful, the address of the child, otherwise the address of the previous
|
||||
/// in the child link list where a new child should be added. 0 if the parent has no children.
|
||||
/// </param>
|
||||
/// <returns>True if a child with matching symbol was found, false otherwise.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private bool FindChild(TSymbol s, int parent, out int child)
|
||||
{
|
||||
int last = 0;
|
||||
child = this.tree.Span[parent].Child;
|
||||
while (child != 0)
|
||||
{
|
||||
if (typeof(TSymbol) == typeof(byte))
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
TSymbol t = this.tree.Span[child].Symbol;
|
||||
if (*(byte*)&t == *(byte*)&s)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (Trie<TSymbol, TValue>.SymbolEquals(this.tree.Span[child].Symbol, s))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
last = child;
|
||||
child = this.tree.Span[child].Next;
|
||||
}
|
||||
|
||||
child = last;
|
||||
return false;
|
||||
}
|
||||
|
||||
private struct Node
|
||||
{
|
||||
/// <summary>Flags indicating characteristics of the node.</summary>
|
||||
public Flags Flags;
|
||||
|
||||
/// <summary>The symbol forming the key prefix added by this node.</summary>
|
||||
public readonly TSymbol Symbol;
|
||||
|
||||
/// <summary>A pointer to the index of the first child node of this node.</summary>
|
||||
public int Child;
|
||||
|
||||
/// <summary>
|
||||
/// A pointer to the index of the next sibling of this node (next child of this node's
|
||||
/// parent).
|
||||
/// </summary>
|
||||
public int Next;
|
||||
|
||||
/// <summary>
|
||||
/// If the current node is a terminal, the value associated with current prefix, otherwise
|
||||
/// undefined.
|
||||
/// </summary>
|
||||
public TValue Value;
|
||||
|
||||
public Node(Flags flags, TSymbol symbol, int child, int next, TValue value)
|
||||
{
|
||||
this.Flags = flags;
|
||||
this.Symbol = symbol;
|
||||
this.Child = child;
|
||||
this.Next = next;
|
||||
this.Value = value;
|
||||
}
|
||||
}
|
||||
|
||||
[Flags]
|
||||
private enum Flags
|
||||
{
|
||||
/// <summary>Interior node within the trie that carries no value.</summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>Either interior or left node within the trie that has a value.</summary>
|
||||
Terminal = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
94
src/Core/Core/Contract.cs
Normal file
94
src/Core/Core/Contract.cs
Normal file
@@ -0,0 +1,94 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Azure.Cosmos.Core
|
||||
{
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
/// <summary>
|
||||
/// Used to check contract invariants in either Debug-only (Assert)
|
||||
/// or Debug and Release (Requires, Invariant, etc.). Contract failures
|
||||
/// will attempt to break into the debugger, will trace the error, and
|
||||
/// will throw a ContractViolationException.
|
||||
/// </summary>
|
||||
[ExcludeFromCodeCoverage]
|
||||
public static class Contract
|
||||
{
|
||||
[Conditional("DEBUG")]
|
||||
public static void Assert(bool condition)
|
||||
{
|
||||
if (!condition)
|
||||
{
|
||||
Contract.Fail("Assert", string.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
public static void Assert(bool condition, string message)
|
||||
{
|
||||
if (!condition)
|
||||
{
|
||||
Contract.Fail("Assert", message);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Requires(bool condition)
|
||||
{
|
||||
if (!condition)
|
||||
{
|
||||
Contract.Fail("Requires", string.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Requires(bool condition, string message)
|
||||
{
|
||||
if (!condition)
|
||||
{
|
||||
Contract.Fail("Requires", message);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Invariant(bool condition)
|
||||
{
|
||||
if (!condition)
|
||||
{
|
||||
Contract.Fail("Invariant", string.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Invariant(bool condition, string message)
|
||||
{
|
||||
if (!condition)
|
||||
{
|
||||
Contract.Fail("Invariant", message);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Fail()
|
||||
{
|
||||
Contract.Fail("Fail", string.Empty);
|
||||
}
|
||||
|
||||
public static void Fail(string message)
|
||||
{
|
||||
Contract.Fail("Fail", message);
|
||||
}
|
||||
|
||||
private static void Fail(string api, string message)
|
||||
{
|
||||
StackTrace stack = new StackTrace(2, true);
|
||||
string error = $"{api} Failure: {message}\n\tStack: {stack}";
|
||||
Trace.TraceError(error);
|
||||
Trace.Flush();
|
||||
|
||||
// Try breaking into the debugger if attached.
|
||||
Debugger.Break();
|
||||
|
||||
// This exception should NEVER be caught.
|
||||
// TODO: make this uncatchable.
|
||||
throw new ContractViolationException(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
34
src/Core/Core/ContractViolationException.cs
Normal file
34
src/Core/Core/ContractViolationException.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Azure.Cosmos.Core
|
||||
{
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
[Serializable]
|
||||
[ExcludeFromCodeCoverage]
|
||||
public class ContractViolationException : Exception
|
||||
{
|
||||
public ContractViolationException()
|
||||
{
|
||||
}
|
||||
|
||||
public ContractViolationException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public ContractViolationException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
}
|
||||
|
||||
protected ContractViolationException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
526
src/Core/Core/Crc32.cs
Normal file
526
src/Core/Core/Crc32.cs
Normal file
@@ -0,0 +1,526 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Azure.Cosmos.Core
|
||||
{
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
//
|
||||
// File implements Slicing-by-8 CRC Generation, as described in
|
||||
// "Novel Table Lookup-Based Algorithms for High-Performance CRC Generation"
|
||||
// IEEE TRANSACTIONS ON COMPUTERS, VOL. 57, NO. 11, NOVEMBER 2008
|
||||
//
|
||||
// Copyright(c) 2004-2006 Intel Corporation - All Rights Reserved
|
||||
//
|
||||
// This software program is licensed subject to the BSD License,
|
||||
// available at http://www.opensource.org/licenses/bsd-license.html.
|
||||
|
||||
/// <summary>
|
||||
/// CRC Generator.
|
||||
/// </summary>
|
||||
public static class Crc32
|
||||
{
|
||||
// Generated tables for managed crc calculation.
|
||||
// Each table n (starting at 0) contains remainders from the long division of
|
||||
// all possible byte values, shifted by an offset of (n * 4 bits).
|
||||
// The divisor used is the crc32 standard polynomial 0xEDB88320
|
||||
// Please see cited paper for more details.
|
||||
private static readonly uint[] CrcTable0 = new uint[256]
|
||||
{
|
||||
0x00000000u, 0x77073096u, 0xee0e612cu, 0x990951bau, 0x076dc419u,
|
||||
0x706af48fu, 0xe963a535u, 0x9e6495a3u, 0x0edb8832u, 0x79dcb8a4u,
|
||||
0xe0d5e91eu, 0x97d2d988u, 0x09b64c2bu, 0x7eb17cbdu, 0xe7b82d07u,
|
||||
0x90bf1d91u, 0x1db71064u, 0x6ab020f2u, 0xf3b97148u, 0x84be41deu,
|
||||
0x1adad47du, 0x6ddde4ebu, 0xf4d4b551u, 0x83d385c7u, 0x136c9856u,
|
||||
0x646ba8c0u, 0xfd62f97au, 0x8a65c9ecu, 0x14015c4fu, 0x63066cd9u,
|
||||
0xfa0f3d63u, 0x8d080df5u, 0x3b6e20c8u, 0x4c69105eu, 0xd56041e4u,
|
||||
0xa2677172u, 0x3c03e4d1u, 0x4b04d447u, 0xd20d85fdu, 0xa50ab56bu,
|
||||
0x35b5a8fau, 0x42b2986cu, 0xdbbbc9d6u, 0xacbcf940u, 0x32d86ce3u,
|
||||
0x45df5c75u, 0xdcd60dcfu, 0xabd13d59u, 0x26d930acu, 0x51de003au,
|
||||
0xc8d75180u, 0xbfd06116u, 0x21b4f4b5u, 0x56b3c423u, 0xcfba9599u,
|
||||
0xb8bda50fu, 0x2802b89eu, 0x5f058808u, 0xc60cd9b2u, 0xb10be924u,
|
||||
0x2f6f7c87u, 0x58684c11u, 0xc1611dabu, 0xb6662d3du, 0x76dc4190u,
|
||||
0x01db7106u, 0x98d220bcu, 0xefd5102au, 0x71b18589u, 0x06b6b51fu,
|
||||
0x9fbfe4a5u, 0xe8b8d433u, 0x7807c9a2u, 0x0f00f934u, 0x9609a88eu,
|
||||
0xe10e9818u, 0x7f6a0dbbu, 0x086d3d2du, 0x91646c97u, 0xe6635c01u,
|
||||
0x6b6b51f4u, 0x1c6c6162u, 0x856530d8u, 0xf262004eu, 0x6c0695edu,
|
||||
0x1b01a57bu, 0x8208f4c1u, 0xf50fc457u, 0x65b0d9c6u, 0x12b7e950u,
|
||||
0x8bbeb8eau, 0xfcb9887cu, 0x62dd1ddfu, 0x15da2d49u, 0x8cd37cf3u,
|
||||
0xfbd44c65u, 0x4db26158u, 0x3ab551ceu, 0xa3bc0074u, 0xd4bb30e2u,
|
||||
0x4adfa541u, 0x3dd895d7u, 0xa4d1c46du, 0xd3d6f4fbu, 0x4369e96au,
|
||||
0x346ed9fcu, 0xad678846u, 0xda60b8d0u, 0x44042d73u, 0x33031de5u,
|
||||
0xaa0a4c5fu, 0xdd0d7cc9u, 0x5005713cu, 0x270241aau, 0xbe0b1010u,
|
||||
0xc90c2086u, 0x5768b525u, 0x206f85b3u, 0xb966d409u, 0xce61e49fu,
|
||||
0x5edef90eu, 0x29d9c998u, 0xb0d09822u, 0xc7d7a8b4u, 0x59b33d17u,
|
||||
0x2eb40d81u, 0xb7bd5c3bu, 0xc0ba6cadu, 0xedb88320u, 0x9abfb3b6u,
|
||||
0x03b6e20cu, 0x74b1d29au, 0xead54739u, 0x9dd277afu, 0x04db2615u,
|
||||
0x73dc1683u, 0xe3630b12u, 0x94643b84u, 0x0d6d6a3eu, 0x7a6a5aa8u,
|
||||
0xe40ecf0bu, 0x9309ff9du, 0x0a00ae27u, 0x7d079eb1u, 0xf00f9344u,
|
||||
0x8708a3d2u, 0x1e01f268u, 0x6906c2feu, 0xf762575du, 0x806567cbu,
|
||||
0x196c3671u, 0x6e6b06e7u, 0xfed41b76u, 0x89d32be0u, 0x10da7a5au,
|
||||
0x67dd4accu, 0xf9b9df6fu, 0x8ebeeff9u, 0x17b7be43u, 0x60b08ed5u,
|
||||
0xd6d6a3e8u, 0xa1d1937eu, 0x38d8c2c4u, 0x4fdff252u, 0xd1bb67f1u,
|
||||
0xa6bc5767u, 0x3fb506ddu, 0x48b2364bu, 0xd80d2bdau, 0xaf0a1b4cu,
|
||||
0x36034af6u, 0x41047a60u, 0xdf60efc3u, 0xa867df55u, 0x316e8eefu,
|
||||
0x4669be79u, 0xcb61b38cu, 0xbc66831au, 0x256fd2a0u, 0x5268e236u,
|
||||
0xcc0c7795u, 0xbb0b4703u, 0x220216b9u, 0x5505262fu, 0xc5ba3bbeu,
|
||||
0xb2bd0b28u, 0x2bb45a92u, 0x5cb36a04u, 0xc2d7ffa7u, 0xb5d0cf31u,
|
||||
0x2cd99e8bu, 0x5bdeae1du, 0x9b64c2b0u, 0xec63f226u, 0x756aa39cu,
|
||||
0x026d930au, 0x9c0906a9u, 0xeb0e363fu, 0x72076785u, 0x05005713u,
|
||||
0x95bf4a82u, 0xe2b87a14u, 0x7bb12baeu, 0x0cb61b38u, 0x92d28e9bu,
|
||||
0xe5d5be0du, 0x7cdcefb7u, 0x0bdbdf21u, 0x86d3d2d4u, 0xf1d4e242u,
|
||||
0x68ddb3f8u, 0x1fda836eu, 0x81be16cdu, 0xf6b9265bu, 0x6fb077e1u,
|
||||
0x18b74777u, 0x88085ae6u, 0xff0f6a70u, 0x66063bcau, 0x11010b5cu,
|
||||
0x8f659effu, 0xf862ae69u, 0x616bffd3u, 0x166ccf45u, 0xa00ae278u,
|
||||
0xd70dd2eeu, 0x4e048354u, 0x3903b3c2u, 0xa7672661u, 0xd06016f7u,
|
||||
0x4969474du, 0x3e6e77dbu, 0xaed16a4au, 0xd9d65adcu, 0x40df0b66u,
|
||||
0x37d83bf0u, 0xa9bcae53u, 0xdebb9ec5u, 0x47b2cf7fu, 0x30b5ffe9u,
|
||||
0xbdbdf21cu, 0xcabac28au, 0x53b39330u, 0x24b4a3a6u, 0xbad03605u,
|
||||
0xcdd70693u, 0x54de5729u, 0x23d967bfu, 0xb3667a2eu, 0xc4614ab8u,
|
||||
0x5d681b02u, 0x2a6f2b94u, 0xb40bbe37u, 0xc30c8ea1u, 0x5a05df1bu,
|
||||
0x2d02ef8du,
|
||||
};
|
||||
|
||||
private static readonly uint[] CrcTable1 = new uint[256]
|
||||
{
|
||||
0x00000000u, 0x191B3141u, 0x32366282u, 0x2B2D53C3u, 0x646CC504u,
|
||||
0x7D77F445u, 0x565AA786u, 0x4F4196C7u, 0xC8D98A08u, 0xD1C2BB49u,
|
||||
0xFAEFE88Au, 0xE3F4D9CBu, 0xACB54F0Cu, 0xB5AE7E4Du, 0x9E832D8Eu,
|
||||
0x87981CCFu, 0x4AC21251u, 0x53D92310u, 0x78F470D3u, 0x61EF4192u,
|
||||
0x2EAED755u, 0x37B5E614u, 0x1C98B5D7u, 0x05838496u, 0x821B9859u,
|
||||
0x9B00A918u, 0xB02DFADBu, 0xA936CB9Au, 0xE6775D5Du, 0xFF6C6C1Cu,
|
||||
0xD4413FDFu, 0xCD5A0E9Eu, 0x958424A2u, 0x8C9F15E3u, 0xA7B24620u,
|
||||
0xBEA97761u, 0xF1E8E1A6u, 0xE8F3D0E7u, 0xC3DE8324u, 0xDAC5B265u,
|
||||
0x5D5DAEAAu, 0x44469FEBu, 0x6F6BCC28u, 0x7670FD69u, 0x39316BAEu,
|
||||
0x202A5AEFu, 0x0B07092Cu, 0x121C386Du, 0xDF4636F3u, 0xC65D07B2u,
|
||||
0xED705471u, 0xF46B6530u, 0xBB2AF3F7u, 0xA231C2B6u, 0x891C9175u,
|
||||
0x9007A034u, 0x179FBCFBu, 0x0E848DBAu, 0x25A9DE79u, 0x3CB2EF38u,
|
||||
0x73F379FFu, 0x6AE848BEu, 0x41C51B7Du, 0x58DE2A3Cu, 0xF0794F05u,
|
||||
0xE9627E44u, 0xC24F2D87u, 0xDB541CC6u, 0x94158A01u, 0x8D0EBB40u,
|
||||
0xA623E883u, 0xBF38D9C2u, 0x38A0C50Du, 0x21BBF44Cu, 0x0A96A78Fu,
|
||||
0x138D96CEu, 0x5CCC0009u, 0x45D73148u, 0x6EFA628Bu, 0x77E153CAu,
|
||||
0xBABB5D54u, 0xA3A06C15u, 0x888D3FD6u, 0x91960E97u, 0xDED79850u,
|
||||
0xC7CCA911u, 0xECE1FAD2u, 0xF5FACB93u, 0x7262D75Cu, 0x6B79E61Du,
|
||||
0x4054B5DEu, 0x594F849Fu, 0x160E1258u, 0x0F152319u, 0x243870DAu,
|
||||
0x3D23419Bu, 0x65FD6BA7u, 0x7CE65AE6u, 0x57CB0925u, 0x4ED03864u,
|
||||
0x0191AEA3u, 0x188A9FE2u, 0x33A7CC21u, 0x2ABCFD60u, 0xAD24E1AFu,
|
||||
0xB43FD0EEu, 0x9F12832Du, 0x8609B26Cu, 0xC94824ABu, 0xD05315EAu,
|
||||
0xFB7E4629u, 0xE2657768u, 0x2F3F79F6u, 0x362448B7u, 0x1D091B74u,
|
||||
0x04122A35u, 0x4B53BCF2u, 0x52488DB3u, 0x7965DE70u, 0x607EEF31u,
|
||||
0xE7E6F3FEu, 0xFEFDC2BFu, 0xD5D0917Cu, 0xCCCBA03Du, 0x838A36FAu,
|
||||
0x9A9107BBu, 0xB1BC5478u, 0xA8A76539u, 0x3B83984Bu, 0x2298A90Au,
|
||||
0x09B5FAC9u, 0x10AECB88u, 0x5FEF5D4Fu, 0x46F46C0Eu, 0x6DD93FCDu,
|
||||
0x74C20E8Cu, 0xF35A1243u, 0xEA412302u, 0xC16C70C1u, 0xD8774180u,
|
||||
0x9736D747u, 0x8E2DE606u, 0xA500B5C5u, 0xBC1B8484u, 0x71418A1Au,
|
||||
0x685ABB5Bu, 0x4377E898u, 0x5A6CD9D9u, 0x152D4F1Eu, 0x0C367E5Fu,
|
||||
0x271B2D9Cu, 0x3E001CDDu, 0xB9980012u, 0xA0833153u, 0x8BAE6290u,
|
||||
0x92B553D1u, 0xDDF4C516u, 0xC4EFF457u, 0xEFC2A794u, 0xF6D996D5u,
|
||||
0xAE07BCE9u, 0xB71C8DA8u, 0x9C31DE6Bu, 0x852AEF2Au, 0xCA6B79EDu,
|
||||
0xD37048ACu, 0xF85D1B6Fu, 0xE1462A2Eu, 0x66DE36E1u, 0x7FC507A0u,
|
||||
0x54E85463u, 0x4DF36522u, 0x02B2F3E5u, 0x1BA9C2A4u, 0x30849167u,
|
||||
0x299FA026u, 0xE4C5AEB8u, 0xFDDE9FF9u, 0xD6F3CC3Au, 0xCFE8FD7Bu,
|
||||
0x80A96BBCu, 0x99B25AFDu, 0xB29F093Eu, 0xAB84387Fu, 0x2C1C24B0u,
|
||||
0x350715F1u, 0x1E2A4632u, 0x07317773u, 0x4870E1B4u, 0x516BD0F5u,
|
||||
0x7A468336u, 0x635DB277u, 0xCBFAD74Eu, 0xD2E1E60Fu, 0xF9CCB5CCu,
|
||||
0xE0D7848Du, 0xAF96124Au, 0xB68D230Bu, 0x9DA070C8u, 0x84BB4189u,
|
||||
0x03235D46u, 0x1A386C07u, 0x31153FC4u, 0x280E0E85u, 0x674F9842u,
|
||||
0x7E54A903u, 0x5579FAC0u, 0x4C62CB81u, 0x8138C51Fu, 0x9823F45Eu,
|
||||
0xB30EA79Du, 0xAA1596DCu, 0xE554001Bu, 0xFC4F315Au, 0xD7626299u,
|
||||
0xCE7953D8u, 0x49E14F17u, 0x50FA7E56u, 0x7BD72D95u, 0x62CC1CD4u,
|
||||
0x2D8D8A13u, 0x3496BB52u, 0x1FBBE891u, 0x06A0D9D0u, 0x5E7EF3ECu,
|
||||
0x4765C2ADu, 0x6C48916Eu, 0x7553A02Fu, 0x3A1236E8u, 0x230907A9u,
|
||||
0x0824546Au, 0x113F652Bu, 0x96A779E4u, 0x8FBC48A5u, 0xA4911B66u,
|
||||
0xBD8A2A27u, 0xF2CBBCE0u, 0xEBD08DA1u, 0xC0FDDE62u, 0xD9E6EF23u,
|
||||
0x14BCE1BDu, 0x0DA7D0FCu, 0x268A833Fu, 0x3F91B27Eu, 0x70D024B9u,
|
||||
0x69CB15F8u, 0x42E6463Bu, 0x5BFD777Au, 0xDC656BB5u, 0xC57E5AF4u,
|
||||
0xEE530937u, 0xF7483876u, 0xB809AEB1u, 0xA1129FF0u, 0x8A3FCC33u,
|
||||
0x9324FD72u,
|
||||
};
|
||||
|
||||
private static readonly uint[] CrcTable2 = new uint[256]
|
||||
{
|
||||
0x00000000u, 0x01C26A37u, 0x0384D46Eu, 0x0246BE59u, 0x0709A8DCu,
|
||||
0x06CBC2EBu, 0x048D7CB2u, 0x054F1685u, 0x0E1351B8u, 0x0FD13B8Fu,
|
||||
0x0D9785D6u, 0x0C55EFE1u, 0x091AF964u, 0x08D89353u, 0x0A9E2D0Au,
|
||||
0x0B5C473Du, 0x1C26A370u, 0x1DE4C947u, 0x1FA2771Eu, 0x1E601D29u,
|
||||
0x1B2F0BACu, 0x1AED619Bu, 0x18ABDFC2u, 0x1969B5F5u, 0x1235F2C8u,
|
||||
0x13F798FFu, 0x11B126A6u, 0x10734C91u, 0x153C5A14u, 0x14FE3023u,
|
||||
0x16B88E7Au, 0x177AE44Du, 0x384D46E0u, 0x398F2CD7u, 0x3BC9928Eu,
|
||||
0x3A0BF8B9u, 0x3F44EE3Cu, 0x3E86840Bu, 0x3CC03A52u, 0x3D025065u,
|
||||
0x365E1758u, 0x379C7D6Fu, 0x35DAC336u, 0x3418A901u, 0x3157BF84u,
|
||||
0x3095D5B3u, 0x32D36BEAu, 0x331101DDu, 0x246BE590u, 0x25A98FA7u,
|
||||
0x27EF31FEu, 0x262D5BC9u, 0x23624D4Cu, 0x22A0277Bu, 0x20E69922u,
|
||||
0x2124F315u, 0x2A78B428u, 0x2BBADE1Fu, 0x29FC6046u, 0x283E0A71u,
|
||||
0x2D711CF4u, 0x2CB376C3u, 0x2EF5C89Au, 0x2F37A2ADu, 0x709A8DC0u,
|
||||
0x7158E7F7u, 0x731E59AEu, 0x72DC3399u, 0x7793251Cu, 0x76514F2Bu,
|
||||
0x7417F172u, 0x75D59B45u, 0x7E89DC78u, 0x7F4BB64Fu, 0x7D0D0816u,
|
||||
0x7CCF6221u, 0x798074A4u, 0x78421E93u, 0x7A04A0CAu, 0x7BC6CAFDu,
|
||||
0x6CBC2EB0u, 0x6D7E4487u, 0x6F38FADEu, 0x6EFA90E9u, 0x6BB5866Cu,
|
||||
0x6A77EC5Bu, 0x68315202u, 0x69F33835u, 0x62AF7F08u, 0x636D153Fu,
|
||||
0x612BAB66u, 0x60E9C151u, 0x65A6D7D4u, 0x6464BDE3u, 0x662203BAu,
|
||||
0x67E0698Du, 0x48D7CB20u, 0x4915A117u, 0x4B531F4Eu, 0x4A917579u,
|
||||
0x4FDE63FCu, 0x4E1C09CBu, 0x4C5AB792u, 0x4D98DDA5u, 0x46C49A98u,
|
||||
0x4706F0AFu, 0x45404EF6u, 0x448224C1u, 0x41CD3244u, 0x400F5873u,
|
||||
0x4249E62Au, 0x438B8C1Du, 0x54F16850u, 0x55330267u, 0x5775BC3Eu,
|
||||
0x56B7D609u, 0x53F8C08Cu, 0x523AAABBu, 0x507C14E2u, 0x51BE7ED5u,
|
||||
0x5AE239E8u, 0x5B2053DFu, 0x5966ED86u, 0x58A487B1u, 0x5DEB9134u,
|
||||
0x5C29FB03u, 0x5E6F455Au, 0x5FAD2F6Du, 0xE1351B80u, 0xE0F771B7u,
|
||||
0xE2B1CFEEu, 0xE373A5D9u, 0xE63CB35Cu, 0xE7FED96Bu, 0xE5B86732u,
|
||||
0xE47A0D05u, 0xEF264A38u, 0xEEE4200Fu, 0xECA29E56u, 0xED60F461u,
|
||||
0xE82FE2E4u, 0xE9ED88D3u, 0xEBAB368Au, 0xEA695CBDu, 0xFD13B8F0u,
|
||||
0xFCD1D2C7u, 0xFE976C9Eu, 0xFF5506A9u, 0xFA1A102Cu, 0xFBD87A1Bu,
|
||||
0xF99EC442u, 0xF85CAE75u, 0xF300E948u, 0xF2C2837Fu, 0xF0843D26u,
|
||||
0xF1465711u, 0xF4094194u, 0xF5CB2BA3u, 0xF78D95FAu, 0xF64FFFCDu,
|
||||
0xD9785D60u, 0xD8BA3757u, 0xDAFC890Eu, 0xDB3EE339u, 0xDE71F5BCu,
|
||||
0xDFB39F8Bu, 0xDDF521D2u, 0xDC374BE5u, 0xD76B0CD8u, 0xD6A966EFu,
|
||||
0xD4EFD8B6u, 0xD52DB281u, 0xD062A404u, 0xD1A0CE33u, 0xD3E6706Au,
|
||||
0xD2241A5Du, 0xC55EFE10u, 0xC49C9427u, 0xC6DA2A7Eu, 0xC7184049u,
|
||||
0xC25756CCu, 0xC3953CFBu, 0xC1D382A2u, 0xC011E895u, 0xCB4DAFA8u,
|
||||
0xCA8FC59Fu, 0xC8C97BC6u, 0xC90B11F1u, 0xCC440774u, 0xCD866D43u,
|
||||
0xCFC0D31Au, 0xCE02B92Du, 0x91AF9640u, 0x906DFC77u, 0x922B422Eu,
|
||||
0x93E92819u, 0x96A63E9Cu, 0x976454ABu, 0x9522EAF2u, 0x94E080C5u,
|
||||
0x9FBCC7F8u, 0x9E7EADCFu, 0x9C381396u, 0x9DFA79A1u, 0x98B56F24u,
|
||||
0x99770513u, 0x9B31BB4Au, 0x9AF3D17Du, 0x8D893530u, 0x8C4B5F07u,
|
||||
0x8E0DE15Eu, 0x8FCF8B69u, 0x8A809DECu, 0x8B42F7DBu, 0x89044982u,
|
||||
0x88C623B5u, 0x839A6488u, 0x82580EBFu, 0x801EB0E6u, 0x81DCDAD1u,
|
||||
0x8493CC54u, 0x8551A663u, 0x8717183Au, 0x86D5720Du, 0xA9E2D0A0u,
|
||||
0xA820BA97u, 0xAA6604CEu, 0xABA46EF9u, 0xAEEB787Cu, 0xAF29124Bu,
|
||||
0xAD6FAC12u, 0xACADC625u, 0xA7F18118u, 0xA633EB2Fu, 0xA4755576u,
|
||||
0xA5B73F41u, 0xA0F829C4u, 0xA13A43F3u, 0xA37CFDAAu, 0xA2BE979Du,
|
||||
0xB5C473D0u, 0xB40619E7u, 0xB640A7BEu, 0xB782CD89u, 0xB2CDDB0Cu,
|
||||
0xB30FB13Bu, 0xB1490F62u, 0xB08B6555u, 0xBBD72268u, 0xBA15485Fu,
|
||||
0xB853F606u, 0xB9919C31u, 0xBCDE8AB4u, 0xBD1CE083u, 0xBF5A5EDAu,
|
||||
0xBE9834EDu,
|
||||
};
|
||||
|
||||
private static readonly uint[] CrcTable3 = new uint[256]
|
||||
{
|
||||
0x00000000u, 0xB8BC6765u, 0xAA09C88Bu, 0x12B5AFEEu, 0x8F629757u,
|
||||
0x37DEF032u, 0x256B5FDCu, 0x9DD738B9u, 0xC5B428EFu, 0x7D084F8Au,
|
||||
0x6FBDE064u, 0xD7018701u, 0x4AD6BFB8u, 0xF26AD8DDu, 0xE0DF7733u,
|
||||
0x58631056u, 0x5019579Fu, 0xE8A530FAu, 0xFA109F14u, 0x42ACF871u,
|
||||
0xDF7BC0C8u, 0x67C7A7ADu, 0x75720843u, 0xCDCE6F26u, 0x95AD7F70u,
|
||||
0x2D111815u, 0x3FA4B7FBu, 0x8718D09Eu, 0x1ACFE827u, 0xA2738F42u,
|
||||
0xB0C620ACu, 0x087A47C9u, 0xA032AF3Eu, 0x188EC85Bu, 0x0A3B67B5u,
|
||||
0xB28700D0u, 0x2F503869u, 0x97EC5F0Cu, 0x8559F0E2u, 0x3DE59787u,
|
||||
0x658687D1u, 0xDD3AE0B4u, 0xCF8F4F5Au, 0x7733283Fu, 0xEAE41086u,
|
||||
0x525877E3u, 0x40EDD80Du, 0xF851BF68u, 0xF02BF8A1u, 0x48979FC4u,
|
||||
0x5A22302Au, 0xE29E574Fu, 0x7F496FF6u, 0xC7F50893u, 0xD540A77Du,
|
||||
0x6DFCC018u, 0x359FD04Eu, 0x8D23B72Bu, 0x9F9618C5u, 0x272A7FA0u,
|
||||
0xBAFD4719u, 0x0241207Cu, 0x10F48F92u, 0xA848E8F7u, 0x9B14583Du,
|
||||
0x23A83F58u, 0x311D90B6u, 0x89A1F7D3u, 0x1476CF6Au, 0xACCAA80Fu,
|
||||
0xBE7F07E1u, 0x06C36084u, 0x5EA070D2u, 0xE61C17B7u, 0xF4A9B859u,
|
||||
0x4C15DF3Cu, 0xD1C2E785u, 0x697E80E0u, 0x7BCB2F0Eu, 0xC377486Bu,
|
||||
0xCB0D0FA2u, 0x73B168C7u, 0x6104C729u, 0xD9B8A04Cu, 0x446F98F5u,
|
||||
0xFCD3FF90u, 0xEE66507Eu, 0x56DA371Bu, 0x0EB9274Du, 0xB6054028u,
|
||||
0xA4B0EFC6u, 0x1C0C88A3u, 0x81DBB01Au, 0x3967D77Fu, 0x2BD27891u,
|
||||
0x936E1FF4u, 0x3B26F703u, 0x839A9066u, 0x912F3F88u, 0x299358EDu,
|
||||
0xB4446054u, 0x0CF80731u, 0x1E4DA8DFu, 0xA6F1CFBAu, 0xFE92DFECu,
|
||||
0x462EB889u, 0x549B1767u, 0xEC277002u, 0x71F048BBu, 0xC94C2FDEu,
|
||||
0xDBF98030u, 0x6345E755u, 0x6B3FA09Cu, 0xD383C7F9u, 0xC1366817u,
|
||||
0x798A0F72u, 0xE45D37CBu, 0x5CE150AEu, 0x4E54FF40u, 0xF6E89825u,
|
||||
0xAE8B8873u, 0x1637EF16u, 0x048240F8u, 0xBC3E279Du, 0x21E91F24u,
|
||||
0x99557841u, 0x8BE0D7AFu, 0x335CB0CAu, 0xED59B63Bu, 0x55E5D15Eu,
|
||||
0x47507EB0u, 0xFFEC19D5u, 0x623B216Cu, 0xDA874609u, 0xC832E9E7u,
|
||||
0x708E8E82u, 0x28ED9ED4u, 0x9051F9B1u, 0x82E4565Fu, 0x3A58313Au,
|
||||
0xA78F0983u, 0x1F336EE6u, 0x0D86C108u, 0xB53AA66Du, 0xBD40E1A4u,
|
||||
0x05FC86C1u, 0x1749292Fu, 0xAFF54E4Au, 0x322276F3u, 0x8A9E1196u,
|
||||
0x982BBE78u, 0x2097D91Du, 0x78F4C94Bu, 0xC048AE2Eu, 0xD2FD01C0u,
|
||||
0x6A4166A5u, 0xF7965E1Cu, 0x4F2A3979u, 0x5D9F9697u, 0xE523F1F2u,
|
||||
0x4D6B1905u, 0xF5D77E60u, 0xE762D18Eu, 0x5FDEB6EBu, 0xC2098E52u,
|
||||
0x7AB5E937u, 0x680046D9u, 0xD0BC21BCu, 0x88DF31EAu, 0x3063568Fu,
|
||||
0x22D6F961u, 0x9A6A9E04u, 0x07BDA6BDu, 0xBF01C1D8u, 0xADB46E36u,
|
||||
0x15080953u, 0x1D724E9Au, 0xA5CE29FFu, 0xB77B8611u, 0x0FC7E174u,
|
||||
0x9210D9CDu, 0x2AACBEA8u, 0x38191146u, 0x80A57623u, 0xD8C66675u,
|
||||
0x607A0110u, 0x72CFAEFEu, 0xCA73C99Bu, 0x57A4F122u, 0xEF189647u,
|
||||
0xFDAD39A9u, 0x45115ECCu, 0x764DEE06u, 0xCEF18963u, 0xDC44268Du,
|
||||
0x64F841E8u, 0xF92F7951u, 0x41931E34u, 0x5326B1DAu, 0xEB9AD6BFu,
|
||||
0xB3F9C6E9u, 0x0B45A18Cu, 0x19F00E62u, 0xA14C6907u, 0x3C9B51BEu,
|
||||
0x842736DBu, 0x96929935u, 0x2E2EFE50u, 0x2654B999u, 0x9EE8DEFCu,
|
||||
0x8C5D7112u, 0x34E11677u, 0xA9362ECEu, 0x118A49ABu, 0x033FE645u,
|
||||
0xBB838120u, 0xE3E09176u, 0x5B5CF613u, 0x49E959FDu, 0xF1553E98u,
|
||||
0x6C820621u, 0xD43E6144u, 0xC68BCEAAu, 0x7E37A9CFu, 0xD67F4138u,
|
||||
0x6EC3265Du, 0x7C7689B3u, 0xC4CAEED6u, 0x591DD66Fu, 0xE1A1B10Au,
|
||||
0xF3141EE4u, 0x4BA87981u, 0x13CB69D7u, 0xAB770EB2u, 0xB9C2A15Cu,
|
||||
0x017EC639u, 0x9CA9FE80u, 0x241599E5u, 0x36A0360Bu, 0x8E1C516Eu,
|
||||
0x866616A7u, 0x3EDA71C2u, 0x2C6FDE2Cu, 0x94D3B949u, 0x090481F0u,
|
||||
0xB1B8E695u, 0xA30D497Bu, 0x1BB12E1Eu, 0x43D23E48u, 0xFB6E592Du,
|
||||
0xE9DBF6C3u, 0x516791A6u, 0xCCB0A91Fu, 0x740CCE7Au, 0x66B96194u,
|
||||
0xDE0506F1u,
|
||||
};
|
||||
|
||||
private static readonly uint[] CrcTable4 = new uint[256]
|
||||
{
|
||||
0x00000000u, 0x3D6029B0u, 0x7AC05360u, 0x47A07AD0u, 0xF580A6C0u,
|
||||
0xC8E08F70u, 0x8F40F5A0u, 0xB220DC10u, 0x30704BC1u, 0x0D106271u,
|
||||
0x4AB018A1u, 0x77D03111u, 0xC5F0ED01u, 0xF890C4B1u, 0xBF30BE61u,
|
||||
0x825097D1u, 0x60E09782u, 0x5D80BE32u, 0x1A20C4E2u, 0x2740ED52u,
|
||||
0x95603142u, 0xA80018F2u, 0xEFA06222u, 0xD2C04B92u, 0x5090DC43u,
|
||||
0x6DF0F5F3u, 0x2A508F23u, 0x1730A693u, 0xA5107A83u, 0x98705333u,
|
||||
0xDFD029E3u, 0xE2B00053u, 0xC1C12F04u, 0xFCA106B4u, 0xBB017C64u,
|
||||
0x866155D4u, 0x344189C4u, 0x0921A074u, 0x4E81DAA4u, 0x73E1F314u,
|
||||
0xF1B164C5u, 0xCCD14D75u, 0x8B7137A5u, 0xB6111E15u, 0x0431C205u,
|
||||
0x3951EBB5u, 0x7EF19165u, 0x4391B8D5u, 0xA121B886u, 0x9C419136u,
|
||||
0xDBE1EBE6u, 0xE681C256u, 0x54A11E46u, 0x69C137F6u, 0x2E614D26u,
|
||||
0x13016496u, 0x9151F347u, 0xAC31DAF7u, 0xEB91A027u, 0xD6F18997u,
|
||||
0x64D15587u, 0x59B17C37u, 0x1E1106E7u, 0x23712F57u, 0x58F35849u,
|
||||
0x659371F9u, 0x22330B29u, 0x1F532299u, 0xAD73FE89u, 0x9013D739u,
|
||||
0xD7B3ADE9u, 0xEAD38459u, 0x68831388u, 0x55E33A38u, 0x124340E8u,
|
||||
0x2F236958u, 0x9D03B548u, 0xA0639CF8u, 0xE7C3E628u, 0xDAA3CF98u,
|
||||
0x3813CFCBu, 0x0573E67Bu, 0x42D39CABu, 0x7FB3B51Bu, 0xCD93690Bu,
|
||||
0xF0F340BBu, 0xB7533A6Bu, 0x8A3313DBu, 0x0863840Au, 0x3503ADBAu,
|
||||
0x72A3D76Au, 0x4FC3FEDAu, 0xFDE322CAu, 0xC0830B7Au, 0x872371AAu,
|
||||
0xBA43581Au, 0x9932774Du, 0xA4525EFDu, 0xE3F2242Du, 0xDE920D9Du,
|
||||
0x6CB2D18Du, 0x51D2F83Du, 0x167282EDu, 0x2B12AB5Du, 0xA9423C8Cu,
|
||||
0x9422153Cu, 0xD3826FECu, 0xEEE2465Cu, 0x5CC29A4Cu, 0x61A2B3FCu,
|
||||
0x2602C92Cu, 0x1B62E09Cu, 0xF9D2E0CFu, 0xC4B2C97Fu, 0x8312B3AFu,
|
||||
0xBE729A1Fu, 0x0C52460Fu, 0x31326FBFu, 0x7692156Fu, 0x4BF23CDFu,
|
||||
0xC9A2AB0Eu, 0xF4C282BEu, 0xB362F86Eu, 0x8E02D1DEu, 0x3C220DCEu,
|
||||
0x0142247Eu, 0x46E25EAEu, 0x7B82771Eu, 0xB1E6B092u, 0x8C869922u,
|
||||
0xCB26E3F2u, 0xF646CA42u, 0x44661652u, 0x79063FE2u, 0x3EA64532u,
|
||||
0x03C66C82u, 0x8196FB53u, 0xBCF6D2E3u, 0xFB56A833u, 0xC6368183u,
|
||||
0x74165D93u, 0x49767423u, 0x0ED60EF3u, 0x33B62743u, 0xD1062710u,
|
||||
0xEC660EA0u, 0xABC67470u, 0x96A65DC0u, 0x248681D0u, 0x19E6A860u,
|
||||
0x5E46D2B0u, 0x6326FB00u, 0xE1766CD1u, 0xDC164561u, 0x9BB63FB1u,
|
||||
0xA6D61601u, 0x14F6CA11u, 0x2996E3A1u, 0x6E369971u, 0x5356B0C1u,
|
||||
0x70279F96u, 0x4D47B626u, 0x0AE7CCF6u, 0x3787E546u, 0x85A73956u,
|
||||
0xB8C710E6u, 0xFF676A36u, 0xC2074386u, 0x4057D457u, 0x7D37FDE7u,
|
||||
0x3A978737u, 0x07F7AE87u, 0xB5D77297u, 0x88B75B27u, 0xCF1721F7u,
|
||||
0xF2770847u, 0x10C70814u, 0x2DA721A4u, 0x6A075B74u, 0x576772C4u,
|
||||
0xE547AED4u, 0xD8278764u, 0x9F87FDB4u, 0xA2E7D404u, 0x20B743D5u,
|
||||
0x1DD76A65u, 0x5A7710B5u, 0x67173905u, 0xD537E515u, 0xE857CCA5u,
|
||||
0xAFF7B675u, 0x92979FC5u, 0xE915E8DBu, 0xD475C16Bu, 0x93D5BBBBu,
|
||||
0xAEB5920Bu, 0x1C954E1Bu, 0x21F567ABu, 0x66551D7Bu, 0x5B3534CBu,
|
||||
0xD965A31Au, 0xE4058AAAu, 0xA3A5F07Au, 0x9EC5D9CAu, 0x2CE505DAu,
|
||||
0x11852C6Au, 0x562556BAu, 0x6B457F0Au, 0x89F57F59u, 0xB49556E9u,
|
||||
0xF3352C39u, 0xCE550589u, 0x7C75D999u, 0x4115F029u, 0x06B58AF9u,
|
||||
0x3BD5A349u, 0xB9853498u, 0x84E51D28u, 0xC34567F8u, 0xFE254E48u,
|
||||
0x4C059258u, 0x7165BBE8u, 0x36C5C138u, 0x0BA5E888u, 0x28D4C7DFu,
|
||||
0x15B4EE6Fu, 0x521494BFu, 0x6F74BD0Fu, 0xDD54611Fu, 0xE03448AFu,
|
||||
0xA794327Fu, 0x9AF41BCFu, 0x18A48C1Eu, 0x25C4A5AEu, 0x6264DF7Eu,
|
||||
0x5F04F6CEu, 0xED242ADEu, 0xD044036Eu, 0x97E479BEu, 0xAA84500Eu,
|
||||
0x4834505Du, 0x755479EDu, 0x32F4033Du, 0x0F942A8Du, 0xBDB4F69Du,
|
||||
0x80D4DF2Du, 0xC774A5FDu, 0xFA148C4Du, 0x78441B9Cu, 0x4524322Cu,
|
||||
0x028448FCu, 0x3FE4614Cu, 0x8DC4BD5Cu, 0xB0A494ECu, 0xF704EE3Cu,
|
||||
0xCA64C78Cu,
|
||||
};
|
||||
|
||||
private static readonly uint[] CrcTable5 = new uint[256]
|
||||
{
|
||||
0x00000000u, 0xCB5CD3A5u, 0x4DC8A10Bu, 0x869472AEu, 0x9B914216u,
|
||||
0x50CD91B3u, 0xD659E31Du, 0x1D0530B8u, 0xEC53826Du, 0x270F51C8u,
|
||||
0xA19B2366u, 0x6AC7F0C3u, 0x77C2C07Bu, 0xBC9E13DEu, 0x3A0A6170u,
|
||||
0xF156B2D5u, 0x03D6029Bu, 0xC88AD13Eu, 0x4E1EA390u, 0x85427035u,
|
||||
0x9847408Du, 0x531B9328u, 0xD58FE186u, 0x1ED33223u, 0xEF8580F6u,
|
||||
0x24D95353u, 0xA24D21FDu, 0x6911F258u, 0x7414C2E0u, 0xBF481145u,
|
||||
0x39DC63EBu, 0xF280B04Eu, 0x07AC0536u, 0xCCF0D693u, 0x4A64A43Du,
|
||||
0x81387798u, 0x9C3D4720u, 0x57619485u, 0xD1F5E62Bu, 0x1AA9358Eu,
|
||||
0xEBFF875Bu, 0x20A354FEu, 0xA6372650u, 0x6D6BF5F5u, 0x706EC54Du,
|
||||
0xBB3216E8u, 0x3DA66446u, 0xF6FAB7E3u, 0x047A07ADu, 0xCF26D408u,
|
||||
0x49B2A6A6u, 0x82EE7503u, 0x9FEB45BBu, 0x54B7961Eu, 0xD223E4B0u,
|
||||
0x197F3715u, 0xE82985C0u, 0x23755665u, 0xA5E124CBu, 0x6EBDF76Eu,
|
||||
0x73B8C7D6u, 0xB8E41473u, 0x3E7066DDu, 0xF52CB578u, 0x0F580A6Cu,
|
||||
0xC404D9C9u, 0x4290AB67u, 0x89CC78C2u, 0x94C9487Au, 0x5F959BDFu,
|
||||
0xD901E971u, 0x125D3AD4u, 0xE30B8801u, 0x28575BA4u, 0xAEC3290Au,
|
||||
0x659FFAAFu, 0x789ACA17u, 0xB3C619B2u, 0x35526B1Cu, 0xFE0EB8B9u,
|
||||
0x0C8E08F7u, 0xC7D2DB52u, 0x4146A9FCu, 0x8A1A7A59u, 0x971F4AE1u,
|
||||
0x5C439944u, 0xDAD7EBEAu, 0x118B384Fu, 0xE0DD8A9Au, 0x2B81593Fu,
|
||||
0xAD152B91u, 0x6649F834u, 0x7B4CC88Cu, 0xB0101B29u, 0x36846987u,
|
||||
0xFDD8BA22u, 0x08F40F5Au, 0xC3A8DCFFu, 0x453CAE51u, 0x8E607DF4u,
|
||||
0x93654D4Cu, 0x58399EE9u, 0xDEADEC47u, 0x15F13FE2u, 0xE4A78D37u,
|
||||
0x2FFB5E92u, 0xA96F2C3Cu, 0x6233FF99u, 0x7F36CF21u, 0xB46A1C84u,
|
||||
0x32FE6E2Au, 0xF9A2BD8Fu, 0x0B220DC1u, 0xC07EDE64u, 0x46EAACCAu,
|
||||
0x8DB67F6Fu, 0x90B34FD7u, 0x5BEF9C72u, 0xDD7BEEDCu, 0x16273D79u,
|
||||
0xE7718FACu, 0x2C2D5C09u, 0xAAB92EA7u, 0x61E5FD02u, 0x7CE0CDBAu,
|
||||
0xB7BC1E1Fu, 0x31286CB1u, 0xFA74BF14u, 0x1EB014D8u, 0xD5ECC77Du,
|
||||
0x5378B5D3u, 0x98246676u, 0x852156CEu, 0x4E7D856Bu, 0xC8E9F7C5u,
|
||||
0x03B52460u, 0xF2E396B5u, 0x39BF4510u, 0xBF2B37BEu, 0x7477E41Bu,
|
||||
0x6972D4A3u, 0xA22E0706u, 0x24BA75A8u, 0xEFE6A60Du, 0x1D661643u,
|
||||
0xD63AC5E6u, 0x50AEB748u, 0x9BF264EDu, 0x86F75455u, 0x4DAB87F0u,
|
||||
0xCB3FF55Eu, 0x006326FBu, 0xF135942Eu, 0x3A69478Bu, 0xBCFD3525u,
|
||||
0x77A1E680u, 0x6AA4D638u, 0xA1F8059Du, 0x276C7733u, 0xEC30A496u,
|
||||
0x191C11EEu, 0xD240C24Bu, 0x54D4B0E5u, 0x9F886340u, 0x828D53F8u,
|
||||
0x49D1805Du, 0xCF45F2F3u, 0x04192156u, 0xF54F9383u, 0x3E134026u,
|
||||
0xB8873288u, 0x73DBE12Du, 0x6EDED195u, 0xA5820230u, 0x2316709Eu,
|
||||
0xE84AA33Bu, 0x1ACA1375u, 0xD196C0D0u, 0x5702B27Eu, 0x9C5E61DBu,
|
||||
0x815B5163u, 0x4A0782C6u, 0xCC93F068u, 0x07CF23CDu, 0xF6999118u,
|
||||
0x3DC542BDu, 0xBB513013u, 0x700DE3B6u, 0x6D08D30Eu, 0xA65400ABu,
|
||||
0x20C07205u, 0xEB9CA1A0u, 0x11E81EB4u, 0xDAB4CD11u, 0x5C20BFBFu,
|
||||
0x977C6C1Au, 0x8A795CA2u, 0x41258F07u, 0xC7B1FDA9u, 0x0CED2E0Cu,
|
||||
0xFDBB9CD9u, 0x36E74F7Cu, 0xB0733DD2u, 0x7B2FEE77u, 0x662ADECFu,
|
||||
0xAD760D6Au, 0x2BE27FC4u, 0xE0BEAC61u, 0x123E1C2Fu, 0xD962CF8Au,
|
||||
0x5FF6BD24u, 0x94AA6E81u, 0x89AF5E39u, 0x42F38D9Cu, 0xC467FF32u,
|
||||
0x0F3B2C97u, 0xFE6D9E42u, 0x35314DE7u, 0xB3A53F49u, 0x78F9ECECu,
|
||||
0x65FCDC54u, 0xAEA00FF1u, 0x28347D5Fu, 0xE368AEFAu, 0x16441B82u,
|
||||
0xDD18C827u, 0x5B8CBA89u, 0x90D0692Cu, 0x8DD55994u, 0x46898A31u,
|
||||
0xC01DF89Fu, 0x0B412B3Au, 0xFA1799EFu, 0x314B4A4Au, 0xB7DF38E4u,
|
||||
0x7C83EB41u, 0x6186DBF9u, 0xAADA085Cu, 0x2C4E7AF2u, 0xE712A957u,
|
||||
0x15921919u, 0xDECECABCu, 0x585AB812u, 0x93066BB7u, 0x8E035B0Fu,
|
||||
0x455F88AAu, 0xC3CBFA04u, 0x089729A1u, 0xF9C19B74u, 0x329D48D1u,
|
||||
0xB4093A7Fu, 0x7F55E9DAu, 0x6250D962u, 0xA90C0AC7u, 0x2F987869u,
|
||||
0xE4C4ABCCu,
|
||||
};
|
||||
|
||||
private static readonly uint[] CrcTable6 = new uint[256]
|
||||
{
|
||||
0x00000000u, 0xA6770BB4u, 0x979F1129u, 0x31E81A9Du, 0xF44F2413u,
|
||||
0x52382FA7u, 0x63D0353Au, 0xC5A73E8Eu, 0x33EF4E67u, 0x959845D3u,
|
||||
0xA4705F4Eu, 0x020754FAu, 0xC7A06A74u, 0x61D761C0u, 0x503F7B5Du,
|
||||
0xF64870E9u, 0x67DE9CCEu, 0xC1A9977Au, 0xF0418DE7u, 0x56368653u,
|
||||
0x9391B8DDu, 0x35E6B369u, 0x040EA9F4u, 0xA279A240u, 0x5431D2A9u,
|
||||
0xF246D91Du, 0xC3AEC380u, 0x65D9C834u, 0xA07EF6BAu, 0x0609FD0Eu,
|
||||
0x37E1E793u, 0x9196EC27u, 0xCFBD399Cu, 0x69CA3228u, 0x582228B5u,
|
||||
0xFE552301u, 0x3BF21D8Fu, 0x9D85163Bu, 0xAC6D0CA6u, 0x0A1A0712u,
|
||||
0xFC5277FBu, 0x5A257C4Fu, 0x6BCD66D2u, 0xCDBA6D66u, 0x081D53E8u,
|
||||
0xAE6A585Cu, 0x9F8242C1u, 0x39F54975u, 0xA863A552u, 0x0E14AEE6u,
|
||||
0x3FFCB47Bu, 0x998BBFCFu, 0x5C2C8141u, 0xFA5B8AF5u, 0xCBB39068u,
|
||||
0x6DC49BDCu, 0x9B8CEB35u, 0x3DFBE081u, 0x0C13FA1Cu, 0xAA64F1A8u,
|
||||
0x6FC3CF26u, 0xC9B4C492u, 0xF85CDE0Fu, 0x5E2BD5BBu, 0x440B7579u,
|
||||
0xE27C7ECDu, 0xD3946450u, 0x75E36FE4u, 0xB044516Au, 0x16335ADEu,
|
||||
0x27DB4043u, 0x81AC4BF7u, 0x77E43B1Eu, 0xD19330AAu, 0xE07B2A37u,
|
||||
0x460C2183u, 0x83AB1F0Du, 0x25DC14B9u, 0x14340E24u, 0xB2430590u,
|
||||
0x23D5E9B7u, 0x85A2E203u, 0xB44AF89Eu, 0x123DF32Au, 0xD79ACDA4u,
|
||||
0x71EDC610u, 0x4005DC8Du, 0xE672D739u, 0x103AA7D0u, 0xB64DAC64u,
|
||||
0x87A5B6F9u, 0x21D2BD4Du, 0xE47583C3u, 0x42028877u, 0x73EA92EAu,
|
||||
0xD59D995Eu, 0x8BB64CE5u, 0x2DC14751u, 0x1C295DCCu, 0xBA5E5678u,
|
||||
0x7FF968F6u, 0xD98E6342u, 0xE86679DFu, 0x4E11726Bu, 0xB8590282u,
|
||||
0x1E2E0936u, 0x2FC613ABu, 0x89B1181Fu, 0x4C162691u, 0xEA612D25u,
|
||||
0xDB8937B8u, 0x7DFE3C0Cu, 0xEC68D02Bu, 0x4A1FDB9Fu, 0x7BF7C102u,
|
||||
0xDD80CAB6u, 0x1827F438u, 0xBE50FF8Cu, 0x8FB8E511u, 0x29CFEEA5u,
|
||||
0xDF879E4Cu, 0x79F095F8u, 0x48188F65u, 0xEE6F84D1u, 0x2BC8BA5Fu,
|
||||
0x8DBFB1EBu, 0xBC57AB76u, 0x1A20A0C2u, 0x8816EAF2u, 0x2E61E146u,
|
||||
0x1F89FBDBu, 0xB9FEF06Fu, 0x7C59CEE1u, 0xDA2EC555u, 0xEBC6DFC8u,
|
||||
0x4DB1D47Cu, 0xBBF9A495u, 0x1D8EAF21u, 0x2C66B5BCu, 0x8A11BE08u,
|
||||
0x4FB68086u, 0xE9C18B32u, 0xD82991AFu, 0x7E5E9A1Bu, 0xEFC8763Cu,
|
||||
0x49BF7D88u, 0x78576715u, 0xDE206CA1u, 0x1B87522Fu, 0xBDF0599Bu,
|
||||
0x8C184306u, 0x2A6F48B2u, 0xDC27385Bu, 0x7A5033EFu, 0x4BB82972u,
|
||||
0xEDCF22C6u, 0x28681C48u, 0x8E1F17FCu, 0xBFF70D61u, 0x198006D5u,
|
||||
0x47ABD36Eu, 0xE1DCD8DAu, 0xD034C247u, 0x7643C9F3u, 0xB3E4F77Du,
|
||||
0x1593FCC9u, 0x247BE654u, 0x820CEDE0u, 0x74449D09u, 0xD23396BDu,
|
||||
0xE3DB8C20u, 0x45AC8794u, 0x800BB91Au, 0x267CB2AEu, 0x1794A833u,
|
||||
0xB1E3A387u, 0x20754FA0u, 0x86024414u, 0xB7EA5E89u, 0x119D553Du,
|
||||
0xD43A6BB3u, 0x724D6007u, 0x43A57A9Au, 0xE5D2712Eu, 0x139A01C7u,
|
||||
0xB5ED0A73u, 0x840510EEu, 0x22721B5Au, 0xE7D525D4u, 0x41A22E60u,
|
||||
0x704A34FDu, 0xD63D3F49u, 0xCC1D9F8Bu, 0x6A6A943Fu, 0x5B828EA2u,
|
||||
0xFDF58516u, 0x3852BB98u, 0x9E25B02Cu, 0xAFCDAAB1u, 0x09BAA105u,
|
||||
0xFFF2D1ECu, 0x5985DA58u, 0x686DC0C5u, 0xCE1ACB71u, 0x0BBDF5FFu,
|
||||
0xADCAFE4Bu, 0x9C22E4D6u, 0x3A55EF62u, 0xABC30345u, 0x0DB408F1u,
|
||||
0x3C5C126Cu, 0x9A2B19D8u, 0x5F8C2756u, 0xF9FB2CE2u, 0xC813367Fu,
|
||||
0x6E643DCBu, 0x982C4D22u, 0x3E5B4696u, 0x0FB35C0Bu, 0xA9C457BFu,
|
||||
0x6C636931u, 0xCA146285u, 0xFBFC7818u, 0x5D8B73ACu, 0x03A0A617u,
|
||||
0xA5D7ADA3u, 0x943FB73Eu, 0x3248BC8Au, 0xF7EF8204u, 0x519889B0u,
|
||||
0x6070932Du, 0xC6079899u, 0x304FE870u, 0x9638E3C4u, 0xA7D0F959u,
|
||||
0x01A7F2EDu, 0xC400CC63u, 0x6277C7D7u, 0x539FDD4Au, 0xF5E8D6FEu,
|
||||
0x647E3AD9u, 0xC209316Du, 0xF3E12BF0u, 0x55962044u, 0x90311ECAu,
|
||||
0x3646157Eu, 0x07AE0FE3u, 0xA1D90457u, 0x579174BEu, 0xF1E67F0Au,
|
||||
0xC00E6597u, 0x66796E23u, 0xA3DE50ADu, 0x05A95B19u, 0x34414184u,
|
||||
0x92364A30u,
|
||||
};
|
||||
|
||||
private static readonly uint[] CrcTable7 = new uint[256]
|
||||
{
|
||||
0x00000000u, 0xCCAA009Eu, 0x4225077Du, 0x8E8F07E3u, 0x844A0EFAu,
|
||||
0x48E00E64u, 0xC66F0987u, 0x0AC50919u, 0xD3E51BB5u, 0x1F4F1B2Bu,
|
||||
0x91C01CC8u, 0x5D6A1C56u, 0x57AF154Fu, 0x9B0515D1u, 0x158A1232u,
|
||||
0xD92012ACu, 0x7CBB312Bu, 0xB01131B5u, 0x3E9E3656u, 0xF23436C8u,
|
||||
0xF8F13FD1u, 0x345B3F4Fu, 0xBAD438ACu, 0x767E3832u, 0xAF5E2A9Eu,
|
||||
0x63F42A00u, 0xED7B2DE3u, 0x21D12D7Du, 0x2B142464u, 0xE7BE24FAu,
|
||||
0x69312319u, 0xA59B2387u, 0xF9766256u, 0x35DC62C8u, 0xBB53652Bu,
|
||||
0x77F965B5u, 0x7D3C6CACu, 0xB1966C32u, 0x3F196BD1u, 0xF3B36B4Fu,
|
||||
0x2A9379E3u, 0xE639797Du, 0x68B67E9Eu, 0xA41C7E00u, 0xAED97719u,
|
||||
0x62737787u, 0xECFC7064u, 0x205670FAu, 0x85CD537Du, 0x496753E3u,
|
||||
0xC7E85400u, 0x0B42549Eu, 0x01875D87u, 0xCD2D5D19u, 0x43A25AFAu,
|
||||
0x8F085A64u, 0x562848C8u, 0x9A824856u, 0x140D4FB5u, 0xD8A74F2Bu,
|
||||
0xD2624632u, 0x1EC846ACu, 0x9047414Fu, 0x5CED41D1u, 0x299DC2EDu,
|
||||
0xE537C273u, 0x6BB8C590u, 0xA712C50Eu, 0xADD7CC17u, 0x617DCC89u,
|
||||
0xEFF2CB6Au, 0x2358CBF4u, 0xFA78D958u, 0x36D2D9C6u, 0xB85DDE25u,
|
||||
0x74F7DEBBu, 0x7E32D7A2u, 0xB298D73Cu, 0x3C17D0DFu, 0xF0BDD041u,
|
||||
0x5526F3C6u, 0x998CF358u, 0x1703F4BBu, 0xDBA9F425u, 0xD16CFD3Cu,
|
||||
0x1DC6FDA2u, 0x9349FA41u, 0x5FE3FADFu, 0x86C3E873u, 0x4A69E8EDu,
|
||||
0xC4E6EF0Eu, 0x084CEF90u, 0x0289E689u, 0xCE23E617u, 0x40ACE1F4u,
|
||||
0x8C06E16Au, 0xD0EBA0BBu, 0x1C41A025u, 0x92CEA7C6u, 0x5E64A758u,
|
||||
0x54A1AE41u, 0x980BAEDFu, 0x1684A93Cu, 0xDA2EA9A2u, 0x030EBB0Eu,
|
||||
0xCFA4BB90u, 0x412BBC73u, 0x8D81BCEDu, 0x8744B5F4u, 0x4BEEB56Au,
|
||||
0xC561B289u, 0x09CBB217u, 0xAC509190u, 0x60FA910Eu, 0xEE7596EDu,
|
||||
0x22DF9673u, 0x281A9F6Au, 0xE4B09FF4u, 0x6A3F9817u, 0xA6959889u,
|
||||
0x7FB58A25u, 0xB31F8ABBu, 0x3D908D58u, 0xF13A8DC6u, 0xFBFF84DFu,
|
||||
0x37558441u, 0xB9DA83A2u, 0x7570833Cu, 0x533B85DAu, 0x9F918544u,
|
||||
0x111E82A7u, 0xDDB48239u, 0xD7718B20u, 0x1BDB8BBEu, 0x95548C5Du,
|
||||
0x59FE8CC3u, 0x80DE9E6Fu, 0x4C749EF1u, 0xC2FB9912u, 0x0E51998Cu,
|
||||
0x04949095u, 0xC83E900Bu, 0x46B197E8u, 0x8A1B9776u, 0x2F80B4F1u,
|
||||
0xE32AB46Fu, 0x6DA5B38Cu, 0xA10FB312u, 0xABCABA0Bu, 0x6760BA95u,
|
||||
0xE9EFBD76u, 0x2545BDE8u, 0xFC65AF44u, 0x30CFAFDAu, 0xBE40A839u,
|
||||
0x72EAA8A7u, 0x782FA1BEu, 0xB485A120u, 0x3A0AA6C3u, 0xF6A0A65Du,
|
||||
0xAA4DE78Cu, 0x66E7E712u, 0xE868E0F1u, 0x24C2E06Fu, 0x2E07E976u,
|
||||
0xE2ADE9E8u, 0x6C22EE0Bu, 0xA088EE95u, 0x79A8FC39u, 0xB502FCA7u,
|
||||
0x3B8DFB44u, 0xF727FBDAu, 0xFDE2F2C3u, 0x3148F25Du, 0xBFC7F5BEu,
|
||||
0x736DF520u, 0xD6F6D6A7u, 0x1A5CD639u, 0x94D3D1DAu, 0x5879D144u,
|
||||
0x52BCD85Du, 0x9E16D8C3u, 0x1099DF20u, 0xDC33DFBEu, 0x0513CD12u,
|
||||
0xC9B9CD8Cu, 0x4736CA6Fu, 0x8B9CCAF1u, 0x8159C3E8u, 0x4DF3C376u,
|
||||
0xC37CC495u, 0x0FD6C40Bu, 0x7AA64737u, 0xB60C47A9u, 0x3883404Au,
|
||||
0xF42940D4u, 0xFEEC49CDu, 0x32464953u, 0xBCC94EB0u, 0x70634E2Eu,
|
||||
0xA9435C82u, 0x65E95C1Cu, 0xEB665BFFu, 0x27CC5B61u, 0x2D095278u,
|
||||
0xE1A352E6u, 0x6F2C5505u, 0xA386559Bu, 0x061D761Cu, 0xCAB77682u,
|
||||
0x44387161u, 0x889271FFu, 0x825778E6u, 0x4EFD7878u, 0xC0727F9Bu,
|
||||
0x0CD87F05u, 0xD5F86DA9u, 0x19526D37u, 0x97DD6AD4u, 0x5B776A4Au,
|
||||
0x51B26353u, 0x9D1863CDu, 0x1397642Eu, 0xDF3D64B0u, 0x83D02561u,
|
||||
0x4F7A25FFu, 0xC1F5221Cu, 0x0D5F2282u, 0x079A2B9Bu, 0xCB302B05u,
|
||||
0x45BF2CE6u, 0x89152C78u, 0x50353ED4u, 0x9C9F3E4Au, 0x121039A9u,
|
||||
0xDEBA3937u, 0xD47F302Eu, 0x18D530B0u, 0x965A3753u, 0x5AF037CDu,
|
||||
0xFF6B144Au, 0x33C114D4u, 0xBD4E1337u, 0x71E413A9u, 0x7B211AB0u,
|
||||
0xB78B1A2Eu, 0x39041DCDu, 0xF5AE1D53u, 0x2C8E0FFFu, 0xE0240F61u,
|
||||
0x6EAB0882u, 0xA201081Cu, 0xA8C40105u, 0x646E019Bu, 0xEAE10678u,
|
||||
0x264B06E6u,
|
||||
};
|
||||
|
||||
public static unsafe uint Update(uint crc32, ReadOnlySpan<byte> span)
|
||||
{
|
||||
Contract.Assert(BitConverter.IsLittleEndian, "Little Endian expected");
|
||||
|
||||
crc32 ^= 0xFFFFFFFFU;
|
||||
int offset = 0;
|
||||
int runningLength = (span.Length / 8) * 8;
|
||||
int endBytes = span.Length - runningLength;
|
||||
|
||||
fixed (uint* words = MemoryMarshal.Cast<byte, uint>(span))
|
||||
{
|
||||
for (int i = 0; i < runningLength / 8; i++)
|
||||
{
|
||||
crc32 ^= words[offset];
|
||||
offset += 1;
|
||||
uint term1 = Crc32.CrcTable7[crc32 & 0x000000FF] ^
|
||||
Crc32.CrcTable6[(crc32 >> 8) & 0x000000FF];
|
||||
|
||||
uint term2 = crc32 >> 16;
|
||||
crc32 = term1 ^
|
||||
Crc32.CrcTable5[term2 & 0x000000FF] ^
|
||||
Crc32.CrcTable4[(term2 >> 8) & 0x000000FF];
|
||||
|
||||
uint term3 = words[offset];
|
||||
offset += 1;
|
||||
term1 = Crc32.CrcTable3[term3 & 0x000000FF] ^
|
||||
Crc32.CrcTable2[(term3 >> 8) & 0x000000FF];
|
||||
|
||||
term2 = term3 >> 16;
|
||||
crc32 ^= term1 ^
|
||||
Crc32.CrcTable1[term2 & 0x000000FF] ^
|
||||
Crc32.CrcTable0[(term2 >> 8) & 0x000000FF];
|
||||
}
|
||||
}
|
||||
|
||||
offset = runningLength;
|
||||
for (int i = 0; i < endBytes; i++)
|
||||
{
|
||||
crc32 = Crc32.CrcTable0[(crc32 ^ span[offset++]) & 0x000000FF] ^ (crc32 >> 8);
|
||||
}
|
||||
|
||||
crc32 ^= 0xFFFFFFFFU;
|
||||
return crc32;
|
||||
}
|
||||
}
|
||||
}
|
||||
31
src/Core/Core/Linear.cs
Normal file
31
src/Core/Core/Linear.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Azure.Cosmos.Core
|
||||
{
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
public static class Linear
|
||||
{
|
||||
/// <summary>
|
||||
/// Perform the managed equivalent of std::move by returning the value at
|
||||
/// <paramref name="src" /> while simultaneously assigning <see cref="T:default" /> to
|
||||
/// <paramref name="src" />.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the value to transfer.</typeparam>
|
||||
/// <param name="src">A reference to the field whose value should be transferred.</param>
|
||||
/// <returns>The value transferred.</returns>
|
||||
/// <remarks>
|
||||
/// The value of <paramref name="src" /> after the transfer is always <see cref="T:default" />
|
||||
/// . The value is considered "consumed".
|
||||
/// </remarks>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static T Move<T>(ref T src)
|
||||
{
|
||||
T retval = src;
|
||||
src = default;
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
}
|
||||
12
src/Core/Core/Microsoft.Azure.Cosmos.Core.csproj
Normal file
12
src/Core/Core/Microsoft.Azure.Cosmos.Core.csproj
Normal file
@@ -0,0 +1,12 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.7.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
136
src/Core/Core/SpanHexExtensions.cs
Normal file
136
src/Core/Core/SpanHexExtensions.cs
Normal file
@@ -0,0 +1,136 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Azure.Cosmos.Core
|
||||
{
|
||||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
public static class SpanHexExtensions
|
||||
{
|
||||
private static readonly byte[] DecodeTable =
|
||||
{
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
|
||||
};
|
||||
|
||||
private static readonly uint[] EncodeTable = SpanHexExtensions.Initialize();
|
||||
|
||||
public static unsafe string ToHexString(this ReadOnlySpan<byte> bytes)
|
||||
{
|
||||
int len = bytes.Length;
|
||||
string result = new string((char)0, len * 2);
|
||||
fixed (uint* lp = SpanHexExtensions.EncodeTable)
|
||||
{
|
||||
fixed (byte* bp = bytes)
|
||||
{
|
||||
fixed (char* rp = result)
|
||||
{
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
((uint*)rp)[i] = lp[bp[i]];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static bool TryParseHexString(this ReadOnlySpan<char> hexChars, out byte[] result)
|
||||
{
|
||||
Contract.Requires(hexChars.Length % 2 == 0);
|
||||
|
||||
int len = hexChars.Length;
|
||||
result = new byte[len / 2];
|
||||
if (!hexChars.TryParseHexString(result.AsSpan()))
|
||||
{
|
||||
result = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static unsafe bool TryParseHexString(this ReadOnlySpan<char> hexChars, Span<byte> result)
|
||||
{
|
||||
Contract.Requires(hexChars.Length % 2 == 0);
|
||||
Contract.Requires(result.Length == hexChars.Length / 2);
|
||||
|
||||
int len = hexChars.Length;
|
||||
fixed (byte* lp = SpanHexExtensions.DecodeTable)
|
||||
{
|
||||
fixed (char* cp = hexChars)
|
||||
{
|
||||
fixed (byte* rp = result)
|
||||
{
|
||||
for (int i = 0; i < len; i += 2)
|
||||
{
|
||||
int c1 = cp[i];
|
||||
if (c1 < 0 || c1 > 255)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
byte b1 = lp[c1];
|
||||
if (b1 == 255)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int c2 = cp[i + 1];
|
||||
if (c2 < 0 || c2 > 255)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
byte b2 = lp[c2];
|
||||
if (b2 == 255)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
rp[i / 2] = (byte)((b1 << 4) | b2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static uint[] Initialize()
|
||||
{
|
||||
uint[] result = new uint[256];
|
||||
for (int i = 0; i < 256; i++)
|
||||
{
|
||||
string s = i.ToString("X2", CultureInfo.InvariantCulture);
|
||||
if (BitConverter.IsLittleEndian)
|
||||
{
|
||||
result[i] = s[0] + ((uint)s[1] << 16);
|
||||
}
|
||||
else
|
||||
{
|
||||
result[i] = s[1] + ((uint)s[0] << 16);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
34
src/Core/Core/Utf8/Readme.md
Normal file
34
src/Core/Core/Utf8/Readme.md
Normal file
@@ -0,0 +1,34 @@
|
||||
This directory contains types derived from
|
||||
[dotnet/corefxlab](https://github.com/dotnet/corefxlab) repo. This repo contains designs
|
||||
proposed by the CLR team but not yet committed for inclusion in either the C# language
|
||||
or the standard .NET Framework. The types included here (e.g. Utf8Span) may **never**
|
||||
appear in the official standard. Including the types here lays a foundation for adopting
|
||||
these types **if** they do become standard in the future.
|
||||
|
||||
[[_TOC_]]
|
||||
|
||||
|
||||
## Utf8Span
|
||||
A readonly struct wrapping a sequence of bytes that are guaranteed to be a valid UTF8
|
||||
encoded string.
|
||||
|
||||
A `Utf8Span` can be created over a `ReadOnlySpan<byte>` at the cost of validating the
|
||||
byte sequence. Once the byte sequence has been validated then a `Utf8Span` can be passed
|
||||
around safely without re-validating the content as UTF8. The type system is used to
|
||||
enforce the correctness.
|
||||
|
||||
## Utf8String
|
||||
A readonly class wrapping a sequence of bytes that are guaranteed to be a valid UTF8
|
||||
encoded string.
|
||||
|
||||
`Utf8String` is the heap equivalent of `Utf8Span` and provides the same capabilities.
|
||||
`Utf8String` can be implicitly converted to `Utf8Span`. This conversion is guaranteed
|
||||
to be cheap and non-allocating. Converting from a `Utf8Span` to a `Utf8String`, however,
|
||||
requires a blittable copy of the content (but not re-validation). Additionally, `Utf8String`
|
||||
can be converted to a `string` object via the expected transcode process. This operation is expensive.
|
||||
|
||||
## UtfAnyString
|
||||
A readonly struct wrapping either a `Utf8String` or a `string` object. The `UtfAnyString`
|
||||
enables API's to accept both UTF8 and UTF16 encoded strings without requiring overloads.
|
||||
UtfAnyString provides implicit conversion **from** either type, but explicit convert **to**
|
||||
either type (because such a conversion *may* require a transcoding copy).
|
||||
51
src/Core/Core/Utf8/Utf16LittleEndianCodePointEnumerator.cs
Normal file
51
src/Core/Core/Utf8/Utf16LittleEndianCodePointEnumerator.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Azure.Cosmos.Core.Utf8
|
||||
{
|
||||
using System;
|
||||
|
||||
internal struct Utf16LittleEndianCodePointEnumerator
|
||||
{
|
||||
private readonly string str;
|
||||
private int index;
|
||||
private uint codePoint;
|
||||
private bool hasValue;
|
||||
|
||||
public Utf16LittleEndianCodePointEnumerator(string str)
|
||||
{
|
||||
Contract.Assert(BitConverter.IsLittleEndian);
|
||||
|
||||
this.str = str;
|
||||
this.index = 0;
|
||||
this.codePoint = 0;
|
||||
this.hasValue = false;
|
||||
}
|
||||
|
||||
public uint Current
|
||||
{
|
||||
get
|
||||
{
|
||||
Contract.Requires(this.hasValue);
|
||||
return this.codePoint;
|
||||
}
|
||||
}
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
if (this.index >= this.str.Length)
|
||||
{
|
||||
this.hasValue = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.hasValue = Utf8Helper.TryDecodeCodePointFromString(this.str, this.index, out this.codePoint, out int charsConsumed);
|
||||
Contract.Invariant(this.hasValue && charsConsumed > 0, "Invalid code point!");
|
||||
this.index += charsConsumed;
|
||||
}
|
||||
|
||||
return this.hasValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
49
src/Core/Core/Utf8/Utf8CodePointEnumerator.cs
Normal file
49
src/Core/Core/Utf8/Utf8CodePointEnumerator.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Azure.Cosmos.Core.Utf8
|
||||
{
|
||||
using System;
|
||||
|
||||
public ref struct Utf8CodePointEnumerator
|
||||
{
|
||||
private readonly ReadOnlySpan<byte> utf8Bytes;
|
||||
private int index;
|
||||
private uint codePoint;
|
||||
private bool hasValue;
|
||||
|
||||
public Utf8CodePointEnumerator(ReadOnlySpan<byte> utf8Bytes)
|
||||
{
|
||||
this.utf8Bytes = utf8Bytes;
|
||||
this.index = 0;
|
||||
this.codePoint = 0;
|
||||
this.hasValue = false;
|
||||
}
|
||||
|
||||
public uint Current
|
||||
{
|
||||
get
|
||||
{
|
||||
Contract.Requires(this.hasValue);
|
||||
return this.codePoint;
|
||||
}
|
||||
}
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
if (this.index >= this.utf8Bytes.Length)
|
||||
{
|
||||
this.hasValue = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.hasValue = Utf8Helper.TryDecodeCodePoint(this.utf8Bytes, this.index, out this.codePoint, out int bytesConsumed);
|
||||
Contract.Invariant(this.hasValue && bytesConsumed > 0, "Invalid code point!");
|
||||
this.index += bytesConsumed;
|
||||
}
|
||||
|
||||
return this.hasValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
134
src/Core/Core/Utf8/Utf8Helper.cs
Normal file
134
src/Core/Core/Utf8/Utf8Helper.cs
Normal file
@@ -0,0 +1,134 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Azure.Cosmos.Core.Utf8
|
||||
{
|
||||
using System;
|
||||
|
||||
internal static class Utf8Helper
|
||||
{
|
||||
public const int MaxCodeUnitsPerCodePoint = 4;
|
||||
|
||||
public static bool TryDecodeCodePoint(ReadOnlySpan<byte> utf8, int index, out uint codePoint, out int bytesConsumed)
|
||||
{
|
||||
if (index >= utf8.Length)
|
||||
{
|
||||
codePoint = default;
|
||||
bytesConsumed = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
byte first = utf8[index];
|
||||
|
||||
bytesConsumed = Utf8Helper.GetEncodedBytes(first);
|
||||
if (bytesConsumed == 0 || utf8.Length - index < bytesConsumed)
|
||||
{
|
||||
bytesConsumed = 0;
|
||||
codePoint = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (bytesConsumed)
|
||||
{
|
||||
case 1:
|
||||
codePoint = first;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
codePoint = (uint)(first & Utf8Helper.B0001_1111U);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
codePoint = (uint)(first & Utf8Helper.B0000_1111U);
|
||||
break;
|
||||
|
||||
case 4:
|
||||
codePoint = (uint)(first & Utf8Helper.B0000_0111U);
|
||||
break;
|
||||
|
||||
default:
|
||||
codePoint = default;
|
||||
bytesConsumed = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 1; i < bytesConsumed; i++)
|
||||
{
|
||||
uint current = utf8[index + i];
|
||||
if ((current & Utf8Helper.B1100_0000U) != Utf8Helper.B1000_0000U)
|
||||
{
|
||||
bytesConsumed = 0;
|
||||
codePoint = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
codePoint = (codePoint << 6) | (Utf8Helper.B0011_1111U & current);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool TryDecodeCodePointFromString(string s, int index, out uint codePoint, out int encodedChars)
|
||||
{
|
||||
if (index < 0 || index >= s.Length)
|
||||
{
|
||||
codePoint = default;
|
||||
encodedChars = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (index == s.Length - 1 && char.IsSurrogate(s[index]))
|
||||
{
|
||||
codePoint = default;
|
||||
encodedChars = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
encodedChars = char.IsHighSurrogate(s[index]) ? 2 : 1;
|
||||
codePoint = unchecked((uint)char.ConvertToUtf32(s, index));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static int GetEncodedBytes(byte b)
|
||||
{
|
||||
if ((b & Utf8Helper.B1000_0000U) == 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ((b & Utf8Helper.B1110_0000U) == Utf8Helper.B1100_0000U)
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
if ((b & Utf8Helper.B1111_0000U) == Utf8Helper.B1110_0000U)
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
|
||||
if ((b & Utf8Helper.B1111_1000U) == Utf8Helper.B1111_0000U)
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
#pragma warning disable SA1310 // Field names should not contain underscore
|
||||
private const byte B0000_0111U = 0x07; //7
|
||||
private const byte B0000_1111U = 0x0F; //15
|
||||
private const byte B0001_1111U = 0x1F; //31
|
||||
private const byte B0011_1111U = 0x3F; //63
|
||||
private const byte B1000_0000U = 0x80; //128
|
||||
private const byte B1100_0000U = 0xC0; //192
|
||||
private const byte B1110_0000U = 0xE0; //224
|
||||
private const byte B1111_0000U = 0xF0; //240
|
||||
private const byte B1111_1000U = 0xF8; //248
|
||||
|
||||
// ReSharper restore InconsistentNaming
|
||||
#pragma warning restore SA1310 // Field names should not contain underscore
|
||||
}
|
||||
}
|
||||
397
src/Core/Core/Utf8/Utf8Span.cs
Normal file
397
src/Core/Core/Utf8/Utf8Span.cs
Normal file
@@ -0,0 +1,397 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma warning disable CA1066 // Type {0} should implement IEquatable<T> because it overrides Equals
|
||||
|
||||
namespace Microsoft.Azure.Cosmos.Core.Utf8
|
||||
{
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
|
||||
// ReSharper disable once UseNameofExpression
|
||||
[DebuggerDisplay("{ToString()}")]
|
||||
public readonly ref struct Utf8Span
|
||||
{
|
||||
public static Utf8Span Empty => default;
|
||||
|
||||
private readonly ReadOnlySpan<byte> buffer;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private Utf8Span(ReadOnlySpan<byte> utf8Bytes)
|
||||
{
|
||||
this.buffer = utf8Bytes;
|
||||
}
|
||||
|
||||
/// <summary>Parses the sequence of bytes to prove it is valid UTF8.</summary>
|
||||
/// <param name="utf8Bytes">The bytes to validate.</param>
|
||||
/// <param name="span">
|
||||
/// If the sequence validates a <see cref="Utf8Span" /> that wraps the bytes in
|
||||
/// <paramref name="utf8Bytes" />, otherwise <see cref="t:default" />.
|
||||
/// </param>
|
||||
/// <returns>True if the sequence validates, false otherwise.</returns>
|
||||
public static bool TryParseUtf8Bytes(ReadOnlySpan<byte> utf8Bytes, out Utf8Span span)
|
||||
{
|
||||
int invalidIndex = Utf8Util.GetIndexOfFirstInvalidUtf8Sequence(utf8Bytes, out int _, out int _);
|
||||
if (invalidIndex != -1)
|
||||
{
|
||||
span = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
span = new Utf8Span(utf8Bytes);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>Creates a <see cref="Utf8Span" /> without validating the underlying bytes.</summary>
|
||||
/// <param name="utf8Bytes">The bytes claiming to be UTF8.</param>
|
||||
/// <returns>A <see cref="Utf8Span" /> wrapping <paramref name="utf8Bytes" />.</returns>
|
||||
/// <remarks>
|
||||
/// This method is dangerous as consumers of the <see cref="Utf8Span" /> must assume the
|
||||
/// underlying bytes are indeed valid UTF8. The method should <bold>only</bold> be used when the UTF8
|
||||
/// sequence has already been externally valid or is known to be valid by construction.
|
||||
/// </remarks>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Utf8Span UnsafeFromUtf8BytesNoValidation(ReadOnlySpan<byte> utf8Bytes)
|
||||
{
|
||||
return new Utf8Span(utf8Bytes);
|
||||
}
|
||||
|
||||
/// <summary>Creates a <see cref="Utf8Span" /> from a UTF16 encoding string.</summary>
|
||||
/// <param name="utf16String">The UTF16 encoding string.</param>
|
||||
/// <returns>A new <see cref="Utf8Span" />.</returns>
|
||||
/// <remarks>
|
||||
/// This method must transcode the UTF16 into UTF8 which both requires allocation and is a
|
||||
/// size of data operation.
|
||||
/// </remarks>
|
||||
public static Utf8Span TranscodeUtf16(string utf16String)
|
||||
{
|
||||
Contract.Requires(utf16String != null);
|
||||
|
||||
if (string.IsNullOrEmpty(utf16String))
|
||||
{
|
||||
return new Utf8Span(ReadOnlySpan<byte>.Empty);
|
||||
}
|
||||
|
||||
return new Utf8Span(Encoding.UTF8.GetBytes(utf16String));
|
||||
}
|
||||
|
||||
/// <summary>The UTF8 byte sequence.</summary>
|
||||
public ReadOnlySpan<byte> Span
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => this.buffer;
|
||||
}
|
||||
|
||||
/// <summary>The length in bytes of the UTF8 encoding.</summary>
|
||||
public int Length => this.Span.Length;
|
||||
|
||||
/// <summary>True if the length is empty.</summary>
|
||||
public bool IsEmpty => this.Span.Length == 0;
|
||||
|
||||
/// <summary>Non-allocating enumeration of each code point in the UTF8 stream.</summary>
|
||||
public Utf8CodePointEnumerator GetEnumerator()
|
||||
{
|
||||
return new Utf8CodePointEnumerator(this.buffer);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (this.buffer.IsEmpty)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
unsafe
|
||||
{
|
||||
// ReSharper disable once ImpureMethodCallOnReadonlyValueField
|
||||
fixed (byte* bytes = &this.buffer.GetPinnableReference())
|
||||
{
|
||||
return Encoding.UTF8.GetString(bytes, this.buffer.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool ReferenceEquals(Utf8Span other)
|
||||
{
|
||||
return this.buffer == other.buffer;
|
||||
}
|
||||
|
||||
public bool Equals(Utf8Span other)
|
||||
{
|
||||
return this.buffer.SequenceEqual(other.buffer);
|
||||
}
|
||||
|
||||
public bool Equals(string other)
|
||||
{
|
||||
Contract.Requires(other != null);
|
||||
|
||||
Utf8CodePointEnumerator thisEnumerator = this.GetEnumerator();
|
||||
Utf16LittleEndianCodePointEnumerator otherEnumerator = new Utf16LittleEndianCodePointEnumerator(other);
|
||||
|
||||
while (true)
|
||||
{
|
||||
bool hasNext = thisEnumerator.MoveNext();
|
||||
if (hasNext != otherEnumerator.MoveNext())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!hasNext)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (thisEnumerator.Current != otherEnumerator.Current)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
switch (obj)
|
||||
{
|
||||
case string s:
|
||||
return this.Equals(s);
|
||||
case Utf8String s:
|
||||
return this.Equals(s.Span);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
uint hash1 = 5381;
|
||||
uint hash2 = hash1;
|
||||
|
||||
Utf8CodePointEnumerator thisEnumerator = this.GetEnumerator();
|
||||
for (int i = 0; thisEnumerator.MoveNext(); i++)
|
||||
{
|
||||
uint c = thisEnumerator.Current;
|
||||
if (i % 2 == 0)
|
||||
{
|
||||
hash1 = ((hash1 << 5) + hash1) ^ c;
|
||||
}
|
||||
else
|
||||
{
|
||||
hash2 = ((hash2 << 5) + hash2) ^ c;
|
||||
}
|
||||
}
|
||||
|
||||
return (int)(hash1 + (hash2 * 1566083941));
|
||||
}
|
||||
}
|
||||
|
||||
public static bool operator ==(Utf8Span left, Utf8Span right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator !=(Utf8Span left, Utf8Span right)
|
||||
{
|
||||
return !left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator ==(Utf8Span left, string right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator !=(Utf8Span left, string right)
|
||||
{
|
||||
return !left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator ==(string left, Utf8Span right)
|
||||
{
|
||||
return right.Equals(left);
|
||||
}
|
||||
|
||||
public static bool operator !=(string left, Utf8Span right)
|
||||
{
|
||||
return !right.Equals(left);
|
||||
}
|
||||
|
||||
public static bool operator <(Utf8Span left, Utf8Span right)
|
||||
{
|
||||
return left.CompareTo(right) < 0;
|
||||
}
|
||||
|
||||
public static bool operator <=(Utf8Span left, Utf8Span right)
|
||||
{
|
||||
return left.CompareTo(right) <= 0;
|
||||
}
|
||||
|
||||
public static bool operator >(Utf8Span left, Utf8Span right)
|
||||
{
|
||||
return left.CompareTo(right) > 0;
|
||||
}
|
||||
|
||||
public static bool operator >=(Utf8Span left, Utf8Span right)
|
||||
{
|
||||
return left.CompareTo(right) >= 0;
|
||||
}
|
||||
|
||||
public static bool operator <(Utf8Span left, string right)
|
||||
{
|
||||
return left.CompareTo(right) < 0;
|
||||
}
|
||||
|
||||
public static bool operator <=(Utf8Span left, string right)
|
||||
{
|
||||
return left.CompareTo(right) <= 0;
|
||||
}
|
||||
|
||||
public static bool operator >(Utf8Span left, string right)
|
||||
{
|
||||
return left.CompareTo(right) > 0;
|
||||
}
|
||||
|
||||
public static bool operator >=(Utf8Span left, string right)
|
||||
{
|
||||
return left.CompareTo(right) >= 0;
|
||||
}
|
||||
|
||||
public static bool operator <(string left, Utf8Span right)
|
||||
{
|
||||
return right.CompareTo(left) >= 0;
|
||||
}
|
||||
|
||||
public static bool operator <=(string left, Utf8Span right)
|
||||
{
|
||||
return right.CompareTo(left) > 0;
|
||||
}
|
||||
|
||||
public static bool operator >(string left, Utf8Span right)
|
||||
{
|
||||
return right.CompareTo(left) <= 0;
|
||||
}
|
||||
|
||||
public static bool operator >=(string left, Utf8Span right)
|
||||
{
|
||||
return right.CompareTo(left) < 0;
|
||||
}
|
||||
|
||||
public int CompareTo(Utf8Span other)
|
||||
{
|
||||
ReadOnlySpan<byte> left = this.Span;
|
||||
ReadOnlySpan<byte> right = other.Span;
|
||||
int minLength = left.Length;
|
||||
if (minLength > right.Length)
|
||||
{
|
||||
minLength = right.Length;
|
||||
}
|
||||
|
||||
for (int i = 0; i < minLength; i++)
|
||||
{
|
||||
int result = left[i].CompareTo(right[i]);
|
||||
if (result != 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return left.Length.CompareTo(right.Length);
|
||||
}
|
||||
|
||||
public int CompareTo(string other)
|
||||
{
|
||||
Contract.Requires(other != null);
|
||||
|
||||
Utf8CodePointEnumerator thisEnumerator = this.GetEnumerator();
|
||||
Utf16LittleEndianCodePointEnumerator otherEnumerator = new Utf16LittleEndianCodePointEnumerator(other);
|
||||
|
||||
while (true)
|
||||
{
|
||||
bool thisHasNext = thisEnumerator.MoveNext();
|
||||
bool otherHasNext = otherEnumerator.MoveNext();
|
||||
if (!thisHasNext && !otherHasNext)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!thisHasNext)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!otherHasNext)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint thisCurrent = thisEnumerator.Current;
|
||||
uint otherCurrent = otherEnumerator.Current;
|
||||
|
||||
if (thisCurrent == otherCurrent)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
return thisCurrent.CompareTo(otherCurrent);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if this <see cref="Utf8Span" /> starts with (or equals) the second.
|
||||
/// </summary>
|
||||
/// <param name="pattern">The <see cref="Utf8Span" /> to compare.</param>
|
||||
/// <returns>If starts with.</returns>
|
||||
public bool StartsWith(Utf8Span pattern)
|
||||
{
|
||||
return this.Span.StartsWith(pattern.Span);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if this <see cref="Utf8Span" /> ends with (or equals) the second.
|
||||
/// </summary>
|
||||
/// <param name="pattern">The <see cref="Utf8Span" /> to compare.</param>
|
||||
/// <returns>If starts with.</returns>
|
||||
public bool EndsWith(Utf8Span pattern)
|
||||
{
|
||||
return this.Span.EndsWith(pattern.Span);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if a specified <see cref="Utf8Span" /> occurs within this <see cref="Utf8Span" />.
|
||||
/// </summary>
|
||||
/// <param name="pattern">The <see cref="Utf8Span" /> to compare.</param>
|
||||
/// <returns>If contains.</returns>
|
||||
public bool Contains(Utf8Span pattern)
|
||||
{
|
||||
return this.Span.IndexOf(pattern.Span) >= 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Splits a <see cref="Utf8Span" /> around first occurrence of <paramref name="pattern"/> into the left and right segments.
|
||||
/// The pattern is not included in either left or right results.
|
||||
/// </summary>
|
||||
/// <param name="pattern">The <see cref="Utf8Span" /> split around.</param>
|
||||
/// <param name="left">The <see cref="Utf8Span" /> before the pattern.</param>
|
||||
/// <param name="right">The <see cref="Utf8Span" /> after the pattern.</param>
|
||||
/// <returns>True if success, false if does not contain pattern.</returns>
|
||||
public bool TrySplitFirst(Utf8Span pattern, out Utf8Span left, out Utf8Span right)
|
||||
{
|
||||
int indexOfPattern = this.Span.IndexOf(pattern.Span);
|
||||
|
||||
if (indexOfPattern < 0)
|
||||
{
|
||||
left = default;
|
||||
right = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
left = new Utf8Span(this.Span.Slice(0, indexOfPattern));
|
||||
right = new Utf8Span(this.Span.Slice(indexOfPattern + pattern.Length));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
382
src/Core/Core/Utf8/Utf8String.cs
Normal file
382
src/Core/Core/Utf8/Utf8String.cs
Normal file
@@ -0,0 +1,382 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma warning disable CA1066 // Type {0} should implement IEquatable<T> because it overrides Equals
|
||||
#pragma warning disable CA2225 // Operator overloads have named alternates
|
||||
#pragma warning disable IDE0041 // Use 'is null' check
|
||||
|
||||
namespace Microsoft.Azure.Cosmos.Core.Utf8
|
||||
{
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
|
||||
// ReSharper disable once UseNameofExpression
|
||||
[DebuggerDisplay("{ToString()}")]
|
||||
public class Utf8String : IEquatable<Utf8String>, IComparable<Utf8String>, IEquatable<string>, IComparable<string>
|
||||
{
|
||||
private readonly ReadOnlyMemory<byte> buffer;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private Utf8String(ReadOnlyMemory<byte> utf8Bytes)
|
||||
{
|
||||
this.buffer = utf8Bytes;
|
||||
}
|
||||
|
||||
public static readonly Utf8String Empty = new Utf8String(default);
|
||||
|
||||
/// <summary>The UTF8 byte sequence.</summary>
|
||||
public Utf8Span Span
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => Utf8Span.UnsafeFromUtf8BytesNoValidation(this.buffer.Span);
|
||||
}
|
||||
|
||||
/// <summary>The length in bytes of the UTF8 encoding.</summary>
|
||||
public int Length => this.buffer.Length;
|
||||
|
||||
/// <summary>True if the length is empty.</summary>
|
||||
public bool IsEmpty => this.buffer.Length == 0;
|
||||
|
||||
/// <summary>Parses the sequence of bytes to prove it is valid UTF8.</summary>
|
||||
/// <param name="utf8Bytes">The bytes to validate.</param>
|
||||
/// <param name="str">
|
||||
/// If the sequence validates a <see cref="Utf8String" /> that wraps the bytes in
|
||||
/// <paramref name="utf8Bytes" />, otherwise <see cref="t:default" />.
|
||||
/// </param>
|
||||
/// <remarks>The new <see cref="Utf8String"/> takes ownership of <paramref name="utf8Bytes"/>.</remarks>
|
||||
/// <returns>True if the sequence validates, false otherwise.</returns>
|
||||
public static bool TryParseUtf8Bytes(ReadOnlyMemory<byte> utf8Bytes, out Utf8String str)
|
||||
{
|
||||
int invalidIndex = Utf8Util.GetIndexOfFirstInvalidUtf8Sequence(utf8Bytes.Span, out int _, out int _);
|
||||
if (invalidIndex != -1)
|
||||
{
|
||||
str = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
str = new Utf8String(utf8Bytes);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>Creates a <see cref="Utf8String" /> without validating the underlying bytes.</summary>
|
||||
/// <param name="utf8Bytes">The bytes claiming to be UTF8.</param>
|
||||
/// <returns>A <see cref="Utf8String" /> wrapping <paramref name="utf8Bytes" />.</returns>
|
||||
/// <remarks>
|
||||
/// This method is dangerous as consumers of the <see cref="Utf8String" /> must assume the
|
||||
/// underlying bytes are indeed valid UTF8. The method should <bold>only</bold> be used when the UTF8
|
||||
/// sequence has already been externally valid or is known to be valid by construction.
|
||||
/// </remarks>
|
||||
/// <remarks>The new <see cref="Utf8String"/> takes ownership of <paramref name="utf8Bytes"/>.</remarks>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Utf8String UnsafeFromUtf8BytesNoValidation(ReadOnlyMemory<byte> utf8Bytes)
|
||||
{
|
||||
Contract.Assert(Utf8Util.GetIndexOfFirstInvalidUtf8Sequence(utf8Bytes.Span, out int _, out int _) == -1);
|
||||
return new Utf8String(utf8Bytes);
|
||||
}
|
||||
|
||||
/// <summary>Creates a <see cref="Utf8String" /> from a <see cref="Utf8Span"/>.</summary>
|
||||
/// <param name="span">The bytes that are UTF8.</param>
|
||||
/// <returns>A <see cref="Utf8String" /> with contents from <paramref name="span" />.</returns>
|
||||
public static Utf8String CopyFrom(Utf8Span span)
|
||||
{
|
||||
Contract.Assert(Utf8Util.GetIndexOfFirstInvalidUtf8Sequence(span.Span, out int _, out int _) == -1);
|
||||
return new Utf8String(span.Span.ToArray());
|
||||
}
|
||||
|
||||
/// <summary>Creates a <see cref="Utf8String" /> from a UTF16 encoding string.</summary>
|
||||
/// <param name="utf16String">The UTF16 encoding string.</param>
|
||||
/// <returns>A new <see cref="Utf8String" />.</returns>
|
||||
/// <remarks>
|
||||
/// This method must transcode the UTF16 into UTF8 which both requires allocation and is a
|
||||
/// size of data operation.
|
||||
/// </remarks>
|
||||
public static Utf8String TranscodeUtf16(string utf16String)
|
||||
{
|
||||
if (object.ReferenceEquals(utf16String, null))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(utf16String))
|
||||
{
|
||||
return new Utf8String(ReadOnlyMemory<byte>.Empty);
|
||||
}
|
||||
|
||||
return new Utf8String(Encoding.UTF8.GetBytes(utf16String));
|
||||
}
|
||||
|
||||
/// <summary><see cref="Utf8Span" /> over the string's content.</summary>
|
||||
/// <param name="utf8String">The string whose content is returned.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Utf8Span(Utf8String utf8String)
|
||||
{
|
||||
return (utf8String == null) ? default : utf8String.Span;
|
||||
}
|
||||
|
||||
public static implicit operator UtfAnyString(Utf8String utf8String)
|
||||
{
|
||||
return new UtfAnyString(utf8String);
|
||||
}
|
||||
|
||||
public static bool operator ==(Utf8String left, Utf8String right)
|
||||
{
|
||||
if (object.ReferenceEquals(null, left))
|
||||
{
|
||||
return object.ReferenceEquals(null, right);
|
||||
}
|
||||
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator !=(Utf8String left, Utf8String right)
|
||||
{
|
||||
if (object.ReferenceEquals(null, left))
|
||||
{
|
||||
return !object.ReferenceEquals(null, right);
|
||||
}
|
||||
|
||||
return !left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator <(Utf8String left, Utf8String right)
|
||||
{
|
||||
return object.ReferenceEquals(left, null) ? !object.ReferenceEquals(right, null) : left.CompareTo(right) < 0;
|
||||
}
|
||||
|
||||
public static bool operator <=(Utf8String left, Utf8String right)
|
||||
{
|
||||
return object.ReferenceEquals(left, null) || left.CompareTo(right) <= 0;
|
||||
}
|
||||
|
||||
public static bool operator >(Utf8String left, Utf8String right)
|
||||
{
|
||||
return !object.ReferenceEquals(left, null) && left.CompareTo(right) > 0;
|
||||
}
|
||||
|
||||
public static bool operator >=(Utf8String left, Utf8String right)
|
||||
{
|
||||
return object.ReferenceEquals(left, null) ? object.ReferenceEquals(right, null) : left.CompareTo(right) >= 0;
|
||||
}
|
||||
|
||||
public static bool operator <(Utf8String left, string right)
|
||||
{
|
||||
return object.ReferenceEquals(left, null) ? !object.ReferenceEquals(right, null) : left.CompareTo(right) < 0;
|
||||
}
|
||||
|
||||
public static bool operator <=(Utf8String left, string right)
|
||||
{
|
||||
return object.ReferenceEquals(left, null) || left.CompareTo(right) <= 0;
|
||||
}
|
||||
|
||||
public static bool operator >(Utf8String left, string right)
|
||||
{
|
||||
return !object.ReferenceEquals(left, null) && left.CompareTo(right) > 0;
|
||||
}
|
||||
|
||||
public static bool operator >=(Utf8String left, string right)
|
||||
{
|
||||
return object.ReferenceEquals(left, null) ? object.ReferenceEquals(right, null) : left.CompareTo(right) >= 0;
|
||||
}
|
||||
|
||||
public static bool operator <(string left, Utf8String right)
|
||||
{
|
||||
return object.ReferenceEquals(right, null) ? object.ReferenceEquals(left, null) : right.CompareTo(left) >= 0;
|
||||
}
|
||||
|
||||
public static bool operator <=(string left, Utf8String right)
|
||||
{
|
||||
return !object.ReferenceEquals(right, null) && right.CompareTo(left) > 0;
|
||||
}
|
||||
|
||||
public static bool operator >(string left, Utf8String right)
|
||||
{
|
||||
return object.ReferenceEquals(right, null) || right.CompareTo(left) <= 0;
|
||||
}
|
||||
|
||||
public static bool operator >=(string left, Utf8String right)
|
||||
{
|
||||
return object.ReferenceEquals(right, null) ? !object.ReferenceEquals(left, null) : right.CompareTo(left) < 0;
|
||||
}
|
||||
|
||||
/// <summary>Non-allocating enumeration of each code point in the UTF8 stream.</summary>
|
||||
public Utf8CodePointEnumerator GetEnumerator()
|
||||
{
|
||||
return this.Span.GetEnumerator();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return this.Span.ToString();
|
||||
}
|
||||
|
||||
public bool Equals(Utf8Span other)
|
||||
{
|
||||
return this.Span.Equals(other);
|
||||
}
|
||||
|
||||
public bool Equals(Utf8String other)
|
||||
{
|
||||
if (object.ReferenceEquals(null, other))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (object.ReferenceEquals(this, other))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return this.Span.Equals(other.Span);
|
||||
}
|
||||
|
||||
public bool Equals(string other)
|
||||
{
|
||||
if (object.ReferenceEquals(null, other))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.Span.Equals(other);
|
||||
}
|
||||
|
||||
public override bool Equals(object other)
|
||||
{
|
||||
if (object.ReferenceEquals(null, other))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (object.ReferenceEquals(this, other))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return this.Span.Equals(other);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return this.Span.GetHashCode();
|
||||
}
|
||||
|
||||
public int CompareTo(Utf8String other)
|
||||
{
|
||||
if (other == null)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
return this.Span.CompareTo(other.Span);
|
||||
}
|
||||
|
||||
public int CompareTo(string other)
|
||||
{
|
||||
if (other == null)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
return this.Span.CompareTo(other);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Splits a <see cref="Utf8String" /> around first occurrence of <paramref name="pattern"/> into the left and right segments.
|
||||
/// The pattern is not included in either left or right results.
|
||||
/// </summary>
|
||||
/// <param name="pattern">The <see cref="Utf8Span" /> split around.</param>
|
||||
/// <param name="left">The <see cref="Utf8String" /> before the pattern.</param>
|
||||
/// <param name="right">The <see cref="Utf8String" /> after the pattern.</param>
|
||||
/// <returns>True if success, false if does not contain pattern.</returns>
|
||||
public bool TrySplitFirst(Utf8Span pattern, out Utf8String left, out Utf8String right)
|
||||
{
|
||||
int indexOfPattern = this.buffer.Span.IndexOf(pattern.Span);
|
||||
|
||||
if (indexOfPattern < 0)
|
||||
{
|
||||
left = default;
|
||||
right = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
left = new Utf8String(this.buffer.Slice(0, indexOfPattern));
|
||||
right = new Utf8String(this.buffer.Slice(indexOfPattern + pattern.Length));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes given <see cref="Utf8Span" /> from start and outputs resultant <see cref="Utf8String" />.
|
||||
/// </summary>
|
||||
/// <param name="pattern">The <see cref="Utf8Span" /> to remove.</param>
|
||||
/// <param name="output">The <see cref="Utf8String" /> with value removed from start.</param>
|
||||
/// <returns>Is success.</returns>
|
||||
public bool TryTrimLeft(Utf8Span pattern, out Utf8String output)
|
||||
{
|
||||
if (!this.Span.StartsWith(pattern))
|
||||
{
|
||||
output = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
output = new Utf8String(this.buffer.Slice(pattern.Length));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes given <see cref="Utf8Span" /> from end and outputs resultant <see cref="Utf8String" />.
|
||||
/// </summary>
|
||||
/// <param name="pattern">The <see cref="Utf8Span" /> to remove.</param>
|
||||
/// <param name="output">The <see cref="Utf8String" /> with value removed from end.</param>
|
||||
/// <returns>Is success.</returns>
|
||||
public bool TryTrimRight(Utf8Span pattern, out Utf8String output)
|
||||
{
|
||||
if (!this.Span.EndsWith(pattern))
|
||||
{
|
||||
output = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
output = new Utf8String(this.buffer.Slice(0, this.buffer.Length - pattern.Length));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes given <paramref name="leftPattern"/> from the start and <paramref name="rightPattern"/>
|
||||
/// from the end and outputs resultant <see cref="Utf8String" />.
|
||||
/// </summary>
|
||||
/// <param name="leftPattern">The <see cref="Utf8Span" /> to remove from left.</param>
|
||||
/// <param name="rightPattern">The <see cref="Utf8Span" /> to remove from right.</param>
|
||||
/// <param name="output">The <see cref="Utf8String" /> with value removed from end.</param>
|
||||
/// <remarks>Will return false if patterns overlap in string to trim.</remarks>
|
||||
/// <returns>Is success.</returns>
|
||||
public bool TryTrim(Utf8Span leftPattern, Utf8Span rightPattern, out Utf8String output)
|
||||
{
|
||||
if (!this.Span.StartsWith(leftPattern) ||
|
||||
!this.Span.EndsWith(rightPattern) ||
|
||||
(this.buffer.Length < leftPattern.Length + rightPattern.Length))
|
||||
{
|
||||
output = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
output = new Utf8String(this.buffer.Slice(leftPattern.Length, this.buffer.Length - leftPattern.Length - rightPattern.Length));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the specified <see cref="Utf8String"/> is null or an empty.
|
||||
/// </summary>
|
||||
/// <param name="utf8String"></param>
|
||||
/// <returns>If null or empty.</returns>
|
||||
public static bool IsNullOrEmpty(Utf8String utf8String)
|
||||
{
|
||||
return utf8String == null || utf8String.IsEmpty;
|
||||
}
|
||||
}
|
||||
}
|
||||
419
src/Core/Core/Utf8/Utf8Util.Helpers.cs
Normal file
419
src/Core/Core/Utf8/Utf8Util.Helpers.cs
Normal file
@@ -0,0 +1,419 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma warning disable SA1601 // Partial elements should be documented
|
||||
#pragma warning disable SA1119 // Statement should not use unnecessary parenthesis
|
||||
|
||||
//
|
||||
// This file contains utility methods shared by the UTF-8 workhorse methods.
|
||||
namespace Microsoft.Azure.Cosmos.Core.Utf8
|
||||
{
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
internal static partial class Utf8Util
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns <see langword="true" /> iff <paramref name="value" /> is between
|
||||
/// <paramref name="lowerBound" /> and <paramref name="upperBound" />, inclusive.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal static bool IsInRangeInclusive(uint value, uint lowerBound, uint upperBound)
|
||||
{
|
||||
return unchecked((value - lowerBound) <= (upperBound - lowerBound));
|
||||
}
|
||||
|
||||
/// <summary>Casts an <see cref="IntPtr" /> to an <see cref="int" /> without overflow checking.</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static int ConvertIntPtrToInt32WithoutOverflowCheck(IntPtr value)
|
||||
{
|
||||
if (IntPtr.Size == 4)
|
||||
{
|
||||
return (int)value;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (int)(long)value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given a 24-bit integer which represents a three-byte buffer read in machine endianness,
|
||||
/// counts the number of consecutive ASCII bytes starting from the beginning of the buffer. Returns a
|
||||
/// value 0 - 3, inclusive.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static uint CountNumberOfLeadingAsciiBytesFrom24BitInteger(uint value)
|
||||
{
|
||||
// The 'allBytesUpToNowAreAscii' DWORD uses bit twiddling to hold a 1 or a 0 depending
|
||||
// on whether all processed bytes were ASCII. Then we accumulate all of the
|
||||
// results to calculate how many consecutive ASCII bytes are present.
|
||||
value = ~value;
|
||||
|
||||
if (BitConverter.IsLittleEndian)
|
||||
{
|
||||
// Read first byte
|
||||
uint allBytesUpToNowAreAscii = (value >>= 7) & 1;
|
||||
uint numAsciiBytes = allBytesUpToNowAreAscii;
|
||||
|
||||
// Read second byte
|
||||
allBytesUpToNowAreAscii &= (value >>= 8);
|
||||
numAsciiBytes += allBytesUpToNowAreAscii;
|
||||
|
||||
// Read third byte
|
||||
allBytesUpToNowAreAscii &= (value >>= 8);
|
||||
numAsciiBytes += allBytesUpToNowAreAscii;
|
||||
|
||||
return numAsciiBytes;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Read first byte
|
||||
uint allBytesUpToNowAreAscii = (value = Utf8Util.ROL32(value, 1)) & 1;
|
||||
uint numAsciiBytes = allBytesUpToNowAreAscii;
|
||||
|
||||
// Read second byte
|
||||
allBytesUpToNowAreAscii &= (value = Utf8Util.ROL32(value, 8));
|
||||
numAsciiBytes += allBytesUpToNowAreAscii;
|
||||
|
||||
// Read third byte
|
||||
allBytesUpToNowAreAscii &= (_ = Utf8Util.ROL32(value, 8));
|
||||
numAsciiBytes += allBytesUpToNowAreAscii;
|
||||
|
||||
return numAsciiBytes;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Returns <see langword="true" /> iff all bytes in <paramref name="value" /> are ASCII.</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool DWordAllBytesAreAscii(uint value)
|
||||
{
|
||||
return ((value & 0x80808080U) == 0U);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given a UTF-8 buffer which has been read into a DWORD in machine endianness, returns
|
||||
/// <see langword="true" /> iff the buffer contains two UTF-8 sequences that match the mask [ 110yyyyy
|
||||
/// 10xxxxxx 110yyyyy 10xxxxxx ]. This method *does not* validate that the sequences are well-formed;
|
||||
/// the caller must still perform overlong form checking.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool DWordBeginsAndEndsWithUtf8TwoByteMask(uint value)
|
||||
{
|
||||
// The code in this method is equivalent to the code
|
||||
// below, but the JIT is able to inline + optimize it
|
||||
// better in release builds.
|
||||
//
|
||||
// if (BitConverter.IsLittleEndian)
|
||||
// {
|
||||
// const uint mask = 0xC0E0C0E0U;
|
||||
// const uint comparand = 0x80C080C0U;
|
||||
// return ((value & mask) == comparand);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// const uint mask = 0xE0C0E0C0U;
|
||||
// const uint comparand = 0xC080C080U;
|
||||
// return ((value & mask) == comparand);
|
||||
// }
|
||||
return (BitConverter.IsLittleEndian && ((value & 0xC0E0C0E0U) == 0x80C080C0U)) ||
|
||||
(!BitConverter.IsLittleEndian && ((value & 0xE0C0E0C0U) == 0xC080C080U));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given a UTF-8 buffer which has been read into a DWORD in machine endianness, returns
|
||||
/// <see langword="true" /> iff the first two bytes of the buffer are an overlong representation of a
|
||||
/// sequence that should be represented as one byte. This method *does not* validate that the sequence
|
||||
/// matches the appropriate 2-byte sequence mask (see <see cref="DWordBeginsWithUtf8TwoByteMask" />).
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool DWordBeginsWithOverlongUtf8TwoByteSequence(uint value)
|
||||
{
|
||||
// ASSUMPTION: Caller has already checked the '110yyyyy 10xxxxxx' mask of the input.
|
||||
Contract.Assert(Utf8Util.DWordBeginsWithUtf8TwoByteMask(value));
|
||||
|
||||
// Per Table 3-7, first byte of two-byte sequence must be within range C2 .. DF.
|
||||
// Since we already validated it's 80 <= ?? <= DF (per mask check earlier), now only need
|
||||
// to check that it's < C2.
|
||||
return (BitConverter.IsLittleEndian && (unchecked((byte)value) < (byte)0xC2)) || (!BitConverter.IsLittleEndian && (value < 0xC2000000U));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given a UTF-8 buffer which has been read into a DWORD in machine endianness, returns
|
||||
/// <see langword="true" /> iff the first four bytes of the buffer match the UTF-8 4-byte sequence mask
|
||||
/// [ 11110www 10zzzzzz 10yyyyyy 10xxxxxx ]. This method *does not* validate that the sequence is
|
||||
/// well-formed; the caller must still perform overlong form or out-of-range checking.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool DWordBeginsWithUtf8FourByteMask(uint value)
|
||||
{
|
||||
// The code in this method is equivalent to the code
|
||||
// below, but the JIT is able to inline + optimize it
|
||||
// better in release builds.
|
||||
//
|
||||
// if (BitConverter.IsLittleEndian)
|
||||
// {
|
||||
// const uint mask = 0xC0C0C0F8U;
|
||||
// const uint comparand = 0x808080F0U;
|
||||
// return ((value & mask) == comparand);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// const uint mask = 0xF8C0C0C0U;
|
||||
// const uint comparand = 0xF0808000U;
|
||||
// return ((value & mask) == comparand);
|
||||
// }
|
||||
return (BitConverter.IsLittleEndian && ((value & 0xC0C0C0F8U) == 0x808080F0U)) ||
|
||||
(!BitConverter.IsLittleEndian && ((value & 0xF8C0C0C0U) == 0xF0808000U));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given a UTF-8 buffer which has been read into a DWORD in machine endianness, returns
|
||||
/// <see langword="true" /> iff the first three bytes of the buffer match the UTF-8 3-byte sequence
|
||||
/// mask [ 1110zzzz 10yyyyyy 10xxxxxx ]. This method *does not* validate that the sequence is
|
||||
/// well-formed; the caller must still perform overlong form or surrogate checking.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool DWordBeginsWithUtf8ThreeByteMask(uint value)
|
||||
{
|
||||
// The code in this method is equivalent to the code
|
||||
// below, but the JIT is able to inline + optimize it
|
||||
// better in release builds.
|
||||
//
|
||||
// if (BitConverter.IsLittleEndian)
|
||||
// {
|
||||
// const uint mask = 0x00C0C0F0U;
|
||||
// const uint comparand = 0x008080E0U;
|
||||
// return ((value & mask) == comparand);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// const uint mask = 0xF0C0C000U;
|
||||
// const uint comparand = 0xE0808000U;
|
||||
// return ((value & mask) == comparand);
|
||||
// }
|
||||
return (BitConverter.IsLittleEndian && ((value & 0x00C0C0F0U) == 0x008080E0U)) ||
|
||||
(!BitConverter.IsLittleEndian && ((value & 0xF0C0C000U) == 0xE0808000U));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given a UTF-8 buffer which has been read into a DWORD in machine endianness, returns
|
||||
/// <see langword="true" /> iff the first two bytes of the buffer match the UTF-8 2-byte sequence mask
|
||||
/// [ 110yyyyy 10xxxxxx ]. This method *does not* validate that the sequence is well-formed; the caller
|
||||
/// must still perform overlong form checking.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool DWordBeginsWithUtf8TwoByteMask(uint value)
|
||||
{
|
||||
// The code in this method is equivalent to the code
|
||||
// below, but the JIT is able to inline + optimize it
|
||||
// better in release builds.
|
||||
//
|
||||
// if (BitConverter.IsLittleEndian)
|
||||
// {
|
||||
// const uint mask = 0x0000C0E0U;
|
||||
// const uint comparand = 0x000080C0U;
|
||||
// return ((value & mask) == comparand);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// const uint mask = 0xE0C00000U;
|
||||
// const uint comparand = 0xC0800000U;
|
||||
// return ((value & mask) == comparand);
|
||||
// }
|
||||
return (BitConverter.IsLittleEndian && ((value & 0x0000C0E0U) == 0x000080C0U)) ||
|
||||
(!BitConverter.IsLittleEndian && ((value & 0xE0C00000U) == 0xC0800000U));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given a UTF-8 buffer which has been read into a DWORD in machine endianness, returns
|
||||
/// <see langword="true" /> iff the first two bytes of the buffer are an overlong representation of a
|
||||
/// sequence that should be represented as one byte. This method *does not* validate that the sequence
|
||||
/// matches the appropriate 2-byte sequence mask (see <see cref="DWordBeginsWithUtf8TwoByteMask" />).
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool DWordEndsWithOverlongUtf8TwoByteSequence(uint value)
|
||||
{
|
||||
// ASSUMPTION: Caller has already checked the '110yyyyy 10xxxxxx' mask of the input.
|
||||
Contract.Assert(Utf8Util.DWordEndsWithUtf8TwoByteMask(value));
|
||||
|
||||
// Per Table 3-7, first byte of two-byte sequence must be within range C2 .. DF.
|
||||
// We already validated that it's 80 .. DF (per mask check earlier).
|
||||
// C2 = 1100 0010
|
||||
// DF = 1101 1111
|
||||
// This means that we can AND the leading byte with the mask 0001 1110 (1E),
|
||||
// and if the result is zero the sequence is overlong.
|
||||
return (BitConverter.IsLittleEndian && ((value & 0x001E0000U) == 0U)) || (!BitConverter.IsLittleEndian && ((value & 0x1E00U) == 0U));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given a UTF-8 buffer which has been read into a DWORD in machine endianness, returns
|
||||
/// <see langword="true" /> iff the last two bytes of the buffer match the UTF-8 2-byte sequence mask [
|
||||
/// 110yyyyy 10xxxxxx ]. This method *does not* validate that the sequence is well-formed; the caller
|
||||
/// must still perform overlong form checking.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool DWordEndsWithUtf8TwoByteMask(uint value)
|
||||
{
|
||||
// The code in this method is equivalent to the code
|
||||
// below, but the JIT is able to inline + optimize it
|
||||
// better in release builds.
|
||||
//
|
||||
// if (BitConverter.IsLittleEndian)
|
||||
// {
|
||||
// const uint mask = 0xC0E00000U;
|
||||
// const uint comparand = 0x80C00000U;
|
||||
// return ((value & mask) == comparand);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// const uint mask = 0x0000E0C0U;
|
||||
// const uint comparand = 0x0000C080U;
|
||||
// return ((value & mask) == comparand);
|
||||
// }
|
||||
return (BitConverter.IsLittleEndian && ((value & 0xC0E00000U) == 0x80C00000U)) ||
|
||||
(!BitConverter.IsLittleEndian && ((value & 0x0000E0C0U) == 0x0000C080U));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given a UTF-8 buffer which has been read into a DWORD on a little-endian machine, returns
|
||||
/// <see langword="true" /> iff the first two bytes of the buffer are a well-formed UTF-8 two-byte
|
||||
/// sequence. This wraps the mask check and the overlong check into a single operation. Returns
|
||||
/// <see langword="false" /> if running on a big-endian machine.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool DWordBeginsWithValidUtf8TwoByteSequenceLittleEndian(uint value)
|
||||
{
|
||||
// Per Table 3-7, valid 2-byte sequences are [ C2..DF ] [ 80..BF ].
|
||||
// In little-endian, that would be represented as:
|
||||
// [ ######## ######## 10xxxxxx 110yyyyy ].
|
||||
// Due to the little-endian representation we can perform a trick by ANDing the low
|
||||
// WORD with the bitmask [ 11000000 11111111 ] and checking that the value is within
|
||||
// the range [ 11000000_11000010, 11000000_11011111 ]. This performs both the
|
||||
// 2-byte-sequence bitmask check and overlong form validation with one comparison.
|
||||
Contract.Assert(BitConverter.IsLittleEndian);
|
||||
|
||||
return (BitConverter.IsLittleEndian && Utf8Util.IsInRangeInclusive(value & 0xC0FFU, 0x80C2U, 0x80DFU)) ||
|
||||
(!BitConverter.IsLittleEndian && false); // this line - while weird - helps JIT produce optimal code
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given a UTF-8 buffer which has been read into a DWORD on a little-endian machine, returns
|
||||
/// <see langword="true" /> iff the last two bytes of the buffer are a well-formed UTF-8 two-byte
|
||||
/// sequence. This wraps the mask check and the overlong check into a single operation. Returns
|
||||
/// <see langword="false" /> if running on a big-endian machine.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool DWordEndsWithValidUtf8TwoByteSequenceLittleEndian(uint value)
|
||||
{
|
||||
// See comments in DWordBeginsWithValidUtf8TwoByteSequenceLittleEndian.
|
||||
Contract.Assert(BitConverter.IsLittleEndian);
|
||||
|
||||
return (BitConverter.IsLittleEndian && Utf8Util.IsInRangeInclusive(value & 0xC0FF0000U, 0x80C20000U, 0x80DF0000U)) ||
|
||||
(!BitConverter.IsLittleEndian && false); // this line - while weird - helps JIT produce optimal code
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given a UTF-8 buffer which has been read into a DWORD in machine endianness, returns
|
||||
/// <see langword="true" /> iff the fourth byte of the buffer is ASCII.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool DWordFourthByteIsAscii(uint value)
|
||||
{
|
||||
// The code in this method is equivalent to the code
|
||||
// below, but the JIT is able to inline + optimize it
|
||||
// better in release builds.
|
||||
//
|
||||
// if (BitConverter.IsLittleEndian)
|
||||
// {
|
||||
// return ((int)value >= 0);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// return ((value & 0x80U) == 0U);
|
||||
// }
|
||||
return (BitConverter.IsLittleEndian && unchecked((int)value >= 0)) || (!BitConverter.IsLittleEndian && ((value & 0x80U) == 0U));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given a UTF-8 buffer which has been read into a DWORD in machine endianness, returns
|
||||
/// <see langword="true" /> iff the third byte of the buffer is ASCII.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool DWordThirdByteIsAscii(uint value)
|
||||
{
|
||||
// The code in this method is equivalent to the code
|
||||
// below, but the JIT is able to inline + optimize it
|
||||
// better in release builds.
|
||||
//
|
||||
// if (BitConverter.IsLittleEndian)
|
||||
// {
|
||||
// return ((value & 0x800000U) == 0U);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// return ((value & 0x8000U) == 0U);
|
||||
// }
|
||||
return (BitConverter.IsLittleEndian && ((value & 0x800000U) == 0U)) || (!BitConverter.IsLittleEndian && ((value & 0x8000U) == 0U));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given a memory reference, returns the number of bytes that must be added to the reference
|
||||
/// before the reference is DWORD-aligned. Returns a number in the range 0 - 3, inclusive.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static unsafe int GetNumberOfBytesToNextDWordAlignment(ref byte @ref)
|
||||
{
|
||||
return unchecked((int)((uint)sizeof(uint) - ((uint)Unsafe.AsPointer(ref @ref) % sizeof(uint))));
|
||||
}
|
||||
|
||||
/// <summary>Returns <see langword="true" /> iff (<paramref name="a" /> <= <paramref name="b" />).</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static unsafe bool IntPtrIsLessThanOrEqualTo(IntPtr a, IntPtr b)
|
||||
{
|
||||
return (a.ToPointer() <= b.ToPointer());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true" /> iff the low byte of <paramref name="value" /> is a UTF-8
|
||||
/// continuation byte (10xxxxxx).
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool IsUtf8ContinuationByte(uint value)
|
||||
{
|
||||
return ((value & 0xC0U) == 0x80U);
|
||||
}
|
||||
|
||||
/// <summary>Returns the OR of the next two DWORDs in the buffer.</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static uint ReadAndFoldTwoDWordsUnaligned(ref byte buffer)
|
||||
{
|
||||
return Unsafe.ReadUnaligned<uint>(ref buffer) | Unsafe.ReadUnaligned<uint>(ref Unsafe.Add(ref buffer, sizeof(uint)));
|
||||
}
|
||||
|
||||
/// <summary>Returns the OR of the next two QWORDs in the buffer.</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static ulong ReadAndFoldTwoQWordsUnaligned(ref byte buffer)
|
||||
{
|
||||
return Unsafe.ReadUnaligned<ulong>(ref buffer) | Unsafe.ReadUnaligned<ulong>(ref Unsafe.Add(ref buffer, sizeof(ulong)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rotates a DWORD left. The JIT is smart enough to turn this into a ROL / ROR
|
||||
/// instruction.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static uint ROL32(uint value, int shift)
|
||||
{
|
||||
return (value << shift) | (value >> (32 - shift));
|
||||
}
|
||||
|
||||
/// <summary>Returns <see langword="true" /> iff all bytes in <paramref name="value" /> are ASCII.</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool QWordAllBytesAreAscii(ulong value)
|
||||
{
|
||||
return ((value & 0x8080808080808080UL) == 0UL);
|
||||
}
|
||||
}
|
||||
}
|
||||
752
src/Core/Core/Utf8/Utf8Util.Validation.cs
Normal file
752
src/Core/Core/Utf8/Utf8Util.Validation.cs
Normal file
@@ -0,0 +1,752 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma warning disable SA1512 // Single-line comments should not be followed by blank line
|
||||
#pragma warning disable SA1601 // Partial elements should be documented
|
||||
|
||||
//
|
||||
// This file contains workhorse methods for performing validation of UTF-8 byte sequences.
|
||||
namespace Microsoft.Azure.Cosmos.Core.Utf8
|
||||
{
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
internal static partial class Utf8Util
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the offset in <paramref name="input" /> of where the first invalid UTF-8 sequence
|
||||
/// appears, or -1 if the input is valid UTF-8 text. (Empty inputs are considered valid.) On method
|
||||
/// return the <paramref name="scalarCount" /> parameter will contain the total number of Unicode
|
||||
/// scalar values seen up to (but not including) the first invalid sequence, and
|
||||
/// <paramref name="surrogatePairCount" /> will contain the number of surrogate pairs present if this
|
||||
/// text up to (but not including) the first invalid sequence were represented as UTF-16. To get the
|
||||
/// total UTF-16 code unit count, add <paramref name="surrogatePairCount" /> to
|
||||
/// <paramref name="scalarCount" />.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal static int GetIndexOfFirstInvalidUtf8Sequence(ReadOnlySpan<byte> input, out int scalarCount, out int surrogatePairCount)
|
||||
{
|
||||
return Utf8.Utf8Util.GetIndexOfFirstInvalidUtf8Sequence(
|
||||
ref MemoryMarshal.GetReference(input),
|
||||
input.Length,
|
||||
out scalarCount,
|
||||
out surrogatePairCount);
|
||||
}
|
||||
|
||||
// This method will consume as many ASCII bytes as it can using fast vectorized processing, returning the number of
|
||||
// consumed (ASCII) bytes. It's possible that the method exits early, perhaps because there is some non-ASCII byte
|
||||
// later in the sequence or because we're running out of input to search. The intent is that the caller *skips over*
|
||||
// the number of bytes returned by this method, then it continues data processing from the next byte.
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static unsafe IntPtr ConsumeAsciiBytesVectorized(ref byte buffer, int length)
|
||||
{
|
||||
// Only allow vectorization if vectors are hardware-accelerated and we have enough
|
||||
// data to allow a vectorized search.
|
||||
|
||||
if (!Vector.IsHardwareAccelerated || length < 3 * Vector<byte>.Count)
|
||||
{
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
|
||||
// JITter will generate VMOVUPD instructions, which performs better when the memory to read
|
||||
// is aligned. The GC may move the buffer around in memory, and while it will never cause
|
||||
// moved data to be misaligned with respect to the natural word size, no such guarantee is
|
||||
// made with respect to SIMD vector alignment. We'll pin the buffer so that we can enforce
|
||||
// alignment manually.
|
||||
|
||||
fixed (byte* pbBuffer = &buffer)
|
||||
{
|
||||
// [Potentially unaligned] single SIMD read and comparison, quick check for non-ASCII data.
|
||||
|
||||
Vector<byte> mask = new Vector<byte>((byte)0x80);
|
||||
if ((Unsafe.ReadUnaligned<Vector<byte>>(pbBuffer) & mask) != Vector<byte>.Zero)
|
||||
{
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
|
||||
// Round 'pbBuffer' up to the *next* aligned address. If 'pbBuffer' was already aligned, this
|
||||
// just bumps the address up to the next vector. The read above guaranteed that we read all
|
||||
// data between 'pbBuffer' and 'pbAlignedBuffer' and checked it for non-ASCII bytes. It's
|
||||
// possible we'll duplicate a little bit of work if 'pbBuffer' wasn't already aligned since
|
||||
// its tail end may overlap with the immediate upcoming aligned read, but it's faster just to
|
||||
// perform the extra work and not worry about checking for this condition.
|
||||
|
||||
// 'pbAlignedBuffer' will be somewhere between 1 and Vector<byte>.Count bytes ahead of 'pbBuffer',
|
||||
// hence the check for a length of >= 3 * Vector<byte>.Count at the beginning of this method.
|
||||
|
||||
byte* pbAlignedBuffer;
|
||||
if (IntPtr.Size >= 8)
|
||||
{
|
||||
pbAlignedBuffer = (byte*)(((long)pbBuffer + Vector<byte>.Count) & ~((long)Vector<byte>.Count - 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
pbAlignedBuffer = (byte*)(((int)pbBuffer + Vector<byte>.Count) & ~((int)Vector<byte>.Count - 1));
|
||||
}
|
||||
|
||||
// Now iterate and read two aligned SIMD vectors at a time. We can skip the first length check on the
|
||||
// first iteration of the loop since we already performed a length check at the very beginning of this
|
||||
// method.
|
||||
|
||||
byte* pbFinalPosAtWhichCanReadTwoVectors = &pbBuffer[length - (2 * Vector<byte>.Count)];
|
||||
Contract.Assert(pbAlignedBuffer <= pbFinalPosAtWhichCanReadTwoVectors);
|
||||
|
||||
do
|
||||
{
|
||||
if (((Unsafe.Read<Vector<byte>>(pbAlignedBuffer) | Unsafe.Read<Vector<byte>>(pbAlignedBuffer + Vector<byte>.Count)) & mask) !=
|
||||
Vector<byte>.Zero)
|
||||
{
|
||||
break; // non-ASCII data incoming
|
||||
}
|
||||
}
|
||||
while ((pbAlignedBuffer += 2 * Vector<byte>.Count) <= pbFinalPosAtWhichCanReadTwoVectors);
|
||||
|
||||
// We consumed all data up to 'pbAlignedBuffer' and know it to be non-ASCII.
|
||||
return (IntPtr)(pbAlignedBuffer - pbBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static int GetIndexOfFirstInvalidUtf8Sequence(ref byte inputBuffer, int inputLength, out int scalarCount, out int surrogatePairCount)
|
||||
{
|
||||
// The fields below control where we read from the buffer.
|
||||
|
||||
IntPtr inputBufferCurrentOffset = IntPtr.Zero;
|
||||
int tempScalarCount = inputLength;
|
||||
int tempSurrogatePairCount = 0;
|
||||
|
||||
// If the sequence is long enough, try running vectorized "is this sequence ASCII?"
|
||||
// logic. We perform a small test of the first few bytes to make sure they're all
|
||||
// ASCII before we incur the cost of invoking the vectorized code path.
|
||||
|
||||
if (Vector.IsHardwareAccelerated)
|
||||
{
|
||||
if (IntPtr.Size >= 8)
|
||||
{
|
||||
// Test first 16 bytes and check for all-ASCII.
|
||||
if ((inputLength >= (2 * sizeof(ulong)) + (3 * Vector<byte>.Count)) &&
|
||||
Utf8.Utf8Util.QWordAllBytesAreAscii(Utf8.Utf8Util.ReadAndFoldTwoQWordsUnaligned(ref inputBuffer)))
|
||||
{
|
||||
inputBufferCurrentOffset = Utf8.Utf8Util.ConsumeAsciiBytesVectorized(
|
||||
ref Unsafe.Add(ref inputBuffer, 2 * sizeof(ulong)),
|
||||
inputLength - (2 * sizeof(ulong))) +
|
||||
(2 * sizeof(ulong));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Test first 8 bytes and check for all-ASCII.
|
||||
if ((inputLength >= (2 * sizeof(uint)) + (3 * Vector<byte>.Count)) &&
|
||||
Utf8.Utf8Util.DWordAllBytesAreAscii(Utf8.Utf8Util.ReadAndFoldTwoDWordsUnaligned(ref inputBuffer)))
|
||||
{
|
||||
inputBufferCurrentOffset = Utf8.Utf8Util.ConsumeAsciiBytesVectorized(
|
||||
ref Unsafe.Add(ref inputBuffer, 2 * sizeof(uint)),
|
||||
inputLength - (2 * sizeof(uint))) +
|
||||
(2 * sizeof(uint));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int inputBufferRemainingBytes = inputLength - Utf8.Utf8Util.ConvertIntPtrToInt32WithoutOverflowCheck(inputBufferCurrentOffset);
|
||||
|
||||
// Begin the main loop.
|
||||
|
||||
#if DEBUG
|
||||
long lastOffsetProcessed = -1; // used for invariant checking in debug builds
|
||||
#endif
|
||||
|
||||
while (inputBufferRemainingBytes >= sizeof(uint))
|
||||
{
|
||||
// Read 32 bits at a time. This is enough to hold any possible UTF8-encoded scalar.
|
||||
|
||||
Contract.Assert(inputLength - (int)inputBufferCurrentOffset >= sizeof(uint));
|
||||
uint thisDWord = Unsafe.ReadUnaligned<uint>(ref Unsafe.Add(ref inputBuffer, inputBufferCurrentOffset));
|
||||
|
||||
AfterReadDWord:
|
||||
|
||||
#if DEBUG
|
||||
Contract.Assert(lastOffsetProcessed < (long)inputBufferCurrentOffset, "Algorithm should've made forward progress since last read.");
|
||||
lastOffsetProcessed = (long)inputBufferCurrentOffset;
|
||||
#endif
|
||||
|
||||
// First, check for the common case of all-ASCII bytes.
|
||||
|
||||
if (Utf8.Utf8Util.DWordAllBytesAreAscii(thisDWord))
|
||||
{
|
||||
// We read an all-ASCII sequence.
|
||||
|
||||
inputBufferCurrentOffset += 4;
|
||||
inputBufferRemainingBytes -= 4;
|
||||
|
||||
// If we saw a sequence of all ASCII, there's a good chance a significant amount of following data is also ASCII.
|
||||
// Below is basically unrolled loops with poor man's vectorization.
|
||||
|
||||
if (inputBufferRemainingBytes >= 5 * sizeof(uint))
|
||||
{
|
||||
// The JIT produces better codegen for aligned reads than it does for
|
||||
// unaligned reads, and we want the processor to operate at maximum
|
||||
// efficiency in the loop that follows, so we'll align the references
|
||||
// now. It's OK to do this without pinning because the GC will never
|
||||
// move a heap-allocated object in a manner that messes with its
|
||||
// alignment.
|
||||
{
|
||||
ref byte refToCurrentDWord = ref Unsafe.Add(ref inputBuffer, inputBufferCurrentOffset);
|
||||
thisDWord = Unsafe.ReadUnaligned<uint>(ref refToCurrentDWord);
|
||||
if (!Utf8.Utf8Util.DWordAllBytesAreAscii(thisDWord))
|
||||
{
|
||||
goto AfterReadDWordSkipAllBytesAsciiCheck;
|
||||
}
|
||||
|
||||
int adjustment = Utf8.Utf8Util.GetNumberOfBytesToNextDWordAlignment(ref refToCurrentDWord);
|
||||
inputBufferCurrentOffset += adjustment;
|
||||
|
||||
// will adjust 'bytes remaining' value after below loop
|
||||
}
|
||||
|
||||
// At this point, the input buffer offset points to an aligned DWORD.
|
||||
// We also know that there's enough room to read at least four DWORD's from the stream.
|
||||
|
||||
IntPtr inputBufferFinalOffsetAtWhichCanSafelyLoop = (IntPtr)(inputLength - (4 * sizeof(uint)));
|
||||
do
|
||||
{
|
||||
ref uint currentReadPosition = ref Unsafe.As<byte, uint>(ref Unsafe.Add(ref inputBuffer, inputBufferCurrentOffset));
|
||||
|
||||
if (!Utf8.Utf8Util.DWordAllBytesAreAscii(currentReadPosition | Unsafe.Add(ref currentReadPosition, 1)))
|
||||
{
|
||||
goto LoopTerminatedEarlyDueToNonAsciiData;
|
||||
}
|
||||
|
||||
if (!Utf8.Utf8Util.DWordAllBytesAreAscii(Unsafe.Add(ref currentReadPosition, 2) | Unsafe.Add(ref currentReadPosition, 3)))
|
||||
{
|
||||
inputBufferCurrentOffset += 2 * sizeof(uint);
|
||||
goto LoopTerminatedEarlyDueToNonAsciiData;
|
||||
}
|
||||
|
||||
inputBufferCurrentOffset += 4 * sizeof(uint);
|
||||
}
|
||||
while (Utf8.Utf8Util.IntPtrIsLessThanOrEqualTo(inputBufferCurrentOffset, inputBufferFinalOffsetAtWhichCanSafelyLoop));
|
||||
|
||||
inputBufferRemainingBytes = inputLength - Utf8.Utf8Util.ConvertIntPtrToInt32WithoutOverflowCheck(inputBufferCurrentOffset);
|
||||
continue; // need to perform a bounds check because we might be running out of data
|
||||
|
||||
LoopTerminatedEarlyDueToNonAsciiData:
|
||||
|
||||
// We know that there's *at least* two DWORD's of data remaining in the buffer.
|
||||
// We also know that one of them (or both of them) contains non-ASCII data somewhere.
|
||||
// Let's perform a quick check here to bypass the logic at the beginning of the main loop.
|
||||
|
||||
thisDWord = Unsafe.As<byte, uint>(ref Unsafe.Add(ref inputBuffer, inputBufferCurrentOffset));
|
||||
if (Utf8.Utf8Util.DWordAllBytesAreAscii(thisDWord))
|
||||
{
|
||||
inputBufferCurrentOffset += 4;
|
||||
thisDWord = Unsafe.As<byte, uint>(ref Unsafe.Add(ref inputBuffer, inputBufferCurrentOffset));
|
||||
}
|
||||
|
||||
inputBufferRemainingBytes = inputLength - Utf8.Utf8Util.ConvertIntPtrToInt32WithoutOverflowCheck(inputBufferCurrentOffset);
|
||||
goto AfterReadDWordSkipAllBytesAsciiCheck;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
AfterReadDWordSkipAllBytesAsciiCheck:
|
||||
|
||||
Contract.Assert(!Utf8.Utf8Util.DWordAllBytesAreAscii(thisDWord)); // this should have been handled earlier
|
||||
|
||||
// Next, try stripping off ASCII bytes one at a time.
|
||||
// We only handle up to three ASCII bytes here since we handled the four ASCII byte case above.
|
||||
{
|
||||
uint numLeadingAsciiBytes = Utf8.Utf8Util.CountNumberOfLeadingAsciiBytesFrom24BitInteger(thisDWord);
|
||||
inputBufferCurrentOffset += (int)numLeadingAsciiBytes;
|
||||
inputBufferRemainingBytes -= (int)numLeadingAsciiBytes;
|
||||
|
||||
if (inputBufferRemainingBytes < sizeof(uint))
|
||||
{
|
||||
goto ProcessRemainingBytesSlow; // Input buffer doesn't contain enough data to read a DWORD
|
||||
}
|
||||
else
|
||||
{
|
||||
// The input buffer at the current offset contains a non-ASCII byte.
|
||||
// Read an entire DWORD and fall through to multi-byte consumption logic.
|
||||
thisDWord = Unsafe.ReadUnaligned<uint>(ref Unsafe.Add(ref inputBuffer, inputBufferCurrentOffset));
|
||||
}
|
||||
}
|
||||
|
||||
// At this point, we know we're working with a multi-byte code unit,
|
||||
// but we haven't yet validated it.
|
||||
|
||||
// The masks and comparands are derived from the Unicode Standard, Table 3-6.
|
||||
// Additionally, we need to check for valid byte sequences per Table 3-7.
|
||||
|
||||
// Check the 2-byte case.
|
||||
|
||||
BeforeProcessTwoByteSequence:
|
||||
|
||||
if (Utf8.Utf8Util.DWordBeginsWithUtf8TwoByteMask(thisDWord))
|
||||
{
|
||||
// Per Table 3-7, valid sequences are:
|
||||
// [ C2..DF ] [ 80..BF ]
|
||||
|
||||
if (Utf8.Utf8Util.DWordBeginsWithOverlongUtf8TwoByteSequence(thisDWord))
|
||||
{
|
||||
goto Error;
|
||||
}
|
||||
|
||||
ProcessTwoByteSequenceSkipOverlongFormCheck:
|
||||
|
||||
// Optimization: If this is a two-byte-per-character language like Cyrillic or Hebrew,
|
||||
// there's a good chance that if we see one two-byte run then there's another two-byte
|
||||
// run immediately after. Let's check that now.
|
||||
|
||||
// On little-endian platforms, we can check for the two-byte UTF8 mask *and* validate that
|
||||
// the value isn't overlong using a single comparison. On big-endian platforms, we'll need
|
||||
// to validate the mask and validate that the sequence isn't overlong as two separate comparisons.
|
||||
|
||||
if ((BitConverter.IsLittleEndian && Utf8.Utf8Util.DWordEndsWithValidUtf8TwoByteSequenceLittleEndian(thisDWord)) ||
|
||||
(!BitConverter.IsLittleEndian &&
|
||||
(Utf8.Utf8Util.DWordEndsWithUtf8TwoByteMask(thisDWord) && !Utf8.Utf8Util.DWordEndsWithOverlongUtf8TwoByteSequence(thisDWord))))
|
||||
{
|
||||
ConsumeTwoAdjacentKnownGoodTwoByteSequences:
|
||||
|
||||
// We have two runs of two bytes each.
|
||||
inputBufferCurrentOffset += 4;
|
||||
inputBufferRemainingBytes -= 4;
|
||||
tempScalarCount -= 2; // 4 bytes -> 2 scalars
|
||||
|
||||
if (inputBufferRemainingBytes >= sizeof(uint))
|
||||
{
|
||||
// Optimization: If we read a long run of two-byte sequences, the next sequence is probably
|
||||
// also two bytes. Check for that first before going back to the beginning of the loop.
|
||||
|
||||
thisDWord = Unsafe.ReadUnaligned<uint>(ref Unsafe.Add(ref inputBuffer, inputBufferCurrentOffset));
|
||||
|
||||
if (BitConverter.IsLittleEndian)
|
||||
{
|
||||
if (Utf8.Utf8Util.DWordBeginsWithValidUtf8TwoByteSequenceLittleEndian(thisDWord))
|
||||
{
|
||||
// The next sequence is a valid two-byte sequence.
|
||||
goto ProcessTwoByteSequenceSkipOverlongFormCheck;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Utf8.Utf8Util.DWordBeginsAndEndsWithUtf8TwoByteMask(thisDWord))
|
||||
{
|
||||
if (Utf8.Utf8Util.DWordBeginsWithOverlongUtf8TwoByteSequence(thisDWord) ||
|
||||
Utf8.Utf8Util.DWordEndsWithOverlongUtf8TwoByteSequence(thisDWord))
|
||||
{
|
||||
// Mask said it was 2x 2-byte sequences but validation failed, go to beginning of loop for error handling
|
||||
goto AfterReadDWord;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Validated next bytes are 2x 2-byte sequences
|
||||
goto ConsumeTwoAdjacentKnownGoodTwoByteSequences;
|
||||
}
|
||||
}
|
||||
else if (Utf8.Utf8Util.DWordBeginsWithUtf8TwoByteMask(thisDWord))
|
||||
{
|
||||
if (Utf8.Utf8Util.DWordBeginsWithOverlongUtf8TwoByteSequence(thisDWord))
|
||||
{
|
||||
// Mask said it was a 2-byte sequence but validation failed
|
||||
goto Error;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Validated next bytes are a single 2-byte sequence with no valid 2-byte sequence following
|
||||
goto ConsumeSingleKnownGoodTwoByteSequence;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we reached this point, the next sequence is something other than a valid
|
||||
// two-byte sequence, so go back to the beginning of the loop.
|
||||
goto AfterReadDWord;
|
||||
}
|
||||
else
|
||||
{
|
||||
goto ProcessRemainingBytesSlow; // Running out of data - go down slow path
|
||||
}
|
||||
}
|
||||
|
||||
ConsumeSingleKnownGoodTwoByteSequence:
|
||||
|
||||
// The buffer contains a 2-byte sequence followed by 2 bytes that aren't a 2-byte sequence.
|
||||
// Unlikely that a 3-byte sequence would follow a 2-byte sequence, so perhaps remaining
|
||||
// bytes are ASCII?
|
||||
|
||||
if (Utf8.Utf8Util.DWordThirdByteIsAscii(thisDWord))
|
||||
{
|
||||
if (Utf8.Utf8Util.DWordFourthByteIsAscii(thisDWord))
|
||||
{
|
||||
inputBufferCurrentOffset += 4; // a 2-byte sequence + 2 ASCII bytes
|
||||
inputBufferRemainingBytes -= 4; // a 2-byte sequence + 2 ASCII bytes
|
||||
tempScalarCount--; // 2-byte sequence + 2 ASCII bytes -> 3 scalars
|
||||
}
|
||||
else
|
||||
{
|
||||
inputBufferCurrentOffset += 3; // a 2-byte sequence + 1 ASCII byte
|
||||
inputBufferRemainingBytes -= 3; // a 2-byte sequence + 1 ASCII byte
|
||||
tempScalarCount--; // 2-byte sequence + 1 ASCII bytes -> 2 scalars
|
||||
|
||||
// A two-byte sequence followed by an ASCII byte followed by a non-ASCII byte.
|
||||
// Read in the next DWORD and jump directly to the start of the multi-byte processing block.
|
||||
|
||||
if (inputBufferRemainingBytes >= sizeof(uint))
|
||||
{
|
||||
thisDWord = Unsafe.ReadUnaligned<uint>(ref Unsafe.Add(ref inputBuffer, inputBufferCurrentOffset));
|
||||
goto BeforeProcessTwoByteSequence;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
inputBufferCurrentOffset += 2;
|
||||
inputBufferRemainingBytes -= 2;
|
||||
tempScalarCount--; // 2-byte sequence -> 1 scalar
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check the 3-byte case.
|
||||
|
||||
if (Utf8.Utf8Util.DWordBeginsWithUtf8ThreeByteMask(thisDWord))
|
||||
{
|
||||
ProcessThreeByteSequenceWithCheck:
|
||||
|
||||
// We need to check for overlong or surrogate three-byte sequences.
|
||||
//
|
||||
// Per Table 3-7, valid sequences are:
|
||||
// [ E0 ] [ A0..BF ] [ 80..BF ]
|
||||
// [ E1..EC ] [ 80..BF ] [ 80..BF ]
|
||||
// [ ED ] [ 80..9F ] [ 80..BF ]
|
||||
// [ EE..EF ] [ 80..BF ] [ 80..BF ]
|
||||
//
|
||||
// Big-endian examples of using the above validation table:
|
||||
// E0A0 = 1110 0000 1010 0000 => invalid (overlong ) patterns are 1110 0000 100# ####
|
||||
// ED9F = 1110 1101 1001 1111 => invalid (surrogate) patterns are 1110 1101 101# ####
|
||||
// If using the bitmask ......................................... 0000 1111 0010 0000 (=0F20),
|
||||
// Then invalid (overlong) patterns match the comparand ......... 0000 0000 0000 0000 (=0000),
|
||||
// And invalid (surrogate) patterns match the comparand ......... 0000 1101 0010 0000 (=0D20).
|
||||
|
||||
if (BitConverter.IsLittleEndian)
|
||||
{
|
||||
// The "overlong or surrogate" check can be implemented using a single jump, but there's
|
||||
// some overhead to moving the bits into the correct locations in order to perform the
|
||||
// correct comparison, and in practice the processor's branch prediction capability is
|
||||
// good enough that we shouldn't bother. So we'll use two jumps instead.
|
||||
|
||||
// Can't extract this check into its own helper method because JIT produces suboptimal
|
||||
// assembly, even with aggressive inlining.
|
||||
|
||||
uint comparand = thisDWord & 0x0000200FU;
|
||||
if ((comparand == 0U) || (comparand == 0x0000200DU))
|
||||
{
|
||||
goto Error;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
uint comparand = thisDWord & 0x0F200000U;
|
||||
if ((comparand == 0U) || (comparand == 0x0D200000U))
|
||||
{
|
||||
goto Error;
|
||||
}
|
||||
}
|
||||
|
||||
ProcessSingleThreeByteSequenceSkipOverlongAndSurrogateChecks:
|
||||
|
||||
inputBufferCurrentOffset += 3;
|
||||
inputBufferRemainingBytes -= 3;
|
||||
tempScalarCount -= 2; // 3 bytes -> 1 scalar
|
||||
|
||||
// Occasionally one-off ASCII characters like spaces, periods, or newlines will make their way
|
||||
// in to the text. If this happens strip it off now before seeing if the next character
|
||||
// consists of three code units.
|
||||
|
||||
if (Utf8.Utf8Util.DWordFourthByteIsAscii(thisDWord))
|
||||
{
|
||||
inputBufferCurrentOffset += 1;
|
||||
inputBufferRemainingBytes--;
|
||||
}
|
||||
|
||||
SuccessfullyProcessedThreeByteSequence:
|
||||
|
||||
// Optimization: A three-byte character could indicate CJK text, which makes it likely
|
||||
// that the character following this one is also CJK. We'll try to process several
|
||||
// three-byte sequences at a time.
|
||||
|
||||
if (IntPtr.Size >= 8 && BitConverter.IsLittleEndian && inputBufferRemainingBytes >= (sizeof(ulong) + 1))
|
||||
{
|
||||
ulong thisQWord = Unsafe.ReadUnaligned<ulong>(ref Unsafe.Add(ref inputBuffer, inputBufferCurrentOffset));
|
||||
|
||||
// Is this three 3-byte sequences in a row?
|
||||
// thisQWord = [ 10yyyyyy 1110zzzz | 10xxxxxx 10yyyyyy 1110zzzz | 10xxxxxx 10yyyyyy 1110zzzz ] [ 10xxxxxx ]
|
||||
// ---- CHAR 3 ---- --------- CHAR 2 --------- --------- CHAR 1 --------- -CHAR 3-
|
||||
if ((thisQWord & 0xC0F0C0C0F0C0C0F0UL) == 0x80E08080E08080E0UL &&
|
||||
Utf8.Utf8Util.IsUtf8ContinuationByte(Unsafe.Add(ref inputBuffer, inputBufferCurrentOffset + sizeof(ulong))))
|
||||
{
|
||||
// Saw a proper bitmask for three incoming 3-byte sequences, perform the
|
||||
// overlong and surrogate sequence checking now.
|
||||
|
||||
// Check the first character.
|
||||
// If the first character is overlong or a surrogate, fail immediately.
|
||||
|
||||
uint comparand = unchecked((uint)thisQWord) & 0x200FU;
|
||||
if ((comparand == 0UL) || (comparand == 0x200DU))
|
||||
{
|
||||
goto Error;
|
||||
}
|
||||
|
||||
// Check the second character.
|
||||
// If this character is overlong or a surrogate, process the first character (which we
|
||||
// know to be good because the first check passed) before reporting an error.
|
||||
|
||||
comparand = unchecked((uint)(thisQWord >> 24)) & 0x200FU;
|
||||
if ((comparand == 0U) || (comparand == 0x200DU))
|
||||
{
|
||||
thisDWord = unchecked((uint)thisQWord);
|
||||
goto ProcessSingleThreeByteSequenceSkipOverlongAndSurrogateChecks;
|
||||
}
|
||||
|
||||
// Check the third character (we already checked that it's followed by a continuation byte).
|
||||
// If this character is overlong or a surrogate, process the first character (which we
|
||||
// know to be good because the first check passed) before reporting an error.
|
||||
|
||||
comparand = unchecked((uint)(thisQWord >> 48)) & 0x200FU;
|
||||
if ((comparand == 0U) || (comparand == 0x200DU))
|
||||
{
|
||||
thisDWord = unchecked((uint)thisQWord);
|
||||
goto ProcessSingleThreeByteSequenceSkipOverlongAndSurrogateChecks;
|
||||
}
|
||||
|
||||
inputBufferCurrentOffset += 9;
|
||||
inputBufferRemainingBytes -= 9;
|
||||
tempScalarCount -= 6; // 9 bytes -> 3 scalars
|
||||
goto SuccessfullyProcessedThreeByteSequence;
|
||||
}
|
||||
|
||||
// Is this two 3-byte sequences in a row?
|
||||
// thisQWord = [ ######## ######## | 10xxxxxx 10yyyyyy 1110zzzz | 10xxxxxx 10yyyyyy 1110zzzz ]
|
||||
// --------- CHAR 2 --------- --------- CHAR 1 ---------
|
||||
if ((thisQWord & 0xC0C0F0C0C0F0UL) == 0x8080E08080E0UL)
|
||||
{
|
||||
// Saw a proper bitmask for two incoming 3-byte sequences, perform the
|
||||
// overlong and surrogate sequence checking now.
|
||||
|
||||
// Check the first character.
|
||||
// If the first character is overlong or a surrogate, fail immediately.
|
||||
|
||||
uint comparand = unchecked((uint)thisQWord) & 0x200FU;
|
||||
if ((comparand == 0UL) || (comparand == 0x200DU))
|
||||
{
|
||||
goto Error;
|
||||
}
|
||||
|
||||
// Check the second character.
|
||||
// If this character is overlong or a surrogate, process the first character (which we
|
||||
// know to be good because the first check passed) before reporting an error.
|
||||
|
||||
comparand = unchecked((uint)(thisQWord >> 24)) & 0x200FU;
|
||||
if ((comparand == 0U) || (comparand == 0x200DU))
|
||||
{
|
||||
thisDWord = unchecked((uint)thisQWord);
|
||||
goto ProcessSingleThreeByteSequenceSkipOverlongAndSurrogateChecks;
|
||||
}
|
||||
|
||||
inputBufferCurrentOffset += 6;
|
||||
inputBufferRemainingBytes -= 6;
|
||||
tempScalarCount -= 4; // 6 bytes -> 2 scalars
|
||||
|
||||
// The next char in the sequence didn't have a 3-byte marker, so it's probably
|
||||
// an ASCII character. Jump back to the beginning of loop processing.
|
||||
continue;
|
||||
}
|
||||
|
||||
thisDWord = unchecked((uint)thisQWord);
|
||||
if (Utf8.Utf8Util.DWordBeginsWithUtf8ThreeByteMask(thisDWord))
|
||||
{
|
||||
// A single three-byte sequence.
|
||||
goto ProcessThreeByteSequenceWithCheck;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not a three-byte sequence; perhaps ASCII?
|
||||
goto AfterReadDWord;
|
||||
}
|
||||
}
|
||||
|
||||
if (inputBufferRemainingBytes >= sizeof(uint))
|
||||
{
|
||||
thisDWord = Unsafe.ReadUnaligned<uint>(ref Unsafe.Add(ref inputBuffer, inputBufferCurrentOffset));
|
||||
|
||||
// Optimization: A three-byte character could indicate CJK text, which makes it likely
|
||||
// that the character following this one is also CJK. We'll check for a three-byte sequence
|
||||
// marker now and jump directly to three-byte sequence processing if we see one, skipping
|
||||
// all of the logic at the beginning of the loop.
|
||||
|
||||
if (Utf8.Utf8Util.DWordBeginsWithUtf8ThreeByteMask(thisDWord))
|
||||
{
|
||||
goto ProcessThreeByteSequenceWithCheck; // Found another [not yet validated] three-byte sequence; process
|
||||
}
|
||||
else
|
||||
{
|
||||
goto AfterReadDWord; // Probably ASCII punctuation or whitespace; go back to start of loop
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
goto ProcessRemainingBytesSlow; // Running out of data
|
||||
}
|
||||
}
|
||||
|
||||
// Assume the 4-byte case, but we need to validate.
|
||||
|
||||
{
|
||||
// We need to check for overlong or invalid (over U+10FFFF) four-byte sequences.
|
||||
//
|
||||
// Per Table 3-7, valid sequences are:
|
||||
// [ F0 ] [ 90..BF ] [ 80..BF ] [ 80..BF ]
|
||||
// [ F1..F3 ] [ 80..BF ] [ 80..BF ] [ 80..BF ]
|
||||
// [ F4 ] [ 80..8F ] [ 80..BF ] [ 80..BF ]
|
||||
|
||||
if (!Utf8.Utf8Util.DWordBeginsWithUtf8FourByteMask(thisDWord))
|
||||
{
|
||||
goto Error;
|
||||
}
|
||||
|
||||
// Now check for overlong / out-of-range sequences.
|
||||
|
||||
if (BitConverter.IsLittleEndian)
|
||||
{
|
||||
// The DWORD we read is [ 10xxxxxx 10yyyyyy 10zzzzzz 11110www ].
|
||||
// We want to get the 'w' byte in front of the 'z' byte so that we can perform
|
||||
// a single range comparison. We'll take advantage of the fact that the JITter
|
||||
// can detect a ROR / ROL operation, then we'll just zero out the bytes that
|
||||
// aren't involved in the range check.
|
||||
|
||||
uint toCheck = unchecked((ushort)thisDWord);
|
||||
|
||||
// At this point, toCheck = [ 00000000 00000000 10zzzzzz 11110www ].
|
||||
|
||||
toCheck = (toCheck << 24) | (toCheck >> 8); // ROR 8 / ROL 24
|
||||
|
||||
// At this point, toCheck = [ 11110www 00000000 00000000 10zzzzzz ].
|
||||
|
||||
if (!Utf8.Utf8Util.IsInRangeInclusive(toCheck, 0xF0000090U, 0xF400008FU))
|
||||
{
|
||||
goto Error;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!Utf8.Utf8Util.IsInRangeInclusive(thisDWord, 0xF0900000U, 0xF48FFFFFU))
|
||||
{
|
||||
goto Error;
|
||||
}
|
||||
}
|
||||
|
||||
// Validation complete.
|
||||
|
||||
inputBufferCurrentOffset += 4;
|
||||
inputBufferRemainingBytes -= 4;
|
||||
tempScalarCount -= 3; // 4 bytes -> 1 scalar
|
||||
tempSurrogatePairCount++; // 4 bytes implies UTF16 surrogate pair
|
||||
|
||||
continue; // go back to beginning of loop for processing
|
||||
}
|
||||
}
|
||||
|
||||
ProcessRemainingBytesSlow:
|
||||
|
||||
Contract.Assert(inputBufferRemainingBytes < 4);
|
||||
while (inputBufferRemainingBytes > 0)
|
||||
{
|
||||
uint firstByte = Unsafe.Add(ref inputBuffer, inputBufferCurrentOffset);
|
||||
|
||||
if (firstByte < 0x80U)
|
||||
{
|
||||
// 1-byte (ASCII) case
|
||||
inputBufferCurrentOffset += 1;
|
||||
inputBufferRemainingBytes -= 1;
|
||||
continue;
|
||||
}
|
||||
else if (inputBufferRemainingBytes >= 2)
|
||||
{
|
||||
uint secondByte = Unsafe.Add(ref inputBuffer, inputBufferCurrentOffset + 1);
|
||||
if (firstByte < 0xE0U)
|
||||
{
|
||||
// 2-byte case
|
||||
if (firstByte >= 0xC2U && Utf8.Utf8Util.IsUtf8ContinuationByte(secondByte))
|
||||
{
|
||||
inputBufferCurrentOffset += 2;
|
||||
inputBufferRemainingBytes -= 2;
|
||||
tempScalarCount--; // 2 bytes -> 1 scalar
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (inputBufferRemainingBytes >= 3)
|
||||
{
|
||||
uint thirdByte = Unsafe.Add(ref inputBuffer, inputBufferCurrentOffset + 2);
|
||||
if (firstByte < 0xF0U)
|
||||
{
|
||||
if (firstByte == 0xE0U)
|
||||
{
|
||||
if (!Utf8.Utf8Util.IsInRangeInclusive(secondByte, 0xA0U, 0xBFU))
|
||||
{
|
||||
goto Error;
|
||||
}
|
||||
}
|
||||
else if (firstByte == 0xEDU)
|
||||
{
|
||||
if (!Utf8.Utf8Util.IsInRangeInclusive(secondByte, 0x80U, 0x9FU))
|
||||
{
|
||||
goto Error;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!Utf8.Utf8Util.IsUtf8ContinuationByte(secondByte))
|
||||
{
|
||||
goto Error;
|
||||
}
|
||||
}
|
||||
|
||||
if (Utf8.Utf8Util.IsUtf8ContinuationByte(thirdByte))
|
||||
{
|
||||
inputBufferCurrentOffset += 3;
|
||||
inputBufferRemainingBytes -= 3;
|
||||
tempScalarCount -= 2; // 3 bytes -> 1 scalar
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Error - no match.
|
||||
|
||||
goto Error;
|
||||
}
|
||||
|
||||
// If we reached this point, we're out of data, and we saw no bad UTF8 sequence.
|
||||
|
||||
scalarCount = tempScalarCount;
|
||||
surrogatePairCount = tempSurrogatePairCount;
|
||||
return -1;
|
||||
|
||||
// Error handling logic.
|
||||
|
||||
Error:
|
||||
|
||||
scalarCount = tempScalarCount -
|
||||
inputBufferRemainingBytes; // we assumed earlier each byte corresponded to a single scalar, perform fixup now to account for unread bytes
|
||||
|
||||
surrogatePairCount = tempSurrogatePairCount;
|
||||
return Utf8.Utf8Util.ConvertIntPtrToInt32WithoutOverflowCheck(inputBufferCurrentOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
480
src/Core/Core/Utf8/UtfAnyString.cs
Normal file
480
src/Core/Core/Utf8/UtfAnyString.cs
Normal file
@@ -0,0 +1,480 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma warning disable CA2225 // Operator overloads have named alternates
|
||||
|
||||
// ReSharper disable once UseNameofExpression
|
||||
namespace Microsoft.Azure.Cosmos.Core.Utf8
|
||||
{
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
/// <summary>A string whose memory representation may be either UTF8 or UTF16.</summary>
|
||||
/// <remarks>
|
||||
/// This type supports polymorphic use of <see cref="string" /> and <see cref="Utf8String" />
|
||||
/// when equality, hashing, and comparison are needed against either encoding. An API leveraging
|
||||
/// <see cref="UtfAnyString" /> can avoid separate method overloads while still accepting either
|
||||
/// encoding without imposing additional allocations.
|
||||
/// </remarks>
|
||||
[DebuggerDisplay("{ToString()}")]
|
||||
public readonly struct UtfAnyString :
|
||||
IEquatable<UtfAnyString>, IComparable<UtfAnyString>,
|
||||
IEquatable<Utf8String>, IComparable<Utf8String>,
|
||||
IEquatable<string>, IComparable<string>
|
||||
{
|
||||
public static UtfAnyString Empty => string.Empty;
|
||||
|
||||
private readonly object buffer;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public UtfAnyString(string utf16String)
|
||||
{
|
||||
this.buffer = utf16String;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public UtfAnyString(Utf8String utf8String)
|
||||
{
|
||||
this.buffer = utf8String;
|
||||
}
|
||||
|
||||
public bool IsUtf8 => this.buffer is Utf8String;
|
||||
|
||||
public bool IsUtf16 => this.buffer is string;
|
||||
|
||||
/// <summary>True if the length is empty.</summary>
|
||||
public bool IsNull => object.ReferenceEquals(null, this.buffer);
|
||||
|
||||
/// <summary>True if the length is empty.</summary>
|
||||
public bool IsEmpty
|
||||
{
|
||||
get
|
||||
{
|
||||
if (object.ReferenceEquals(null, this.buffer))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (this.buffer)
|
||||
{
|
||||
case string s:
|
||||
return s.Length == 0;
|
||||
default:
|
||||
return ((Utf8String)this.buffer).IsEmpty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static implicit operator UtfAnyString(string utf16String)
|
||||
{
|
||||
return new UtfAnyString(utf16String);
|
||||
}
|
||||
|
||||
public static implicit operator string(UtfAnyString str)
|
||||
{
|
||||
return str.buffer?.ToString();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
// ReSharper disable once AssignNullToNotNullAttribute
|
||||
return this.buffer?.ToString();
|
||||
}
|
||||
|
||||
public Utf8String ToUtf8String()
|
||||
{
|
||||
if (object.ReferenceEquals(null, this.buffer))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (this.buffer)
|
||||
{
|
||||
case string s:
|
||||
return Utf8String.TranscodeUtf16(s);
|
||||
default:
|
||||
return (Utf8String)this.buffer;
|
||||
}
|
||||
}
|
||||
|
||||
public bool ReferenceEquals(UtfAnyString other)
|
||||
{
|
||||
return this.buffer == other.buffer;
|
||||
}
|
||||
|
||||
public bool Equals(UtfAnyString other)
|
||||
{
|
||||
if (object.ReferenceEquals(null, this.buffer))
|
||||
{
|
||||
return object.ReferenceEquals(null, other.buffer);
|
||||
}
|
||||
|
||||
switch (this.buffer)
|
||||
{
|
||||
case string s:
|
||||
return other.Equals(s);
|
||||
default:
|
||||
return other.Equals((Utf8String)this.buffer);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Equals(Utf8Span other)
|
||||
{
|
||||
return other.Equals(this.buffer);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
switch (obj)
|
||||
{
|
||||
case string s:
|
||||
return this.Equals(s);
|
||||
case Utf8String s:
|
||||
return this.Equals(s);
|
||||
case UtfAnyString s:
|
||||
return this.Equals(s);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool Equals(Utf8String other)
|
||||
{
|
||||
if (object.ReferenceEquals(null, other))
|
||||
{
|
||||
return object.ReferenceEquals(null, this.buffer);
|
||||
}
|
||||
|
||||
return other.Equals(this.buffer);
|
||||
}
|
||||
|
||||
public bool Equals(string other)
|
||||
{
|
||||
if (object.ReferenceEquals(null, this.buffer))
|
||||
{
|
||||
return object.ReferenceEquals(null, other);
|
||||
}
|
||||
|
||||
switch (this.buffer)
|
||||
{
|
||||
case string s:
|
||||
return string.Equals(s, other, StringComparison.Ordinal);
|
||||
default:
|
||||
return ((Utf8String)this.buffer).Equals(other);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool operator ==(UtfAnyString left, UtfAnyString right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator !=(UtfAnyString left, UtfAnyString right)
|
||||
{
|
||||
return !left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator ==(UtfAnyString left, string right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator !=(UtfAnyString left, string right)
|
||||
{
|
||||
return !left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator ==(string left, UtfAnyString right)
|
||||
{
|
||||
return right.Equals(left);
|
||||
}
|
||||
|
||||
public static bool operator !=(string left, UtfAnyString right)
|
||||
{
|
||||
return !right.Equals(left);
|
||||
}
|
||||
|
||||
public static bool operator ==(UtfAnyString left, Utf8String right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator !=(UtfAnyString left, Utf8String right)
|
||||
{
|
||||
return !left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator ==(Utf8String left, UtfAnyString right)
|
||||
{
|
||||
return right.Equals(left);
|
||||
}
|
||||
|
||||
public static bool operator !=(Utf8String left, UtfAnyString right)
|
||||
{
|
||||
return !right.Equals(left);
|
||||
}
|
||||
|
||||
public static bool operator ==(UtfAnyString left, Utf8Span right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator !=(UtfAnyString left, Utf8Span right)
|
||||
{
|
||||
return !left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator ==(Utf8Span left, UtfAnyString right)
|
||||
{
|
||||
return right.Equals(left);
|
||||
}
|
||||
|
||||
public static bool operator !=(Utf8Span left, UtfAnyString right)
|
||||
{
|
||||
return !right.Equals(left);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
uint hash1 = 5381;
|
||||
uint hash2 = hash1;
|
||||
|
||||
if (object.ReferenceEquals(null, this.buffer))
|
||||
{
|
||||
return unchecked((int)(hash1 + (hash2 * 1566083941)));
|
||||
}
|
||||
|
||||
switch (this.buffer)
|
||||
{
|
||||
case string s:
|
||||
unchecked
|
||||
{
|
||||
Utf16LittleEndianCodePointEnumerator thisEnumerator = new Utf16LittleEndianCodePointEnumerator(s);
|
||||
for (int i = 0; thisEnumerator.MoveNext(); i++)
|
||||
{
|
||||
uint c = thisEnumerator.Current;
|
||||
if (i % 2 == 0)
|
||||
{
|
||||
hash1 = ((hash1 << 5) + hash1) ^ c;
|
||||
}
|
||||
else
|
||||
{
|
||||
hash2 = ((hash2 << 5) + hash2) ^ c;
|
||||
}
|
||||
}
|
||||
|
||||
return (int)(hash1 + (hash2 * 1566083941));
|
||||
}
|
||||
|
||||
default:
|
||||
return this.buffer.GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
public static bool operator <(UtfAnyString left, UtfAnyString right)
|
||||
{
|
||||
return left.CompareTo(right) < 0;
|
||||
}
|
||||
|
||||
public static bool operator <=(UtfAnyString left, UtfAnyString right)
|
||||
{
|
||||
return left.CompareTo(right) <= 0;
|
||||
}
|
||||
|
||||
public static bool operator >(UtfAnyString left, UtfAnyString right)
|
||||
{
|
||||
return left.CompareTo(right) > 0;
|
||||
}
|
||||
|
||||
public static bool operator >=(UtfAnyString left, UtfAnyString right)
|
||||
{
|
||||
return left.CompareTo(right) >= 0;
|
||||
}
|
||||
|
||||
public static bool operator <(UtfAnyString left, string right)
|
||||
{
|
||||
return left.CompareTo(right) < 0;
|
||||
}
|
||||
|
||||
public static bool operator <=(UtfAnyString left, string right)
|
||||
{
|
||||
return left.CompareTo(right) <= 0;
|
||||
}
|
||||
|
||||
public static bool operator >(UtfAnyString left, string right)
|
||||
{
|
||||
return left.CompareTo(right) > 0;
|
||||
}
|
||||
|
||||
public static bool operator >=(UtfAnyString left, string right)
|
||||
{
|
||||
return left.CompareTo(right) >= 0;
|
||||
}
|
||||
|
||||
public static bool operator <(string left, UtfAnyString right)
|
||||
{
|
||||
return right.CompareTo(left) >= 0;
|
||||
}
|
||||
|
||||
public static bool operator <=(string left, UtfAnyString right)
|
||||
{
|
||||
return right.CompareTo(left) > 0;
|
||||
}
|
||||
|
||||
public static bool operator >(string left, UtfAnyString right)
|
||||
{
|
||||
return right.CompareTo(left) <= 0;
|
||||
}
|
||||
|
||||
public static bool operator >=(string left, UtfAnyString right)
|
||||
{
|
||||
return right.CompareTo(left) < 0;
|
||||
}
|
||||
|
||||
public static bool operator <(UtfAnyString left, Utf8String right)
|
||||
{
|
||||
return left.CompareTo(right) < 0;
|
||||
}
|
||||
|
||||
public static bool operator <=(UtfAnyString left, Utf8String right)
|
||||
{
|
||||
return left.CompareTo(right) <= 0;
|
||||
}
|
||||
|
||||
public static bool operator >(UtfAnyString left, Utf8String right)
|
||||
{
|
||||
return left.CompareTo(right) > 0;
|
||||
}
|
||||
|
||||
public static bool operator >=(UtfAnyString left, Utf8String right)
|
||||
{
|
||||
return left.CompareTo(right) >= 0;
|
||||
}
|
||||
|
||||
public static bool operator <(Utf8String left, UtfAnyString right)
|
||||
{
|
||||
return right.CompareTo(left) >= 0;
|
||||
}
|
||||
|
||||
public static bool operator <=(Utf8String left, UtfAnyString right)
|
||||
{
|
||||
return right.CompareTo(left) > 0;
|
||||
}
|
||||
|
||||
public static bool operator >(Utf8String left, UtfAnyString right)
|
||||
{
|
||||
return right.CompareTo(left) <= 0;
|
||||
}
|
||||
|
||||
public static bool operator >=(Utf8String left, UtfAnyString right)
|
||||
{
|
||||
return right.CompareTo(left) < 0;
|
||||
}
|
||||
|
||||
public static bool operator <(UtfAnyString left, Utf8Span right)
|
||||
{
|
||||
return left.CompareTo(right) < 0;
|
||||
}
|
||||
|
||||
public static bool operator <=(UtfAnyString left, Utf8Span right)
|
||||
{
|
||||
return left.CompareTo(right) <= 0;
|
||||
}
|
||||
|
||||
public static bool operator >(UtfAnyString left, Utf8Span right)
|
||||
{
|
||||
return left.CompareTo(right) > 0;
|
||||
}
|
||||
|
||||
public static bool operator >=(UtfAnyString left, Utf8Span right)
|
||||
{
|
||||
return left.CompareTo(right) >= 0;
|
||||
}
|
||||
|
||||
public static bool operator <(Utf8Span left, UtfAnyString right)
|
||||
{
|
||||
return right.CompareTo(left) >= 0;
|
||||
}
|
||||
|
||||
public static bool operator <=(Utf8Span left, UtfAnyString right)
|
||||
{
|
||||
return right.CompareTo(left) > 0;
|
||||
}
|
||||
|
||||
public static bool operator >(Utf8Span left, UtfAnyString right)
|
||||
{
|
||||
return right.CompareTo(left) <= 0;
|
||||
}
|
||||
|
||||
public static bool operator >=(Utf8Span left, UtfAnyString right)
|
||||
{
|
||||
return right.CompareTo(left) < 0;
|
||||
}
|
||||
|
||||
public int CompareTo(UtfAnyString other)
|
||||
{
|
||||
if (object.ReferenceEquals(null, other.buffer))
|
||||
{
|
||||
return object.ReferenceEquals(null, this.buffer) ? 0 : 1;
|
||||
}
|
||||
|
||||
switch (other.buffer)
|
||||
{
|
||||
case string s:
|
||||
return this.CompareTo(s);
|
||||
default:
|
||||
return this.CompareTo((Utf8String)other.buffer);
|
||||
}
|
||||
}
|
||||
|
||||
public int CompareTo(Utf8String other)
|
||||
{
|
||||
if (object.ReferenceEquals(null, this.buffer))
|
||||
{
|
||||
return object.ReferenceEquals(null, other) ? 0 : -1;
|
||||
}
|
||||
|
||||
switch (this.buffer)
|
||||
{
|
||||
case string s:
|
||||
return -other.Span.CompareTo(s);
|
||||
default:
|
||||
return -other.Span.CompareTo((Utf8String)this.buffer);
|
||||
}
|
||||
}
|
||||
|
||||
public int CompareTo(Utf8Span other)
|
||||
{
|
||||
if (object.ReferenceEquals(null, this.buffer))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (this.buffer)
|
||||
{
|
||||
case string s:
|
||||
return -other.CompareTo(s);
|
||||
default:
|
||||
return -other.CompareTo((Utf8String)this.buffer);
|
||||
}
|
||||
}
|
||||
|
||||
public int CompareTo(string other)
|
||||
{
|
||||
if (object.ReferenceEquals(null, this.buffer))
|
||||
{
|
||||
return object.ReferenceEquals(null, other) ? 0 : -1;
|
||||
}
|
||||
|
||||
switch (this.buffer)
|
||||
{
|
||||
case string s:
|
||||
return string.Compare(s, other, StringComparison.Ordinal);
|
||||
default:
|
||||
return ((Utf8String)this.buffer).CompareTo(other);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
52
src/Directory.Build.props
Normal file
52
src/Directory.Build.props
Normal 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
181
src/HybridRow.sln
Normal 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
|
||||
@@ -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>
|
||||
453
src/Serialization/HybridRow.Json/RowReaderJsonExtensions.cs
Normal file
453
src/Serialization/HybridRow.Json/RowReaderJsonExtensions.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
24
src/Serialization/HybridRow.Json/RowReaderJsonSettings.cs
Normal file
24
src/Serialization/HybridRow.Json/RowReaderJsonSettings.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -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>";
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
74
src/Serialization/HybridRow.Native.Tests.Unit/ResultAssert.h
Normal file
74
src/Serialization/HybridRow.Native.Tests.Unit/ResultAssert.h
Normal 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());
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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>);
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
};
|
||||
}
|
||||
4
src/Serialization/HybridRow.Native.Tests.Unit/cpp.hint
Normal file
4
src/Serialization/HybridRow.Native.Tests.Unit/cpp.hint
Normal 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)
|
||||
5
src/Serialization/HybridRow.Native.Tests.Unit/pch.cpp
Normal file
5
src/Serialization/HybridRow.Native.Tests.Unit/pch.cpp
Normal 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.
|
||||
7
src/Serialization/HybridRow.Native.Tests.Unit/pch.h
Normal file
7
src/Serialization/HybridRow.Native.Tests.Unit/pch.h
Normal file
@@ -0,0 +1,7 @@
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../HybridRow.Native/HybridRow.Native.h"
|
||||
27
src/Serialization/HybridRow.Native/AllowEmptyKind.h
Normal file
27
src/Serialization/HybridRow.Native/AllowEmptyKind.h
Normal 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,
|
||||
};
|
||||
}
|
||||
105
src/Serialization/HybridRow.Native/ArrayHybridRowSerializer.h
Normal file
105
src/Serialization/HybridRow.Native/ArrayHybridRowSerializer.h
Normal 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();
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
47
src/Serialization/HybridRow.Native/ArrayPropertyType.h
Normal file
47
src/Serialization/HybridRow.Native/ArrayPropertyType.h
Normal 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;
|
||||
};
|
||||
}
|
||||
55
src/Serialization/HybridRow.Native/DateTime.h
Normal file
55
src/Serialization/HybridRow.Native/DateTime.h
Normal 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);
|
||||
}
|
||||
};
|
||||
}
|
||||
81
src/Serialization/HybridRow.Native/Decimal.h
Normal file
81
src/Serialization/HybridRow.Native/Decimal.h
Normal 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);
|
||||
}
|
||||
};
|
||||
}
|
||||
65
src/Serialization/HybridRow.Native/EnumSchema.h
Normal file
65
src/Serialization/HybridRow.Native/EnumSchema.h
Normal 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;
|
||||
};
|
||||
}
|
||||
41
src/Serialization/HybridRow.Native/EnumValue.h
Normal file
41
src/Serialization/HybridRow.Native/EnumValue.h
Normal 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;
|
||||
};
|
||||
}
|
||||
78
src/Serialization/HybridRow.Native/Float128.h
Normal file
78
src/Serialization/HybridRow.Native/Float128.h
Normal 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);
|
||||
}
|
||||
};
|
||||
}
|
||||
23
src/Serialization/HybridRow.Native/Guid.cpp
Normal file
23
src/Serialization/HybridRow.Native/Guid.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
65
src/Serialization/HybridRow.Native/Guid.h
Normal file
65
src/Serialization/HybridRow.Native/Guid.h
Normal 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]);
|
||||
}
|
||||
};
|
||||
}
|
||||
59
src/Serialization/HybridRow.Native/HybridRow.Native.h
Normal file
59
src/Serialization/HybridRow.Native/HybridRow.Native.h
Normal 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"
|
||||
@@ -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>
|
||||
177
src/Serialization/HybridRow.Native/HybridRow.Native.vcxproj
Normal file
177
src/Serialization/HybridRow.Native/HybridRow.Native.vcxproj
Normal 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>
|
||||
@@ -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>
|
||||
53
src/Serialization/HybridRow.Native/HybridRowHeader.h
Normal file
53
src/Serialization/HybridRow.Native/HybridRowHeader.h
Normal 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
Reference in New Issue
Block a user