diff --git a/include/PerfectHash.h b/include/PerfectHash.h
index 2ebb843b..4976e513 100644
--- a/include/PerfectHash.h
+++ b/include/PerfectHash.h
@@ -1948,6 +1948,11 @@ IsValidSeedMasks(
6, \
DECL_SEED_MASKS(0, 0, 0x1f1f1f1f, 0, 0, 0x1f1f1f1f, 0, 0) \
) \
+ ENTRY( \
+ MultiplyRotateR, \
+ 3, \
+ DECL_SEED_MASKS(0, 0, 0x1f1f, 0, 0, 0, 0, 0) \
+ ) \
LAST_ENTRY(Scratch, 3, NO_SEED_MASKS)
#define PERFECT_HASH_HASH_FUNCTION_TABLE_ENTRY(ENTRY) \
diff --git a/include/PerfectHashErrors.h b/include/PerfectHashErrors.h
index d25d4416..94cccd84 100644
--- a/include/PerfectHashErrors.h
+++ b/include/PerfectHashErrors.h
@@ -248,6 +248,7 @@ Module Name:
// 18 ShiftMultiplyXorShift (3)
// 19 ShiftMultiplyXorShift2 (6)
// 20 RotateMultiplyXorRotate2 (6)
+// 21 MultiplyRotateR (3)
//
// N.B. The lowest latency hash functions with good solving ability, in order of
// ascending latency, are: Crc32RotateX, Crc32RotateXY, Crc32RotateWXYZ.
diff --git a/src/CompiledPerfectHashTable/CompiledPerfectHashTableChm01IndexMultiplyRotateRAnd.c b/src/CompiledPerfectHashTable/CompiledPerfectHashTableChm01IndexMultiplyRotateRAnd.c
new file mode 100644
index 00000000..54311acb
--- /dev/null
+++ b/src/CompiledPerfectHashTable/CompiledPerfectHashTableChm01IndexMultiplyRotateRAnd.c
@@ -0,0 +1,29 @@
+
+DECLARE_INDEX_ROUTINE()
+{
+ CPHINDEX Index;
+ CPHDKEY Vertex1;
+ CPHDKEY Vertex2;
+ CPHDKEY MaskedLow;
+ CPHDKEY MaskedHigh;
+ CPHDKEY DownsizedKey;
+
+ DownsizedKey = DOWNSIZE_KEY(Key);
+
+ Vertex1 = DownsizedKey * SEED1;
+ Vertex1 = _rotr(Vertex1, SEED3_BYTE1);
+
+ Vertex2 = DownsizedKey * SEED2;
+ Vertex2 = _rotr(Vertex2, SEED3_BYTE2);
+
+ MaskedLow = Vertex1 & HASH_MASK;
+ MaskedHigh = Vertex2 & HASH_MASK;
+
+ Vertex1 = TABLE_DATA[MaskedLow];
+ Vertex2 = TABLE_DATA[MaskedHigh];
+
+ Index = (CPHINDEX)((Vertex1 + Vertex2) & INDEX_MASK);
+
+ return Index;
+}
+
diff --git a/src/PerfectHash/CompiledPerfectHashTableChm01IndexMultiplyRotateRAnd_CSource_RawCString.h b/src/PerfectHash/CompiledPerfectHashTableChm01IndexMultiplyRotateRAnd_CSource_RawCString.h
new file mode 100644
index 00000000..e672fdcf
--- /dev/null
+++ b/src/PerfectHash/CompiledPerfectHashTableChm01IndexMultiplyRotateRAnd_CSource_RawCString.h
@@ -0,0 +1,59 @@
+//
+// Auto-generated.
+//
+
+DECLSPEC_ALIGN(16)
+const CHAR CompiledPerfectHashTableChm01IndexMultiplyRotateRAndCSourceRawCStr[] =
+ "\n"
+ "//\n"
+ "// Begin CompiledPerfectHashTableChm01IndexMultiplyRotateRAnd.c.\n"
+ "//\n"
+ "\n"
+ "\n"
+ "DECLARE_INDEX_ROUTINE()\n"
+ "{\n"
+ " CPHINDEX Index;\n"
+ " CPHDKEY Vertex1;\n"
+ " CPHDKEY Vertex2;\n"
+ " CPHDKEY MaskedLow;\n"
+ " CPHDKEY MaskedHigh;\n"
+ " CPHDKEY DownsizedKey;\n"
+ "\n"
+ " DownsizedKey = DOWNSIZE_KEY(Key);\n"
+ "\n"
+ " Vertex1 = DownsizedKey * SEED1;\n"
+ " Vertex1 = _rotr(Vertex1, SEED3_BYTE1);\n"
+ "\n"
+ " Vertex2 = DownsizedKey * SEED2;\n"
+ " Vertex2 = _rotr(Vertex2, SEED3_BYTE2);\n"
+ "\n"
+ " MaskedLow = Vertex1 & HASH_MASK;\n"
+ " MaskedHigh = Vertex2 & HASH_MASK;\n"
+ "\n"
+ " Vertex1 = TABLE_DATA[MaskedLow];\n"
+ " Vertex2 = TABLE_DATA[MaskedHigh];\n"
+ "\n"
+ " Index = (CPHINDEX)((Vertex1 + Vertex2) & INDEX_MASK);\n"
+ "\n"
+ " return Index;\n"
+ "}\n"
+ "\n"
+ "\n"
+ "//\n"
+ "// End CompiledPerfectHashTableChm01IndexMultiplyRotateRAnd.c.\n"
+ "//\n"
+ "\n"
+;
+
+const STRING CompiledPerfectHashTableChm01IndexMultiplyRotateRAndCSourceRawCString = {
+ sizeof(CompiledPerfectHashTableChm01IndexMultiplyRotateRAndCSourceRawCStr) - sizeof(CHAR),
+ sizeof(CompiledPerfectHashTableChm01IndexMultiplyRotateRAndCSourceRawCStr),
+#ifdef _WIN64
+ 0,
+#endif
+ (PCHAR)&CompiledPerfectHashTableChm01IndexMultiplyRotateRAndCSourceRawCStr,
+};
+
+#ifndef RawCString
+#define RawCString (&CompiledPerfectHashTableChm01IndexMultiplyRotateRAndCSourceRawCString)
+#endif
diff --git a/src/PerfectHash/CompiledPerfectHashTableIndexRoutines.h b/src/PerfectHash/CompiledPerfectHashTableIndexRoutines.h
index 2293c764..d3f67d17 100644
--- a/src/PerfectHash/CompiledPerfectHashTableIndexRoutines.h
+++ b/src/PerfectHash/CompiledPerfectHashTableIndexRoutines.h
@@ -18,6 +18,7 @@
#include "CompiledPerfectHashTableChm01IndexShiftMultiplyXorShiftAnd_CSource_RawCString.h"
#include "CompiledPerfectHashTableChm01IndexShiftMultiplyXorShift2And_CSource_RawCString.h"
#include "CompiledPerfectHashTableChm01IndexRotateMultiplyXorRotate2And_CSource_RawCString.h"
+#include "CompiledPerfectHashTableChm01IndexMultiplyRotateRAnd_CSource_RawCString.h"
//
// Keep this last.
diff --git a/src/PerfectHash/PerfectHash.vcxproj b/src/PerfectHash/PerfectHash.vcxproj
index abfb0e4d..b90e98f5 100644
--- a/src/PerfectHash/PerfectHash.vcxproj
+++ b/src/PerfectHash/PerfectHash.vcxproj
@@ -114,6 +114,7 @@
+
diff --git a/src/PerfectHash/PerfectHash.vcxproj.filters b/src/PerfectHash/PerfectHash.vcxproj.filters
index e957c8f4..33386ebe 100644
--- a/src/PerfectHash/PerfectHash.vcxproj.filters
+++ b/src/PerfectHash/PerfectHash.vcxproj.filters
@@ -477,6 +477,9 @@
Private Header Files %28Auto-Generated%29
+
+ Private Header Files %28Auto-Generated%29
+
Private Header Files
diff --git a/src/PerfectHash/PerfectHashErrors.mc b/src/PerfectHash/PerfectHashErrors.mc
index 8df9c6c9..eb538ad4 100644
--- a/src/PerfectHash/PerfectHashErrors.mc
+++ b/src/PerfectHash/PerfectHashErrors.mc
@@ -194,6 +194,7 @@ Hash Functions:
18 ShiftMultiplyXorShift (3)
19 ShiftMultiplyXorShift2 (6)
20 RotateMultiplyXorRotate2 (6)
+ 21 MultiplyRotateR (3)
N.B. The lowest latency hash functions with good solving ability, in order of
ascending latency, are: Crc32RotateX, Crc32RotateXY, Crc32RotateWXYZ.
diff --git a/src/PerfectHash/PerfectHashErrors_English.bin b/src/PerfectHash/PerfectHashErrors_English.bin
index 5e919119..6a7b65ac 100644
Binary files a/src/PerfectHash/PerfectHashErrors_English.bin and b/src/PerfectHash/PerfectHashErrors_English.bin differ
diff --git a/src/PerfectHash/PerfectHashTableHash.c b/src/PerfectHash/PerfectHashTableHash.c
index b64ccb0f..1b7b7e13 100644
--- a/src/PerfectHash/PerfectHashTableHash.c
+++ b/src/PerfectHash/PerfectHashTableHash.c
@@ -2286,4 +2286,100 @@ PerfectHashTableHashShiftMultiplyXorShift2(
);
}
+_Use_decl_annotations_
+HRESULT
+PerfectHashTableSeededHashMultiplyRotateR(
+ PPERFECT_HASH_TABLE Table,
+ ULONG Key,
+ ULONG NumberOfSeeds,
+ PULONG Seeds,
+ PULONGLONG Hash
+ )
+/*++
+
+Routine Description:
+
+ Performs a multiply then right rotate.
+
+Arguments:
+
+ Table - Supplies a pointer to the table for which the hash is being created.
+
+ Key - Supplies the input value to hash.
+
+ NumberOfSeeds - Supplies the number of elements in the Seeds array.
+
+ Seeds - Supplies an array of ULONG seed values.
+
+ Hash - Receives two 32-bit hashes merged into a 64-bit value.
+
+Return Value:
+
+ S_OK on success. If the two 32-bit hash values are identical, E_FAIL.
+
+--*/
+{
+ ULONG Seed1;
+ ULONG Seed2;
+ ULONG_BYTES Seed3;
+ ULONG Vertex1;
+ ULONG Vertex2;
+ ULONG DownsizedKey;
+ ULARGE_INTEGER Result;
+
+ UNREFERENCED_PARAMETER(Table);
+
+ ASSERT(NumberOfSeeds >= 3);
+ UNREFERENCED_PARAMETER(NumberOfSeeds);
+
+ //
+ // Initialize aliases.
+ //
+
+ Seed1 = Seeds[0];
+ Seed2 = Seeds[1];
+ Seed3.AsULong = Seeds[2];
+ DownsizedKey = Key;
+
+ //
+ // Calculate the individual hash parts.
+ //
+
+ Vertex1 = DownsizedKey * SEED1;
+ Vertex1 = _rotr(Vertex1, SEED3_BYTE1);
+
+ Vertex2 = DownsizedKey * SEED2;
+ Vertex2 = _rotr(Vertex2, SEED3_BYTE2);
+
+ if (Vertex1 == Vertex2) {
+ return E_FAIL;
+ }
+
+ Result.LowPart = Vertex1;
+ Result.HighPart = Vertex2;
+
+ *Hash = Result.QuadPart;
+
+ return S_OK;
+}
+
+_Use_decl_annotations_
+HRESULT
+PerfectHashTableHashMultiplyRotateR(
+ PPERFECT_HASH_TABLE Table,
+ ULONG Key,
+ PULONGLONG Hash
+ )
+{
+ PTABLE_INFO_ON_DISK TableInfo = Table->TableInfoOnDisk;
+
+ return PerfectHashTableSeededHashMultiplyRotateR(
+ Table,
+ Key,
+ TableInfo->NumberOfSeeds,
+ &TableInfo->FirstSeed,
+ Hash
+ );
+}
+
// vim:set ts=8 sw=4 sts=4 tw=80 expandtab :