diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7364c98c..15e336f9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,18 +21,14 @@ jobs: name: soundness uses: swiftlang/github-workflows/.github/workflows/soundness.yml@main with: - # https://github.com/swiftlang/swift-package-manager/issues/8103 - api_breakage_check_enabled: false - # swift:6.2-noble leads to issues with Snippets - # e.g. https://github.com/apple/swift-homomorphic-encryption/actions/runs/18144503507/job/51643132814#step:5:1087 - docs_check_container_image: swift:6.1-noble + docs_check_container_image: swift:6.2.2-noble format_check_enabled: false tests: name: swifttests uses: swiftlang/github-workflows/.github/workflows/swift_package_test.yml@main with: enable_windows_checks: false - linux_exclude_swift_versions: "[{\"swift_version\": \"5.9\"}, {\"swift_version\": \"5.10\"}]" + linux_exclude_swift_versions: "[{\"swift_version\": \"5.9\"}, {\"swift_version\": \"5.10\"}, {\"swift_version\": \"6.0\"}, {\"swift_version\": \"6.1\"}]" linux_env_vars: SWIFT_HOMOMORPHIC_ENCRYPTION_ENABLE_BENCHMARKING=1 linux_pre_build_command: "apt-get update && apt-get install -y libjemalloc-dev" linux_build_command: > diff --git a/.spi.yml b/.spi.yml index f126f899..7fa6cbcb 100644 --- a/.spi.yml +++ b/.spi.yml @@ -12,4 +12,4 @@ builder: - PNNSProcessDatabase - PrivateInformationRetrieval - PrivateNearestNeighborSearch - swift_version: 6.0 + swift_version: 6.2 diff --git a/.swiftformat b/.swiftformat index a467a0e2..52013e25 100644 --- a/.swiftformat +++ b/.swiftformat @@ -8,4 +8,4 @@ --nospaceoperators ..<, ... --ranges no-space --self init-only ---swiftversion 6.0 +--swiftversion 6.2 diff --git a/Package.swift b/Package.swift index a248c7bf..7aa8b680 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version: 6.0 +// swift-tools-version: 6.2 // The swift-tools-version declares the minimum version of Swift required to build this package. // Remember to update CI if changing @@ -108,12 +108,13 @@ let package = Package( "CUtil", "ModularArithmetic", ], + exclude: ["HomomorphicEncryption.docc"], swiftSettings: librarySettings), .target( name: "HomomorphicEncryptionProtobuf", dependencies: ["HomomorphicEncryption", .product(name: "SwiftProtobuf", package: "swift-protobuf")], - exclude: ["generated/README.md"], + exclude: ["generated/README.md", "HomomorphicEncryptionProtobuf.docc"], swiftSettings: librarySettings), .target( name: "_HomomorphicEncryptionExtras", @@ -124,6 +125,7 @@ let package = Package( dependencies: ["HomomorphicEncryption", .product(name: "AsyncAlgorithms", package: "swift-async-algorithms"), .product(name: "Numerics", package: "swift-numerics")], + exclude: ["PrivateInformationRetrieval.docc"], swiftSettings: librarySettings), .target( name: "PrivateNearestNeighborSearch", @@ -133,6 +135,7 @@ let package = Package( "HomomorphicEncryption", "_HomomorphicEncryptionExtras", ], + exclude: ["PrivateNearestNeighborSearch.docc"], swiftSettings: librarySettings), .target( name: "ApplicationProtobuf", @@ -140,7 +143,7 @@ let package = Package( "PrivateInformationRetrieval", "PrivateNearestNeighborSearch", .product(name: "SwiftProtobuf", package: "swift-protobuf")], - exclude: ["generated/README.md", "protobuf_module_mappings.txtpb"], + exclude: ["ApplicationProtobuf.docc", "generated/README.md", "protobuf_module_mappings.txtpb"], swiftSettings: librarySettings), .target( name: "_TestUtilities", @@ -159,6 +162,7 @@ let package = Package( "HomomorphicEncryption", "ApplicationProtobuf", ], + exclude: ["PIRGenerateDatabase.docc"], swiftSettings: executableSettings), .executableTarget( name: "PIRProcessDatabase", @@ -169,6 +173,7 @@ let package = Package( "HomomorphicEncryption", .product(name: "Logging", package: "swift-log"), ], + exclude: ["PIRProcessDatabase.docc"], swiftSettings: executableSettings), .executableTarget( name: "PIRShardDatabase", @@ -177,6 +182,7 @@ let package = Package( "HomomorphicEncryption", "ApplicationProtobuf", ], + exclude: ["PIRShardDatabase.docc"], swiftSettings: executableSettings), .executableTarget( name: "PNNSGenerateDatabase", @@ -185,6 +191,7 @@ let package = Package( "HomomorphicEncryption", "ApplicationProtobuf", ], + exclude: ["PNNSGenerateDatabase.docc"], swiftSettings: executableSettings), .executableTarget( name: "PNNSProcessDatabase", @@ -195,6 +202,7 @@ let package = Package( "HomomorphicEncryption", .product(name: "Logging", package: "swift-log"), ], + exclude: ["PNNSProcessDatabase.docc"], swiftSettings: executableSettings), .testTarget( name: "HomomorphicEncryptionTests", @@ -342,6 +350,6 @@ if enableDocCPlugin { // Set the minimum macOS version for the package #if canImport(Darwin) package.platforms = [ - .macOS(.v15), // Constrained by UInt128 support + .macOS(.v26), // Constrained by use of Span ] #endif diff --git a/README.md b/README.md index 5c649e9e..0d689a22 100644 --- a/README.md +++ b/README.md @@ -136,7 +136,7 @@ The following table maps Swift Homomorphic Encryption package versions to requir Package version | Swift version | Xcode version ----------------|---------------|----------------------------------------- 1.0.x | >= Swift 5.10 | >= Xcode 15.3 -main | >= Swift 6.0 | >= Xcode 16.1 +main | >= Swift 6.2 | >= Xcode 26 ### Source Stability Swift Homomorphic Encryption follows [Semantic Versioning 2.0.0](https://semver.org/spec/v2.0.0.html). Source breaking changes to the public API can only land in a new major version, with the following exception: @@ -155,7 +155,7 @@ We'd like this package to quickly embrace Swift language and toolchain improveme Developing Swift Homomorphic Encryption requires: * [Nick Lockwood SwiftFormat](https://github.com/nicklockwood/SwiftFormat), 0.58.6 * [pre-commit](https://pre-commit.com) -* [swift-format](https://github.com/swiftlang/swift-format), 600.0.0 +* [swift-format](https://github.com/swiftlang/swift-format), 602.0.0 * [swift-protobuf](https://github.com/apple/swift-protobuf), 1.31.1 * [SwiftLint](https://github.com/realm/SwiftLint), 0.62.2 diff --git a/Sources/HomomorphicEncryption/Array2d.swift b/Sources/HomomorphicEncryption/Array2d.swift index cc9eb2b5..5dcf9962 100644 --- a/Sources/HomomorphicEncryption/Array2d.swift +++ b/Sources/HomomorphicEncryption/Array2d.swift @@ -76,36 +76,31 @@ public struct Array2d: Equatable, columnCount: columnCount) } - /// Provides scoped access to the underlying buffer storing the array's data. + /// Provides scoped access to the underlying buffer storing the array's data using a Span. /// /// Use this method when you need temporary read-only access to the array's contiguous storage. - /// The buffer pointer is only valid for the duration of the closure's execution. /// - /// - Parameter body: A closure that takes an `UnsafeBufferPointer` to the array's data. - /// The buffer pointer argument is valid only for the duration of the closure's execution. + /// - Parameter body: A closure that takes a `Span` to the array's data. /// - Returns: The return value of the `body` closure. /// - Throws: Rethrows any error thrown by the `body` closure. - public func withUnsafeData(_ body: (UnsafeBufferPointer) throws -> Return) rethrows -> Return { - try data.withUnsafeBufferPointer { pointer in - try body(pointer) - } + @inlinable + public func withDataSpan(_ body: (Span) throws -> Return) rethrows -> Return { + try body(data.span) } /// Provides scoped access to the underlying buffer storing the array's data for mutation. /// /// Use this method when you need temporary read-write access to the array's contiguous storage. - /// The buffer pointer is only valid for the duration of the closure's execution. /// - /// - Parameter body: A closure that takes an `UnsafeMutableBufferPointer` to the array's data. - /// The buffer pointer argument is valid only for the duration of the closure's execution. + /// - Parameter body: A closure that takes a `MutableSpan` to the array's data. /// - Returns: The return value of the `body` closure. /// - Throws: Rethrows any error thrown by the `body` closure. - public mutating func withUnsafeMutableData(_ body: (UnsafeMutableBufferPointer) throws - -> Return) rethrows -> Return + @inlinable + public mutating func withMutableDataSpan(_ body: (inout MutableSpan) throws -> Return) rethrows + -> Return { - try data.withUnsafeMutableBufferPointer { pointer in - try body(pointer) - } + var span = data.mutableSpan + return try body(&span) } } diff --git a/Sources/HomomorphicEncryption/Bfv/Bfv+Encrypt.swift b/Sources/HomomorphicEncryption/Bfv/Bfv+Encrypt.swift index 37310ea0..bf2a959a 100644 --- a/Sources/HomomorphicEncryption/Bfv/Bfv+Encrypt.swift +++ b/Sources/HomomorphicEncryption/Bfv/Bfv+Encrypt.swift @@ -85,61 +85,57 @@ extension Bfv { let plaintextIndices = plaintext.poly.polyIndices(rnsIndex: 0) var adjust = plaintext - // swiftlint:disable:next closure_body_length - plaintext.poly.data.data.withUnsafeBufferPointer { plaintextData in - adjust.poly.data.data.withUnsafeMutableBufferPointer { adjustPtr in - let t = rnsTool.t.modulus - if T.DoubleWidth(t.multipliedFullWidth(by: t)) <= T.max { - // Prefer single-word division, since it's faster - for index in plaintextIndices { - var adjustCoeff = rnsTool.qModT &* plaintextData[index] - adjustCoeff &+= rnsTool.tThreshold - adjustPtr[index] = adjustCoeff.dividingFloor(by: rnsTool.t) - } - } else { - let tThreshold = Scalar.DoubleWidth(rnsTool.tThreshold) - for index in plaintextIndices { - var adjustCoeff = Scalar - .DoubleWidth(rnsTool.qModT.multipliedFullWidth(by: plaintextData[index])) - adjustCoeff &+= tThreshold - adjustPtr[index] = adjustCoeff.dividingFloor(by: rnsTool.t).low - } - } + + let plaintextData = plaintext.poly.data.data.span + var adjustSpan = adjust.poly.data.data.mutableSpan + let t = rnsTool.t.modulus + if T.DoubleWidth(t.multipliedFullWidth(by: t)) <= T.max { + // Prefer single-word division, since it's faster + for index in plaintextIndices { + var adjustCoeff = rnsTool.qModT &* plaintextData[index] + adjustCoeff &+= rnsTool.tThreshold + adjustSpan[index] = adjustCoeff.dividingFloor(by: rnsTool.t) + } + } else { + let tThreshold = Scalar.DoubleWidth(rnsTool.tThreshold) + for index in plaintextIndices { + var adjustCoeff = Scalar + .DoubleWidth(rnsTool.qModT.multipliedFullWidth(by: plaintextData[index])) + adjustCoeff &+= tThreshold + adjustSpan[index] = adjustCoeff.dividingFloor(by: rnsTool.t).low } + } - var c0 = ciphertext.polys[0] - let c1 = ciphertext.polys[1] // used for polynomial index computation only - - switch op { - case PlaintextTranslateOp.Add: - c0.data.data.withUnsafeMutableBufferPointer { c0Ptr in - for (rnsIndex, rnsDelta) in rnsTool.qDivT.enumerated() { - for (plainIndex, cipherIndex) in c1.polyIndices(rnsIndex: rnsIndex).enumerated() { - let plainTimesDelta = rnsDelta.multiplyMod(plaintextData[plainIndex]) - let roundQTimesMt = plainTimesDelta.addMod( - adjust[plainIndex], - modulus: rnsDelta.modulus) - c0Ptr[cipherIndex] = c0Ptr[cipherIndex].addMod(roundQTimesMt, modulus: rnsDelta.modulus) - } - } + var c0 = ciphertext.polys[0] + let c1 = ciphertext.polys[1] // used for polynomial index computation only + + switch op { + case PlaintextTranslateOp.Add: + var c0Span = c0.data.data.mutableSpan + for (rnsIndex, rnsDelta) in rnsTool.qDivT.enumerated() { + for (plainIndex, cipherIndex) in c1.polyIndices(rnsIndex: rnsIndex).enumerated() { + let plainTimesDelta = rnsDelta.multiplyMod(plaintextData[plainIndex]) + let roundQTimesMt = plainTimesDelta.addMod( + adjust[plainIndex], + modulus: rnsDelta.modulus) + c0Span[cipherIndex] = c0Span[cipherIndex].addMod(roundQTimesMt, modulus: rnsDelta.modulus) } - case PlaintextTranslateOp.Subtract: - c0.data.data.withUnsafeMutableBufferPointer { c0Ptr in - for (rnsIndex, rnsDelta) in rnsTool.qDivT.enumerated() { - for (plainIndex, cipherIndex) in c1.polyIndices(rnsIndex: rnsIndex).enumerated() { - let plainTimesDelta = rnsDelta.multiplyMod(plaintextData[plainIndex]) - let roundQTimesMt = plainTimesDelta.addMod( - adjust[plainIndex], - modulus: rnsDelta.modulus) - c0Ptr[cipherIndex] = c0Ptr[cipherIndex].subtractMod( - roundQTimesMt, - modulus: rnsDelta.modulus) - } - } + } + case PlaintextTranslateOp.Subtract: + var c0Span = c0.data.data.mutableSpan + for (rnsIndex, rnsDelta) in rnsTool.qDivT.enumerated() { + for (plainIndex, cipherIndex) in c1.polyIndices(rnsIndex: rnsIndex).enumerated() { + let plainTimesDelta = rnsDelta.multiplyMod(plaintextData[plainIndex]) + let roundQTimesMt = plainTimesDelta.addMod( + adjust[plainIndex], + modulus: rnsDelta.modulus) + c0Span[cipherIndex] = c0Span[cipherIndex].subtractMod( + roundQTimesMt, + modulus: rnsDelta.modulus) } } - ciphertext.polys[0] = c0 } + ciphertext.polys[0] = c0 } @inlinable diff --git a/Sources/HomomorphicEncryption/Bfv/Bfv+Keys.swift b/Sources/HomomorphicEncryption/Bfv/Bfv+Keys.swift index 3c4d0b77..5125eaae 100644 --- a/Sources/HomomorphicEncryption/Bfv/Bfv+Keys.swift +++ b/Sources/HomomorphicEncryption/Bfv/Bfv+Keys.swift @@ -179,25 +179,23 @@ extension Bfv { for (index, poly) in keyCiphers[decomposeIndex].polys.enumerated() { let accIndex = poly.data.index(row: index, column: 0) let polyIndex = poly.data.index(row: keyIndex, column: 0) - poly.data.data.withUnsafeBufferPointer { polyPtr in - for columnIndex in 0..: HeScheme { { let poly = result.polys[0] for (polyIndex, accumulatorPoly) in accumulator.enumerated() { - accumulatorPoly.data.withUnsafeBufferPointer { accumulatorPolyPtr in - result.polys[polyIndex].data.data.withUnsafeMutableBufferPointer { resultPtr in - for (rnsIndex, modulus) in poly.polyContext().reduceModuli.enumerated() { - for index in poly.polyIndices(rnsIndex: rnsIndex) { - resultPtr[index] = modulus.reduce(accumulatorPolyPtr[index]) - } - } + let accumulatorPolySpan = accumulatorPoly.data.span + var resultSpan = result.polys[polyIndex].data.data.mutableSpan + for (rnsIndex, modulus) in poly.polyContext().reduceModuli.enumerated() { + for index in poly.polyIndices(rnsIndex: rnsIndex) { + resultSpan[index] = modulus.reduce(accumulatorPolySpan[index]) } } } diff --git a/Sources/HomomorphicEncryption/CrtComposer.swift b/Sources/HomomorphicEncryption/CrtComposer.swift index 570abcdb..f36460b0 100644 --- a/Sources/HomomorphicEncryption/CrtComposer.swift +++ b/Sources/HomomorphicEncryption/CrtComposer.swift @@ -87,7 +87,7 @@ public struct _CrtComposer: Sendable { for column in 0..= Swift 5.10 | >= Xcode 15.3 -main | >= Swift 6.0 | >= Xcode 16.1 +main | >= Swift 6.2 | >= Xcode 26 ### Source Stability Swift Homomorphic Encryption follows [Semantic Versioning 2.0.0](https://semver.org/spec/v2.0.0.html). Source breaking changes to the public API can only land in a new major version, with the following exception: diff --git a/Sources/HomomorphicEncryption/PolyRq/Galois.swift b/Sources/HomomorphicEncryption/PolyRq/Galois.swift index 636a39c0..e155cfbb 100644 --- a/Sources/HomomorphicEncryption/PolyRq/Galois.swift +++ b/Sources/HomomorphicEncryption/PolyRq/Galois.swift @@ -121,19 +121,17 @@ extension PolyRq where F == Coeff { func outputIndex(column: Int) -> Int { data.index(row: rnsIndex, column: column) } - data.data.withUnsafeBufferPointer { dataPtr in - output.data.data.withUnsafeMutableBufferPointer { outputPtr in - for dataIndex in dataIndices { - guard let (negate, outIndex) = iterator.next() else { - preconditionFailure("GaloisCoeffIterator goes out of index") - } - if negate { - outputPtr[outputIndex(column: outIndex)] = dataPtr[dataIndex] - .negateMod(modulus: modulus) - } else { - outputPtr[outputIndex(column: outIndex)] = dataPtr[dataIndex] - } - } + let dataSpan = data.data.span + var outputSpan = output.data.data.mutableSpan + for dataIndex in dataIndices { + guard let (negate, outIndex) = iterator.next() else { + preconditionFailure("GaloisCoeffIterator goes out of index") + } + if negate { + outputSpan[outputIndex(column: outIndex)] = dataSpan[dataIndex] + .negateMod(modulus: modulus) + } else { + outputSpan[outputIndex(column: outIndex)] = dataSpan[dataIndex] } } } diff --git a/Sources/HomomorphicEncryption/PolyRq/PolyRq.swift b/Sources/HomomorphicEncryption/PolyRq/PolyRq.swift index 6ce8280c..6779f2de 100644 --- a/Sources/HomomorphicEncryption/PolyRq/PolyRq.swift +++ b/Sources/HomomorphicEncryption/PolyRq/PolyRq.swift @@ -147,13 +147,11 @@ extension PolyRq { public static func += (_ lhs: inout Self, _ rhs: Self) { lhs.validateMetadataEquality(with: rhs) - lhs.data.data.withUnsafeMutableBufferPointer { lhsData in - rhs.data.data.withUnsafeBufferPointer { rhsData in - for (rnsIndex, modulus) in rhs.moduli.enumerated() { - for index in rhs.polyIndices(rnsIndex: rnsIndex) { - lhsData[index] = lhsData[index].addMod(rhsData[index], modulus: modulus) - } - } + var lhsData = lhs.data.data.mutableSpan + let rhsData = rhs.data.data.span + for (rnsIndex, modulus) in rhs.moduli.enumerated() { + for index in rhs.polyIndices(rnsIndex: rnsIndex) { + lhsData[index] = lhsData[index].addMod(rhsData[index], modulus: modulus) } } } @@ -166,13 +164,11 @@ extension PolyRq { public static func -= (_ lhs: inout Self, _ rhs: Self) { lhs.validateMetadataEquality(with: rhs) - lhs.data.data.withUnsafeMutableBufferPointer { lhsData in - rhs.data.data.withUnsafeBufferPointer { rhsData in - for (rnsIndex, modulus) in rhs.moduli.enumerated() { - for index in rhs.polyIndices(rnsIndex: rnsIndex) { - lhsData[index] = lhsData[index].subtractMod(rhsData[index], modulus: modulus) - } - } + var lhsData = lhs.data.data.mutableSpan + let rhsData = rhs.data.data.span + for (rnsIndex, modulus) in rhs.moduli.enumerated() { + for index in rhs.polyIndices(rnsIndex: rnsIndex) { + lhsData[index] = lhsData[index].subtractMod(rhsData[index], modulus: modulus) } } } @@ -188,13 +184,11 @@ extension PolyRq { static func mulAssign(_ lhs: inout Self, secretPoly: borrowing Self) where F == Eval { let context = lhs.context assert(secretPoly.context.isParentOfOrEqual(to: context)) - lhs.data.data.withUnsafeMutableBufferPointer { lhsData in - secretPoly.data.data.withUnsafeBufferPointer { rhsData in - for (rnsIndex, modulus) in context.reduceModuli.enumerated() { - for index in secretPoly.polyIndices(rnsIndex: rnsIndex) { - lhsData[index] = modulus.multiplyMod(lhsData[index], rhsData[index]) - } - } + var lhsData = lhs.data.data.mutableSpan + let rhsData = secretPoly.data.data.span + for (rnsIndex, modulus) in context.reduceModuli.enumerated() { + for index in secretPoly.polyIndices(rnsIndex: rnsIndex) { + lhsData[index] = modulus.multiplyMod(lhsData[index], rhsData[index]) } } } @@ -219,16 +213,13 @@ extension PolyRq { precondition(accumulator.shape == rhs.data.shape) lhs.validateMetadataEquality(with: rhs) - lhs.data.data.withUnsafeBufferPointer { lhsData in - rhs.data.data.withUnsafeBufferPointer { rhsData in - accumulator.data.withUnsafeMutableBufferPointer { accumulatorPtr in - for rnsIndex in rhs.moduli.indices { - for index in rhs.polyIndices(rnsIndex: rnsIndex) { - accumulatorPtr[index] &+= - T.DoubleWidth(lhsData[index].multipliedFullWidth(by: rhsData[index])) - } - } - } + let lhsData = lhs.data.data.span + let rhsData = rhs.data.data.span + var accumulatorSpan = accumulator.data.mutableSpan + for rnsIndex in rhs.moduli.indices { + for index in rhs.polyIndices(rnsIndex: rnsIndex) { + accumulatorSpan[index] &+= + T.DoubleWidth(lhsData[index].multipliedFullWidth(by: rhsData[index])) } } } @@ -246,10 +237,9 @@ extension PolyRq { multiplicand: rhsResidue, divisionModulus: modulus.divisionModulus) let polyIndices = poly.polyIndices(rnsIndex: rnsIndex) - poly.data.data.withUnsafeMutableBufferPointer { lhsData in - for index in polyIndices { - lhsData[index] = multiplicationModulus.multiplyMod(lhsData[index]) - } + var lhsData = poly.data.data.mutableSpan + for index in polyIndices { + lhsData[index] = multiplicationModulus.multiplyMod(lhsData[index]) } } } @@ -308,16 +298,13 @@ extension PolyRq { @inlinable public static prefix func - (_ poly: Self) -> Self { var result = Self.zero(context: poly.context) - result.data.data.withUnsafeMutableBufferPointer { resultData in - poly.data.data.withUnsafeBufferPointer { rhsData in - for (rnsIndex, modulus) in poly.moduli.enumerated() { - for index in poly.polyIndices(rnsIndex: rnsIndex) { - resultData[index] = rhsData[index].negateMod(modulus: modulus) - } - } + var resultData = result.data.data.mutableSpan + let rhsData = poly.data.data.span + for (rnsIndex, modulus) in poly.moduli.enumerated() { + for index in poly.polyIndices(rnsIndex: rnsIndex) { + resultData[index] = rhsData[index].negateMod(modulus: modulus) } } - return result } diff --git a/Sources/HomomorphicEncryption/RnsBaseConverter.swift b/Sources/HomomorphicEncryption/RnsBaseConverter.swift index 094b0336..59e1d118 100644 --- a/Sources/HomomorphicEncryption/RnsBaseConverter.swift +++ b/Sources/HomomorphicEncryption/RnsBaseConverter.swift @@ -98,10 +98,9 @@ public struct _RnsBaseConverter: Sendable { assert(poly.context == inputContext) for (rnsIndex, puncturedProduct) in inversePuncturedProducts.enumerated() { let indices = poly.polyIndices(rnsIndex: rnsIndex) - poly.data.data.withUnsafeMutableBufferPointer { dataPtr in - for index in indices { - dataPtr[index] = puncturedProduct.multiplyMod(dataPtr[index]) - } + var dataSpan = poly.data.data.mutableSpan + for index in indices { + dataSpan[index] = puncturedProduct.multiplyMod(dataSpan[index]) } } } @@ -117,26 +116,25 @@ public struct _RnsBaseConverter: Sendable { @inlinable func convertApproximate(using products: PolyRq) -> PolyRq { var result = PolyRq.zero(context: outputContext) - result.data.data.withUnsafeMutableBufferPointer { resultPtr in - for (rnsOutIndex, tj) in outputContext.reduceModuli.enumerated() { - let puncturedProductColumnIndices = puncturedProducts.rowIndices(row: rnsOutIndex) - var sums = Array(repeating: T.DoubleWidth(0), count: outputContext.degree) - var productsIndex = 0 - for (rnsInIndex, puncturedProdIdx) in puncturedProductColumnIndices.enumerated() { - let puncturedProd = puncturedProducts[puncturedProdIdx] - if rnsInIndex == inputContext.moduli.count &- 1 { - for (coeffIndex, outIndex) in products.polyIndices(rnsIndex: rnsOutIndex).enumerated() { - sums[coeffIndex] &+= - T.DoubleWidth(products.data[productsIndex].multipliedFullWidth(by: puncturedProd)) - resultPtr[outIndex] = tj.reduce(sums[coeffIndex]) - productsIndex &+= 1 - } - } else { - for coeffIndex in 0..: Sendable { var polyModQ = poly try polyModQ.dropContext(to: inputContext) var output = try rnsConvertQToBSk.convertApproximate(poly: polyModQ) - polyModBSk.withUnsafeBufferPointer { polyModBSkPtr in - for (rnsIndex, inverseQModBSk) in inverseQModBSk.enumerated() { - let bSk = rnsConvertQToBSk.outputContext.moduli[rnsIndex] - let outputIndices = output.polyIndices(rnsIndex: rnsIndex) - output.data.data.withUnsafeMutableBufferPointer { outputPtr in - for polyIndex in outputIndices { - let inputCoeff = polyModBSkPtr[polyIndex] - let outputCoeff = outputPtr[polyIndex] - outputPtr[polyIndex] = inverseQModBSk.multiplyMod(inputCoeff &+ bSk &- outputCoeff) - } - } + let polyModBSkSpan = polyModBSk.span + for (rnsIndex, inverseQModBSk) in inverseQModBSk.enumerated() { + let bSk = rnsConvertQToBSk.outputContext.moduli[rnsIndex] + let outputIndices = output.polyIndices(rnsIndex: rnsIndex) + var outputSpan = output.data.data.mutableSpan + for polyIndex in outputIndices { + let inputCoeff = polyModBSkSpan[polyIndex] + let outputCoeff = outputSpan[polyIndex] + outputSpan[polyIndex] = inverseQModBSk.multiplyMod(inputCoeff &+ bSk &- outputCoeff) } } return output @@ -422,34 +420,29 @@ public struct _RnsTool: Sendable { let coeffIndices = alphaSk.coeffIndices var alphaExceedsThreshold = [T]() alphaExceedsThreshold.reserveCapacity(coeffIndices.count) - alphaSk.data.data.withUnsafeMutableBufferPointer { alphaSkPtr in - for coeffIndex in coeffIndices { - let polyModMSkCoeff = polyModMSk[coeffIndex] - var alphaSk = alphaSkPtr[coeffIndex] - alphaSk = inverseBModMSk - .multiplyMod(alphaSk &+ inverseBModMSk.modulus &- polyModMSkCoeff) - alphaSkPtr[coeffIndex] = alphaSk - alphaExceedsThreshold.append(alphaSk.constantTimeGreaterThan(mSkThreshold)) - } + var alphaSkSpan = alphaSk.data.data.mutableSpan + for coeffIndex in coeffIndices { + let polyModMSkCoeff = polyModMSk[coeffIndex] + var alphaSk = alphaSkSpan[coeffIndex] + alphaSk = inverseBModMSk + .multiplyMod(alphaSk &+ inverseBModMSk.modulus &- polyModMSkCoeff) + alphaSkSpan[coeffIndex] = alphaSk + alphaExceedsThreshold.append(alphaSk.constantTimeGreaterThan(mSkThreshold)) } var output = rnsConvertBtoQ.convertApproximate(using: polyModB) - output.data.data.withUnsafeMutableBufferPointer { outputPtr in - alphaSk.data.data.withUnsafeBufferPointer { alphaSkPtr in - alphaExceedsThreshold.withUnsafeBufferPointer { alphaExceedsThresholdPtr in - for (rnsIndex, (qi, (bModQi, negBModQi))) in zip(inputContext.moduli, zip(bModQ, negBModQ)) - .enumerated() - { - for (coeffIndex, outputIndex) in polyModB.polyIndices(rnsIndex: rnsIndex).enumerated() { - // Center alphaSk before Shenoy-Kumeresan conversion - let adjust = T.constantTimeSelect( - if: alphaExceedsThresholdPtr[coeffIndex], - then: bModQi.multiplyMod(mSk &- alphaSkPtr[coeffIndex]), - else: negBModQi.multiplyMod(alphaSkPtr[coeffIndex])) - outputPtr[outputIndex] = outputPtr[outputIndex].addMod(adjust, modulus: qi) - } - } - } + var outputSpan = output.data.data.mutableSpan + let alphaExceedsThresholdSpan = alphaExceedsThreshold.span + for (rnsIndex, (qi, (bModQi, negBModQi))) in zip(inputContext.moduli, zip(bModQ, negBModQ)) + .enumerated() + { + for (coeffIndex, outputIndex) in polyModB.polyIndices(rnsIndex: rnsIndex).enumerated() { + // Center alphaSk before Shenoy-Kumeresan conversion + let adjust = T.constantTimeSelect( + if: alphaExceedsThresholdSpan[coeffIndex], + then: bModQi.multiplyMod(mSk &- alphaSkSpan[coeffIndex]), + else: negBModQi.multiplyMod(alphaSkSpan[coeffIndex])) + outputSpan[outputIndex] = outputSpan[outputIndex].addMod(adjust, modulus: qi) } } diff --git a/Sources/ModularArithmetic/Modulus.swift b/Sources/ModularArithmetic/Modulus.swift index 7eee7897..b3994a36 100644 --- a/Sources/ModularArithmetic/Modulus.swift +++ b/Sources/ModularArithmetic/Modulus.swift @@ -433,13 +433,11 @@ public struct MultiplyConstantArrayModulus: Equatable, Sendab @inlinable public subscript(index: Int) -> MultiplyConstantModulus { - factors.withUnsafeBufferPointer { factorPtr in - multiplicands.withUnsafeBufferPointer { multiplicandsPtr in - MultiplyConstantModulus( - multiplicand: multiplicandsPtr[index], - modulus: modulus, - factor: factorPtr[index]) - } - } + let factorSpan = factors.span + let multiplicandsSpan = multiplicands.span + return MultiplyConstantModulus( + multiplicand: multiplicandsSpan[index], + modulus: modulus, + factor: factorSpan[index]) } } diff --git a/Sources/PIRProcessDatabase/main.swift b/Sources/PIRProcessDatabase/main.swift index e2e566aa..27d210c5 100644 --- a/Sources/PIRProcessDatabase/main.swift +++ b/Sources/PIRProcessDatabase/main.swift @@ -434,7 +434,7 @@ struct ProcessDatabase: AsyncParsableCommand { var evaluationKeyConfig = EvaluationKeyConfig() if parallel { - try await withThrowingTaskGroup(of: EvaluationKeyConfig.self) { group in + try await withThrowingTaskGroup { group in for (shardID, shard) in shards { group.addTask { @Sendable [self] in try await processShard( diff --git a/Tests/HomomorphicEncryptionTests/Array2dTests.swift b/Tests/HomomorphicEncryptionTests/Array2dTests.swift index f33714e7..78774ba0 100644 --- a/Tests/HomomorphicEncryptionTests/Array2dTests.swift +++ b/Tests/HomomorphicEncryptionTests/Array2dTests.swift @@ -186,29 +186,27 @@ struct Array2dTests { } @Test - func withUnsafeData() { + func withDataSpan() { let data = [Int](0..<32) let array = Array2d(data: data, rowCount: 4, columnCount: 8) - array.withUnsafeData { dataPointer in - array.data.withUnsafeBufferPointer { expectedDataPointer in - #expect(dataPointer.baseAddress == expectedDataPointer.baseAddress) + var sum = 0 + array.withDataSpan { span in + for index in span.indices { + sum += span[index] } } + #expect(sum == array.data.sum()) } @Test - func withUnsafeMutableData() throws { + func withMutableDataSpan() { let data = [Int](0..<32) var array = Array2d(data: data, rowCount: 4, columnCount: 8) - // For the comparison we need 'mutable' pointers of the same type. - // But, `withUnsafe*` methods need exclusive ownership of the pointer. - let expectedBaseAddress = try #require( - array.data.withUnsafeMutableBufferPointer { buffer in - buffer.baseAddress - }, - "Expected a valid base address") - array.withUnsafeMutableData { dataPointer in - #expect(dataPointer.baseAddress == expectedBaseAddress) + array.withMutableDataSpan { span in + for index in span.indices { + span[index] += 1 + } } + #expect(array.data == [Int](1..<33)) } }