Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion FFDataWrapper.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -264,9 +264,9 @@
isa = PBXNativeTarget;
buildConfigurationList = A5063F971F73B29500CA4109 /* Build configuration list for PBXNativeTarget "FFDataWrapper" */;
buildPhases = (
A5063F801F73B29500CA4109 /* Headers */,
A5063F7E1F73B29500CA4109 /* Sources */,
A5063F7F1F73B29500CA4109 /* Frameworks */,
A5063F801F73B29500CA4109 /* Headers */,
A5063F811F73B29500CA4109 /* Resources */,
);
buildRules = (
Expand Down
73 changes: 47 additions & 26 deletions FFDataWrapper/FFDataWrapper+Mapping.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,33 +17,39 @@ extension FFDataWrapper {
/// - Parameter block: The closure to execute.
@discardableResult
public func mapData<ResultType>(_ block: (inout Data) throws -> ResultType) rethrows -> ResultType {
var decodedData = Data(repeating:0, count: dataRef.dataBuffer.count)
var decodedData = Data(repeating: 0, count: dataRef.dataBuffer.count)
getDecodedData(&decodedData)

let count = decodedData.withUnsafeMutableBytes({ (destMutableRawBufferPtr: UnsafeMutableRawBufferPointer) -> Int in
switch self.storedCoders {
case .coders(_, let decoder):
decoder(UnsafeBufferPointer(start: self.dataRef.dataBuffer.baseAddress!, count: dataRef.dataBuffer.count),
UnsafeMutableBufferPointer(start: destMutableRawBufferPtr.baseAddress!.assumingMemoryBound(to: UInt8.self),
count: dataRef.dataBuffer.count))
return dataRef.dataBuffer.count
case .infoCoders(_, let decoder):
decoder(UnsafeBufferPointer(start: self.dataRef.dataBuffer.baseAddress!, count: dataRef.dataBuffer.count),
UnsafeMutableBufferPointer(start: destMutableRawBufferPtr.baseAddress!.assumingMemoryBound(to: UInt8.self),
count: dataRef.dataBuffer.count), nil)
return dataRef.dataBuffer.count
}

})
// Must NOT grow the buffer of decodedData (only shrink)
if count < decodedData.count {
decodedData.count = count
defer {
decodedData.resetBytes(in: 0 ..< decodedData.count)
}

let result = try block(&decodedData)

return result
}

#if swift(>=5.5.2) && canImport(_Concurrency)
/// Execute the given async closure with wrapped data.
/// Data is converted back from its internal representation and is wiped after the closure is completed.
/// Wiping of the data will succeed ONLY if the data is not passed outside the closure (i.e. if there are no additional references to it
/// by the time the closure completes).
/// - Parameter block: The closure to execute.
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
@discardableResult
public func mapData<ResultType>(_ block: (inout Data) async throws -> ResultType) async rethrows -> ResultType {
var decodedData = Data(repeating: 0, count: dataRef.dataBuffer.count)
getDecodedData(&decodedData)

decodedData.resetBytes(in: 0 ..< decodedData.count)
defer {
decodedData.resetBytes(in: 0 ..< decodedData.count)
}

let result = try await block(&decodedData)

return result
}
#endif

/// Execute the given closure with wrapped data.
/// This version takes some extra info and passes it to the decoder in case InfoCoder was used.
Expand All @@ -54,9 +60,21 @@ extension FFDataWrapper {
/// - Parameter block: The closure to execute.
@discardableResult
public func mapData<ResultType>(info: Any?, block: (inout Data) throws -> ResultType) rethrows -> ResultType {
var decodedData = Data(repeating:0, count: dataRef.dataBuffer.count)
var decodedData = Data(repeating: 0, count: dataRef.dataBuffer.count)
getDecodedData(&decodedData, info: info)

defer {
decodedData.resetBytes(in: 0 ..< decodedData.count)
}

decodedData.withUnsafeMutableBytes({ (destMutableRawBufferPtr: UnsafeMutableRawBufferPointer) -> Void in
let result = try block(&decodedData)
return result
}

func getDecodedData(_ decodedData: inout Data, info: Any? = nil) {
decodedData.reserveCapacity(dataRef.dataBuffer.count)

let count = decodedData.withUnsafeMutableBytes { destMutableRawBufferPtr -> Int in
switch self.storedCoders {
case .coders(_, let decoder):
decoder(UnsafeBufferPointer(start: self.dataRef.dataBuffer.baseAddress!, count: dataRef.dataBuffer.count),
Expand All @@ -67,10 +85,13 @@ extension FFDataWrapper {
UnsafeMutableBufferPointer(start: destMutableRawBufferPtr.baseAddress!.assumingMemoryBound(to: UInt8.self),
count: dataRef.dataBuffer.count), info)
}
})
let result = try block(&decodedData)
decodedData.resetBytes(in: 0 ..< decodedData.count)
return result
return dataRef.dataBuffer.count
}

// Must NOT grow the buffer of decodedData (only shrink)
if count < decodedData.count {
decodedData.count = count
}
}

/// Will be deprecated soon. Please use mapData instead.
Expand Down
30 changes: 30 additions & 0 deletions FFDataWrapperTests/FFDataWrapperTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,36 @@ class FFDataWrapperTests: XCTestCase
XCTAssertNotEqual(reconstructedBacking, copiedBacking)
}

#if swift(>=5.5.2) && canImport(_Concurrency)
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
func testAsync() async {
// Inline data: 14 bytes
//
let testString = "ABCDEF0123456789ABCDEF"
let testData = testString.data(using: .utf8)!
let testDataLength = testData.count

let dataWrapper = FFDataWrapper(data: testData)
var copiedBacking = Data()

let bytes: UnsafePointer<UInt8> = await dataWrapper.mapData { (data: inout Data) async -> UnsafePointer<UInt8> in
await withUnsafeContinuation { continuation in
let pointer = data.withUnsafeBytes { (ptr: UnsafeRawBufferPointer) -> UnsafePointer<UInt8> in
copiedBacking = Data(bytes: ptr.baseAddress!.assumingMemoryBound(to: UInt8.self), count: data.count)
return ptr.baseAddress!.assumingMemoryBound(to: UInt8.self)
}
continuation.resume(returning: pointer)
}
}

let copiedBackingString = String(data: copiedBacking, encoding: .utf8)
XCTAssertEqual(copiedBackingString, testString)
let reconstructedBacking = Data(bytes: bytes, count: testDataLength)

XCTAssertNotEqual(reconstructedBacking, copiedBacking)
}
#endif

struct StructWithSensitiveData: Decodable
{
var name: String
Expand Down