diff --git a/stdlib/public/Darwin/Foundation/Data.swift b/stdlib/public/Darwin/Foundation/Data.swift index 81dc735cea18e..633cfe2bd7d35 100644 --- a/stdlib/public/Darwin/Foundation/Data.swift +++ b/stdlib/public/Darwin/Foundation/Data.swift @@ -1046,9 +1046,8 @@ public struct Data : ReferenceConvertible, Equatable, Hashable, RandomAccessColl func hash(into hasher: inout Hasher) { hasher.combine(count) - // At most, hash the first 80 bytes of this data. - let range = startIndex ..< Swift.min(startIndex + 80, endIndex) - storage.withUnsafeBytes(in: range) { + // To ensure strong hashing, all bytes must be fed into the hasher. + withUnsafeBytes { hasher.combine(bytes: $0) } } @@ -1250,9 +1249,8 @@ public struct Data : ReferenceConvertible, Equatable, Hashable, RandomAccessColl func hash(into hasher: inout Hasher) { hasher.combine(count) - // Hash at most the first 80 bytes of this data. - let range = startIndex ..< Swift.min(startIndex + 80, endIndex) - storage.withUnsafeBytes(in: range) { + // To ensure strong hashing, all bytes must be fed into the hasher. + withUnsafeBytes { hasher.combine(bytes: $0) } } diff --git a/test/stdlib/TestData.swift b/test/stdlib/TestData.swift index f55a2c9affae2..5790f799aea2d 100644 --- a/test/stdlib/TestData.swift +++ b/test/stdlib/TestData.swift @@ -974,6 +974,59 @@ class TestData : TestDataSuper { expectTrue(d.classForKeyedArchiver == expected) } + func test_Hashing() { + func bridgedData(_ bytes: [UInt8]) -> Data { + return bytes.withUnsafeBytes { buffer in + NSData(bytes: buffer.baseAddress, length: buffer.count) as Data + } + } + + let simpleTests: [[Data]] = [ + [ + Data(), + bridgedData([]), + ], + [ + Data([1]), + bridgedData([1]), + ], + [ + Data([1, 2]), + bridgedData([1, 2]), + ], + [ + Data([1, 2, 3]), + Data([1, 2, 3]), + bridgedData([1, 2, 3]), + bridgedData([1, 2, 3]), + ], + [ + Data([2, 1, 3]), + bridgedData([2, 1, 3]), + ], + ] + checkHashableGroups(simpleTests) + + // To ensure strong hashing, all bytes must be fed into the hasher. + let longTest: [Data] = [ + Data([1]) + Data(UInt8.min ... UInt8.max), + Data([2]) + Data(UInt8.min ... UInt8.max), + Data(UInt8.min ... UInt8.max) + Data([1]), + Data(UInt8.min ... UInt8.max) + Data([2]), + Data(UInt8.min ..< 128) + Data([1]) + Data(128 ... UInt8.max), + Data(UInt8.min ..< 128) + Data([2]) + Data(128 ... UInt8.max), + ] + checkHashable(longTest, equalityOracle: { $0 == $1 }) + + let concatenationTest: [[Data]] = [ + [Data([1, 2, 3]), Data()], + [Data([1, 2]), Data([3])], + [Data([1]), Data([2, 3])], + [Data(), Data([1, 2, 3])], + ] + checkHashable(concatenationTest, equalityOracle: { $0 == $1 }) + } + func test_AnyHashableContainingData() { let values: [Data] = [ Data(base64Encoded: "AAAA")!, @@ -3708,13 +3761,14 @@ class TestData : TestDataSuper { let base2 = Data(bytes: [0, 0xFF, 0xFF, 0]) let base3 = Data(bytes: [0xFF, 0xFF, 0xFF, 0]) let sliceEmulation = Data(bytes: [0xFF, 0xFF]) - expectEqual(base1.hashValue, base2.hashValue) let slice1 = base1[base1.startIndex.advanced(by: 1)..