Consolidate CGDataProviderDirectCallbacks logic
This commit is contained in:
parent
018cb71ab4
commit
8668d86a35
@ -771,6 +771,7 @@
|
||||
508622AD2D026F5200931BF9 /* CanonicalPhoneNumberTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508622AC2D026F5200931BF9 /* CanonicalPhoneNumberTest.swift */; };
|
||||
508C72242C2DFCB2000811F3 /* OWSOutgoingResendResponseTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508C72232C2DFCB2000811F3 /* OWSOutgoingResendResponseTest.swift */; };
|
||||
508F0346296F72F4001D88D0 /* CustomCellBackgroundColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508F0345296F72F4001D88D0 /* CustomCellBackgroundColor.swift */; };
|
||||
508F05A52FA28308004B96E5 /* CGDataProvider+SSK.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508F05A42FA28308004B96E5 /* CGDataProvider+SSK.swift */; };
|
||||
509085B82C498C3F00409B85 /* HTMLMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C5CABC289453B200548EEE /* HTMLMetadata.swift */; };
|
||||
509085BA2C498C4400409B85 /* HTMLMetadataTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F94261D4289B1B5400460798 /* HTMLMetadataTests.swift */; };
|
||||
509085BC2C498D3600409B85 /* LinkPreviewFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 509085BB2C498D3500409B85 /* LinkPreviewFetcher.swift */; };
|
||||
@ -5050,6 +5051,7 @@
|
||||
508622AC2D026F5200931BF9 /* CanonicalPhoneNumberTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CanonicalPhoneNumberTest.swift; sourceTree = "<group>"; };
|
||||
508C72232C2DFCB2000811F3 /* OWSOutgoingResendResponseTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OWSOutgoingResendResponseTest.swift; sourceTree = "<group>"; };
|
||||
508F0345296F72F4001D88D0 /* CustomCellBackgroundColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomCellBackgroundColor.swift; sourceTree = "<group>"; };
|
||||
508F05A42FA28308004B96E5 /* CGDataProvider+SSK.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CGDataProvider+SSK.swift"; sourceTree = "<group>"; };
|
||||
509085BB2C498D3500409B85 /* LinkPreviewFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkPreviewFetcher.swift; sourceTree = "<group>"; };
|
||||
509085BD2C49C29400409B85 /* PaddingBucket.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaddingBucket.swift; sourceTree = "<group>"; };
|
||||
509085BF2C49C2A500409B85 /* PaddingBucketTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaddingBucketTest.swift; sourceTree = "<group>"; };
|
||||
@ -15456,6 +15458,7 @@
|
||||
E7D7C93E28B580AC003F043B /* Bundle+OWS.swift */,
|
||||
88D7BA9D266809F50088D1C2 /* CallMessageRelay.swift */,
|
||||
76387BEF28F4ED73002C7BA5 /* CaseIterable.swift */,
|
||||
508F05A42FA28308004B96E5 /* CGDataProvider+SSK.swift */,
|
||||
F9C5CB2A289453B200548EEE /* Collection+OWS.swift */,
|
||||
0512145A2C5BCECF0021EEC9 /* CollectionDifference+SSK.swift */,
|
||||
34A955AB271B521500B05242 /* CommonStrings.swift */,
|
||||
@ -19174,6 +19177,7 @@
|
||||
668A012A2C2B6088007B8808 /* Catchable.swift in Sources */,
|
||||
F9C5CC0C289453B300548EEE /* CDNDownloadOperation.swift in Sources */,
|
||||
727328072CA6CF570080E2C7 /* Certificates.swift in Sources */,
|
||||
508F05A52FA28308004B96E5 /* CGDataProvider+SSK.swift in Sources */,
|
||||
661396AD28BE74DC00E0C4DF /* ChainedPromise.swift in Sources */,
|
||||
50E5E4B32993352C00E15A1C /* ChangePhoneNumberPniManager.swift in Sources */,
|
||||
6603AC2D29C220F30079BC82 /* ChangePhoneNumberPniManagerMock.swift in Sources */,
|
||||
|
||||
@ -119,7 +119,7 @@ public struct DecryptionMetadata {
|
||||
/// A read-only file handle to a file that is encrypted on disk but reads out plaintext bytes.
|
||||
///
|
||||
/// Functionally behaves like a FileHandle to a virtual plaintext file.
|
||||
public protocol EncryptedFileHandle {
|
||||
public protocol EncryptedFileHandle: CGDataProviderFileHandle {
|
||||
|
||||
/// Length, in bytes, of the decrypted plaintext.
|
||||
/// Comes from the sender; otherwise we don't know where content ends and custom padding begins.
|
||||
@ -136,6 +136,12 @@ public protocol EncryptedFileHandle {
|
||||
func read(upToCount: Int) throws -> Data
|
||||
}
|
||||
|
||||
extension EncryptedFileHandle {
|
||||
public func readNonOptional(upToCount count: Int) throws -> Data {
|
||||
return try self.read(upToCount: count)
|
||||
}
|
||||
}
|
||||
|
||||
enum CryptographyError: Error {
|
||||
/// The caller-provided plaintext length was longer than the encrypted
|
||||
/// length.
|
||||
|
||||
@ -94,16 +94,6 @@ extension UIImage {
|
||||
}
|
||||
|
||||
extension CGDataProvider {
|
||||
|
||||
// Class-bound wrapper around EncryptedFileHandle
|
||||
class EncryptedFileHandleWrapper {
|
||||
let fileHandle: SignalServiceKit.EncryptedFileHandle
|
||||
|
||||
init(_ fileHandle: SignalServiceKit.EncryptedFileHandle) {
|
||||
self.fileHandle = fileHandle
|
||||
}
|
||||
}
|
||||
|
||||
/// If no plaintext length is provided, the file is assumed to only use pkcs7 padding.
|
||||
fileprivate static func loadFromEncryptedFile<T>(
|
||||
at fileURL: URL,
|
||||
@ -124,57 +114,11 @@ extension CGDataProvider {
|
||||
attachmentKey: attachmentKey,
|
||||
)
|
||||
}
|
||||
let dataProvider = try CGDataProvider.from(fileHandle: fileHandle)
|
||||
return try block(dataProvider)
|
||||
}
|
||||
|
||||
public static func from(fileHandle: EncryptedFileHandle) throws -> CGDataProvider {
|
||||
let fileHandle = EncryptedFileHandleWrapper(fileHandle)
|
||||
|
||||
var callbacks = CGDataProviderDirectCallbacks(
|
||||
version: 0,
|
||||
getBytePointer: nil,
|
||||
releaseBytePointer: nil,
|
||||
getBytesAtPosition: { info, buffer, offset, byteCount in
|
||||
guard let info else {
|
||||
return 0
|
||||
}
|
||||
let unmanagedFileHandle = Unmanaged<EncryptedFileHandleWrapper>.fromOpaque(info)
|
||||
let fileHandle = unmanagedFileHandle.takeUnretainedValue().fileHandle
|
||||
do {
|
||||
if offset != fileHandle.offset() {
|
||||
try fileHandle.seek(toOffset: UInt64(offset))
|
||||
}
|
||||
let data = try fileHandle.read(upToCount: byteCount)
|
||||
data.withUnsafeBytes { bytes in
|
||||
buffer.copyMemory(from: bytes.baseAddress!, byteCount: bytes.count)
|
||||
}
|
||||
return data.count
|
||||
} catch {
|
||||
return 0
|
||||
}
|
||||
},
|
||||
releaseInfo: { info in
|
||||
guard let info else {
|
||||
return
|
||||
}
|
||||
let unmanagedFileHandle = Unmanaged<EncryptedFileHandleWrapper>.fromOpaque(info)
|
||||
unmanagedFileHandle.release()
|
||||
},
|
||||
)
|
||||
|
||||
let unmanagedFileHandle = Unmanaged.passRetained(fileHandle)
|
||||
|
||||
guard
|
||||
let dataProvider = CGDataProvider(
|
||||
directInfo: unmanagedFileHandle.toOpaque(),
|
||||
size: Int64(fileHandle.fileHandle.plaintextLength),
|
||||
callbacks: &callbacks,
|
||||
)
|
||||
else {
|
||||
throw OWSAssertionError("Failed to create data provider")
|
||||
let dataProvider = CGDataProvider.from(fileHandle: fileHandle, fileSize: Int64(fileHandle.plaintextLength))
|
||||
guard let dataProvider else {
|
||||
throw OWSAssertionError("couldn't initialize encrypted data provider")
|
||||
}
|
||||
return dataProvider
|
||||
return try block(dataProvider)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
70
SignalServiceKit/Util/CGDataProvider+SSK.swift
Normal file
70
SignalServiceKit/Util/CGDataProvider+SSK.swift
Normal file
@ -0,0 +1,70 @@
|
||||
//
|
||||
// Copyright 2026 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public protocol CGDataProviderFileHandle {
|
||||
func offset() throws -> UInt64
|
||||
func seek(toOffset offset: UInt64) throws
|
||||
func readNonOptional(upToCount count: Int) throws -> Data
|
||||
}
|
||||
|
||||
extension FileHandle: CGDataProviderFileHandle {
|
||||
public func readNonOptional(upToCount count: Int) throws -> Data {
|
||||
return try self.read(upToCount: count) ?? Data()
|
||||
}
|
||||
}
|
||||
|
||||
extension CGDataProvider {
|
||||
// Class-bound wrapper around a FileHandle
|
||||
private class FileHandleWrapper {
|
||||
let fileHandle: any CGDataProviderFileHandle
|
||||
|
||||
init(_ fileHandle: any CGDataProviderFileHandle) {
|
||||
self.fileHandle = fileHandle
|
||||
}
|
||||
}
|
||||
|
||||
public static func from(fileHandle: any CGDataProviderFileHandle, fileSize: Int64) -> CGDataProvider? {
|
||||
var callbacks = CGDataProviderDirectCallbacks(
|
||||
version: 0,
|
||||
getBytePointer: nil,
|
||||
releaseBytePointer: nil,
|
||||
getBytesAtPosition: { info, buffer, offset, byteCount in
|
||||
guard let info else {
|
||||
return 0
|
||||
}
|
||||
let fileHandle = Unmanaged<FileHandleWrapper>.fromOpaque(info).takeUnretainedValue().fileHandle
|
||||
do {
|
||||
if offset != (try fileHandle.offset()) {
|
||||
try fileHandle.seek(toOffset: UInt64(offset))
|
||||
}
|
||||
let data = try fileHandle.readNonOptional(upToCount: byteCount)
|
||||
data.withUnsafeBytes { bytes in
|
||||
buffer.copyMemory(from: bytes.baseAddress!, byteCount: bytes.count)
|
||||
}
|
||||
return data.count
|
||||
} catch {
|
||||
return 0
|
||||
}
|
||||
},
|
||||
releaseInfo: { info in
|
||||
guard let info else {
|
||||
return
|
||||
}
|
||||
Unmanaged<FileHandleWrapper>.fromOpaque(info).release()
|
||||
},
|
||||
)
|
||||
|
||||
let fileHandleWrapper = FileHandleWrapper(fileHandle)
|
||||
let unmanagedFileHandle = Unmanaged.passRetained(fileHandleWrapper)
|
||||
|
||||
return CGDataProvider(
|
||||
directInfo: unmanagedFileHandle.toOpaque(),
|
||||
size: fileSize,
|
||||
callbacks: &callbacks,
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -46,7 +46,10 @@ struct EncryptedFileHandleImageSource: OWSImageSource {
|
||||
}
|
||||
|
||||
func cgImageSource() throws -> CGImageSource? {
|
||||
let dataProvider = try CGDataProvider.from(fileHandle: fileHandle)
|
||||
let dataProvider = CGDataProvider.from(fileHandle: fileHandle, fileSize: Int64(fileHandle.plaintextLength))
|
||||
guard let dataProvider else {
|
||||
throw OWSAssertionError("couldn't initialize encrypted data provider")
|
||||
}
|
||||
return CGImageSourceCreateWithDataProvider(dataProvider, nil)
|
||||
}
|
||||
}
|
||||
|
||||
@ -29,66 +29,10 @@ public struct FileHandleImageSource: OWSImageSource {
|
||||
return try fileHandle.read(upToCount: byteLength) ?? Data()
|
||||
}
|
||||
|
||||
// Class-bound wrapper around FileHandle
|
||||
class FileHandleWrapper {
|
||||
let fileHandle: FileHandle
|
||||
|
||||
init(_ fileHandle: FileHandle) {
|
||||
self.fileHandle = fileHandle
|
||||
}
|
||||
}
|
||||
|
||||
public func cgImageSource() throws -> CGImageSource? {
|
||||
let fileHandle = FileHandleWrapper(fileHandle)
|
||||
|
||||
var callbacks = CGDataProviderDirectCallbacks(
|
||||
version: 0,
|
||||
getBytePointer: nil,
|
||||
releaseBytePointer: nil,
|
||||
getBytesAtPosition: { info, buffer, offset, byteCount in
|
||||
guard
|
||||
let unmanagedFileHandle = info?.assumingMemoryBound(
|
||||
to: Unmanaged<FileHandleWrapper>.self,
|
||||
).pointee
|
||||
else {
|
||||
return 0
|
||||
}
|
||||
let fileHandle = unmanagedFileHandle.takeUnretainedValue().fileHandle
|
||||
do {
|
||||
if offset != (try fileHandle.offset()) {
|
||||
try fileHandle.seek(toOffset: UInt64(offset))
|
||||
}
|
||||
let data = try fileHandle.read(upToCount: byteCount) ?? Data()
|
||||
data.withUnsafeBytes { bytes in
|
||||
buffer.copyMemory(from: bytes.baseAddress!, byteCount: bytes.count)
|
||||
}
|
||||
return data.count
|
||||
} catch {
|
||||
return 0
|
||||
}
|
||||
},
|
||||
releaseInfo: { info in
|
||||
guard
|
||||
let unmanagedFileHandle = info?.assumingMemoryBound(
|
||||
to: Unmanaged<FileHandleWrapper>.self,
|
||||
).pointee
|
||||
else {
|
||||
return
|
||||
}
|
||||
unmanagedFileHandle.release()
|
||||
},
|
||||
)
|
||||
|
||||
var unmanagedFileHandle = Unmanaged.passRetained(fileHandle)
|
||||
|
||||
guard
|
||||
let dataProvider = CGDataProvider(
|
||||
directInfo: &unmanagedFileHandle,
|
||||
size: Int64(byteLength),
|
||||
callbacks: &callbacks,
|
||||
)
|
||||
else {
|
||||
throw OWSAssertionError("Failed to create data provider")
|
||||
let dataProvider = CGDataProvider.from(fileHandle: self.fileHandle, fileSize: Int64(self.byteLength))
|
||||
guard let dataProvider else {
|
||||
throw OWSAssertionError("couldn't create data provider")
|
||||
}
|
||||
return CGImageSourceCreateWithDataProvider(dataProvider, nil)
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user