mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Piggybacks some resilience diagnostics onto the availability checking code. Public and versioned functions with inlineable bodies can only reference other public and internal entities, since the SIL code for the function body is serialized and stored as part of the module. This includes @_transparent functions, @_inlineable functions, accessors for @_inlineable storage, @inline(__always) functions, and in Swift 4 mode, default argument expressions. The new checks are a source-breaking change, however we don't guarantee source compatibility for underscored attributes. The new ABI and tests for the default argument model will come in subsequent commits.
274 lines
7.9 KiB
Plaintext
274 lines
7.9 KiB
Plaintext
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
/// This file implements SipHash-2-4 and SipHash-1-3
|
|
/// (https://131002.net/siphash/).
|
|
///
|
|
/// This file is based on the reference C implementation, which was released
|
|
/// to public domain by:
|
|
///
|
|
/// * Jean-Philippe Aumasson <jeanphilippe.aumasson@gmail.com>
|
|
/// * Daniel J. Bernstein <djb@cr.yp.to>
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
@_versioned
|
|
internal enum _SipHashDetail {
|
|
@_versioned
|
|
@inline(__always)
|
|
internal static func _rotate(_ x: UInt64, leftBy amount: Int) -> UInt64 {
|
|
return (x << UInt64(amount)) | (x >> (64 - UInt64(amount)))
|
|
}
|
|
|
|
@_versioned
|
|
@inline(__always)
|
|
internal static func _loadUnalignedUInt64LE(
|
|
from p: UnsafeRawPointer
|
|
) -> UInt64 {
|
|
return
|
|
UInt64(p.load(fromByteOffset: 0, as: UInt8.self)) |
|
|
(UInt64(p.load(fromByteOffset: 1, as: UInt8.self)) << 8) |
|
|
(UInt64(p.load(fromByteOffset: 2, as: UInt8.self)) << 16) |
|
|
(UInt64(p.load(fromByteOffset: 3, as: UInt8.self)) << 24) |
|
|
(UInt64(p.load(fromByteOffset: 4, as: UInt8.self)) << 32) |
|
|
(UInt64(p.load(fromByteOffset: 5, as: UInt8.self)) << 40) |
|
|
(UInt64(p.load(fromByteOffset: 6, as: UInt8.self)) << 48) |
|
|
(UInt64(p.load(fromByteOffset: 7, as: UInt8.self)) << 56)
|
|
}
|
|
|
|
@_versioned
|
|
@inline(__always)
|
|
internal static func _loadPartialUnalignedUInt64LE(
|
|
from p: UnsafeRawPointer,
|
|
byteCount: Int
|
|
) -> UInt64 {
|
|
_sanityCheck((0..<8).contains(byteCount))
|
|
var result: UInt64 = 0
|
|
if byteCount >= 1 { result |= UInt64(p.load(fromByteOffset: 0, as: UInt8.self)) }
|
|
if byteCount >= 2 { result |= UInt64(p.load(fromByteOffset: 1, as: UInt8.self)) << 8 }
|
|
if byteCount >= 3 { result |= UInt64(p.load(fromByteOffset: 2, as: UInt8.self)) << 16 }
|
|
if byteCount >= 4 { result |= UInt64(p.load(fromByteOffset: 3, as: UInt8.self)) << 24 }
|
|
if byteCount >= 5 { result |= UInt64(p.load(fromByteOffset: 4, as: UInt8.self)) << 32 }
|
|
if byteCount >= 6 { result |= UInt64(p.load(fromByteOffset: 5, as: UInt8.self)) << 40 }
|
|
if byteCount >= 7 { result |= UInt64(p.load(fromByteOffset: 6, as: UInt8.self)) << 48 }
|
|
return result
|
|
}
|
|
|
|
@_versioned
|
|
@inline(__always)
|
|
internal static func _sipRound(
|
|
v0: inout UInt64,
|
|
v1: inout UInt64,
|
|
v2: inout UInt64,
|
|
v3: inout UInt64
|
|
) {
|
|
v0 = v0 &+ v1
|
|
v1 = _rotate(v1, leftBy: 13)
|
|
v1 ^= v0
|
|
v0 = _rotate(v0, leftBy: 32)
|
|
v2 = v2 &+ v3
|
|
v3 = _rotate(v3, leftBy: 16)
|
|
v3 ^= v2
|
|
v0 = v0 &+ v3
|
|
v3 = _rotate(v3, leftBy: 21)
|
|
v3 ^= v0
|
|
v2 = v2 &+ v1
|
|
v1 = _rotate(v1, leftBy: 17)
|
|
v1 ^= v2
|
|
v2 = _rotate(v2, leftBy: 32)
|
|
}
|
|
}
|
|
|
|
% for (c_rounds, d_rounds) in [(2, 4), (1, 3)]:
|
|
% Self = '_SipHash{}{}Context'.format(c_rounds, d_rounds)
|
|
|
|
public // @testable
|
|
struct ${Self} {
|
|
// "somepseudorandomlygeneratedbytes"
|
|
@_versioned
|
|
internal var v0: UInt64 = 0x736f6d6570736575
|
|
|
|
@_versioned
|
|
internal var v1: UInt64 = 0x646f72616e646f6d
|
|
|
|
@_versioned
|
|
internal var v2: UInt64 = 0x6c7967656e657261
|
|
|
|
@_versioned
|
|
internal var v3: UInt64 = 0x7465646279746573
|
|
|
|
@_versioned
|
|
internal var hashedByteCount: UInt64 = 0
|
|
|
|
@_versioned
|
|
internal var dataTail: UInt64 = 0
|
|
|
|
@_versioned
|
|
internal var dataTailByteCount: Int = 0
|
|
|
|
@_versioned
|
|
internal var finalizedHash: UInt64?
|
|
|
|
public init(key: (UInt64, UInt64)) {
|
|
v3 ^= key.1
|
|
v2 ^= key.0
|
|
v1 ^= key.1
|
|
v0 ^= key.0
|
|
}
|
|
|
|
// FIXME(ABI)#62 (UnsafeRawBufferPointer): Use UnsafeRawBufferPointer.
|
|
public // @testable
|
|
mutating func append(_ data: UnsafeRawPointer, byteCount: Int) {
|
|
_append_alwaysInline(data, byteCount: byteCount)
|
|
}
|
|
|
|
// FIXME(ABI)#63 (UnsafeRawBufferPointer): Use UnsafeRawBufferPointer.
|
|
@_versioned
|
|
@inline(__always)
|
|
internal mutating func _append_alwaysInline(
|
|
_ data: UnsafeRawPointer,
|
|
byteCount: Int
|
|
) {
|
|
precondition(finalizedHash == nil)
|
|
_sanityCheck((0..<8).contains(dataTailByteCount))
|
|
|
|
let dataEnd = data + byteCount
|
|
|
|
var data = data
|
|
var byteCount = byteCount
|
|
if dataTailByteCount != 0 {
|
|
let restByteCount = min(
|
|
MemoryLayout<UInt64>.size - dataTailByteCount,
|
|
byteCount)
|
|
let rest = _SipHashDetail._loadPartialUnalignedUInt64LE(
|
|
from: data,
|
|
byteCount: restByteCount)
|
|
dataTail |= rest << UInt64(dataTailByteCount * 8)
|
|
dataTailByteCount += restByteCount
|
|
data += restByteCount
|
|
byteCount -= restByteCount
|
|
}
|
|
|
|
if dataTailByteCount == MemoryLayout<UInt64>.size {
|
|
_appendDirectly(dataTail)
|
|
dataTail = 0
|
|
dataTailByteCount = 0
|
|
} else if dataTailByteCount != 0 {
|
|
_sanityCheck(data == dataEnd)
|
|
return
|
|
}
|
|
|
|
let endOfWords =
|
|
data + byteCount - (byteCount % MemoryLayout<UInt64>.size)
|
|
while data != endOfWords {
|
|
_appendDirectly(_SipHashDetail._loadUnalignedUInt64LE(from: data))
|
|
data += 8
|
|
// No need to update `byteCount`, it is not used beyond this point.
|
|
}
|
|
|
|
if data != dataEnd {
|
|
dataTailByteCount = dataEnd - data
|
|
dataTail = _SipHashDetail._loadPartialUnalignedUInt64LE(
|
|
from: data,
|
|
byteCount: dataTailByteCount)
|
|
}
|
|
}
|
|
|
|
/// This function mixes in the given word directly into the state,
|
|
/// ignoring `dataTail`.
|
|
@_versioned
|
|
@inline(__always)
|
|
internal mutating func _appendDirectly(_ m: UInt64) {
|
|
v3 ^= m
|
|
for _ in 0..<${c_rounds} {
|
|
_SipHashDetail._sipRound(v0: &v0, v1: &v1, v2: &v2, v3: &v3)
|
|
}
|
|
v0 ^= m
|
|
hashedByteCount += 8
|
|
}
|
|
|
|
% for data_type in ['UInt', 'Int', 'UInt64', 'Int64', 'UInt32', 'Int32']:
|
|
public // @testable
|
|
mutating func append(_ data: ${data_type}) {
|
|
var data = data
|
|
_append_alwaysInline(&data, byteCount: MemoryLayout.size(ofValue: data))
|
|
}
|
|
% end
|
|
|
|
public // @testable
|
|
mutating func finalizeAndReturnHash() -> UInt64 {
|
|
return _finalizeAndReturnHash_alwaysInline()
|
|
}
|
|
|
|
@_versioned
|
|
@inline(__always)
|
|
internal mutating func _finalizeAndReturnHash_alwaysInline() -> UInt64 {
|
|
if let finalizedHash = finalizedHash {
|
|
return finalizedHash
|
|
}
|
|
|
|
_sanityCheck((0..<8).contains(dataTailByteCount))
|
|
|
|
hashedByteCount += UInt64(dataTailByteCount)
|
|
let b: UInt64 = (hashedByteCount << 56) | dataTail
|
|
|
|
v3 ^= b
|
|
for _ in 0..<${c_rounds} {
|
|
_SipHashDetail._sipRound(v0: &v0, v1: &v1, v2: &v2, v3: &v3)
|
|
}
|
|
v0 ^= b
|
|
|
|
v2 ^= 0xff
|
|
|
|
for _ in 0..<${d_rounds} {
|
|
_SipHashDetail._sipRound(v0: &v0, v1: &v1, v2: &v2, v3: &v3)
|
|
}
|
|
|
|
finalizedHash = v0 ^ v1 ^ v2 ^ v3
|
|
return finalizedHash!
|
|
}
|
|
|
|
internal mutating func _finalizeAndReturnIntHash() -> Int {
|
|
let hash: UInt64 = finalizeAndReturnHash()
|
|
#if arch(i386) || arch(arm)
|
|
return Int(truncatingBitPattern: hash)
|
|
#elseif arch(x86_64) || arch(arm64) || arch(powerpc64) || arch(powerpc64le) || arch(s390x)
|
|
return Int(Int64(bitPattern: hash))
|
|
#endif
|
|
}
|
|
|
|
// FIXME(ABI)#64 (UnsafeRawBufferPointer): Use UnsafeRawBufferPointer.
|
|
public // @testable
|
|
static func hash(
|
|
data: UnsafeRawPointer,
|
|
dataByteCount: Int,
|
|
key: (UInt64, UInt64)
|
|
) -> UInt64 {
|
|
return ${Self}._hash_alwaysInline(
|
|
data: data,
|
|
dataByteCount: dataByteCount,
|
|
key: key)
|
|
}
|
|
|
|
// FIXME(ABI)#65 (UnsafeRawBufferPointer): Use UnsafeRawBufferPointer.
|
|
@inline(__always)
|
|
public // @testable
|
|
static func _hash_alwaysInline(
|
|
data: UnsafeRawPointer,
|
|
dataByteCount: Int,
|
|
key: (UInt64, UInt64)
|
|
) -> UInt64 {
|
|
var context = ${Self}(key: key)
|
|
context._append_alwaysInline(data, byteCount: dataByteCount)
|
|
return context._finalizeAndReturnHash_alwaysInline()
|
|
}
|
|
}
|
|
% end
|