-
-
Notifications
You must be signed in to change notification settings - Fork 424
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from signalapp/jrose/clone-on-demand
Clone unowned object handles on demand
- Loading branch information
Showing
14 changed files
with
272 additions
and
164 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
class ClonableHandleOwner { | ||
enum MaybeOwnedHandle { | ||
case none | ||
case unowned(OpaquePointer) | ||
case owned(OpaquePointer) | ||
} | ||
|
||
private var handle: MaybeOwnedHandle | ||
|
||
internal func nativeHandle() -> OpaquePointer? { | ||
switch handle { | ||
case .none: | ||
return nil | ||
case .unowned(let handle): | ||
return handle | ||
case .owned(let handle): | ||
return handle | ||
} | ||
} | ||
|
||
internal init(owned handle: OpaquePointer) { | ||
self.handle = .owned(handle) | ||
} | ||
|
||
internal init(unowned handle: OpaquePointer?) { | ||
self.handle = handle.map { .unowned($0) } ?? .none | ||
} | ||
|
||
internal func replaceWithClone() { | ||
guard case .unowned(let currentHandle) = self.handle else { | ||
preconditionFailure("replaceWithClone() called for a handle that's already owned") | ||
} | ||
var newHandle: OpaquePointer? | ||
// Automatic cloning must not fail. | ||
try! CheckError(Self.cloneNativeHandle(&newHandle, currentHandle: currentHandle)) | ||
self.handle = .owned(newHandle!) | ||
} | ||
|
||
fileprivate func takeNativeHandle() -> OpaquePointer? { | ||
if case .unowned = self.handle { | ||
preconditionFailure("unowned handle may have escaped") | ||
} | ||
defer { handle = .none } | ||
return nativeHandle() | ||
} | ||
|
||
fileprivate func forgetUnownedHandle() { | ||
guard case .unowned = self.handle else { | ||
preconditionFailure("forgetUnownedHandle() called for an owned handle") | ||
} | ||
handle = .none | ||
} | ||
|
||
internal class func cloneNativeHandle(_ newHandle: inout OpaquePointer?, currentHandle: OpaquePointer?) -> SignalFfiErrorRef? { | ||
fatalError("\(self) does not support cloning") | ||
} | ||
|
||
internal class func destroyNativeHandle(_ handle: OpaquePointer) { | ||
fatalError("must be implemented by subclasses") | ||
} | ||
|
||
deinit { | ||
switch handle { | ||
case .none: | ||
return | ||
case .unowned(_): | ||
preconditionFailure("unowned handle may have escaped") | ||
case .owned(let handle): | ||
Self.destroyNativeHandle(handle) | ||
} | ||
} | ||
} | ||
|
||
/// Ensures that `handleOwner` actually does own its handle by cloning it. | ||
/// | ||
/// As an optimization, steals the handle if `handleOwner` has no other references. | ||
/// Checking this requires using `inout`; the reference itself won't be modified. | ||
func cloneOrForgetAsNeeded<Owner: ClonableHandleOwner>(_ handleOwner: inout Owner) { | ||
if isKnownUniquelyReferenced(&handleOwner) { | ||
handleOwner.forgetUnownedHandle() | ||
} else { | ||
handleOwner.replaceWithClone() | ||
} | ||
} | ||
|
||
/// Clones the handle owned by `handleOwner`. | ||
/// | ||
/// As an optimization, steals the handle if `handleOwner` has no other references. | ||
/// Checking this requires using `inout`; the reference itself won't be modified. | ||
func cloneOrTakeHandle<Owner: ClonableHandleOwner>(from handleOwner: inout Owner) throws -> OpaquePointer? { | ||
if isKnownUniquelyReferenced(&handleOwner) { | ||
return handleOwner.takeNativeHandle() | ||
} | ||
|
||
var result: OpaquePointer? | ||
try CheckError(type(of: handleOwner).cloneNativeHandle(&result, currentHandle: handleOwner.nativeHandle())) | ||
return result | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,50 +1,45 @@ | ||
import SignalFfi | ||
import Foundation | ||
|
||
class PrivateKey { | ||
private var handle: OpaquePointer? | ||
|
||
class PrivateKey: ClonableHandleOwner { | ||
init(_ bytes: [UInt8]) throws { | ||
var handle: OpaquePointer? | ||
try CheckError(signal_privatekey_deserialize(&handle, bytes, bytes.count)) | ||
super.init(owned: handle!) | ||
} | ||
|
||
internal init(raw_ptr: OpaquePointer?) { | ||
handle = raw_ptr | ||
override internal init(owned handle: OpaquePointer) { | ||
super.init(owned: handle) | ||
} | ||
|
||
static func generate() throws -> PrivateKey { | ||
var handle: OpaquePointer? | ||
try CheckError(signal_privatekey_generate(&handle)) | ||
return PrivateKey(raw_ptr: handle) | ||
return PrivateKey(owned: handle!) | ||
} | ||
|
||
override class func cloneNativeHandle(_ newHandle: inout OpaquePointer?, currentHandle: OpaquePointer?) -> SignalFfiErrorRef? { | ||
return signal_privatekey_clone(&newHandle, currentHandle) | ||
} | ||
|
||
deinit { | ||
override class func destroyNativeHandle(_ handle: OpaquePointer) { | ||
signal_privatekey_destroy(handle) | ||
} | ||
|
||
func serialize() throws -> [UInt8] { | ||
return try invokeFnReturningArray(fn: { (b,bl) in signal_privatekey_serialize(handle,b,bl) }) | ||
return try invokeFnReturningArray(fn: { (b,bl) in signal_privatekey_serialize(nativeHandle(),b,bl) }) | ||
} | ||
|
||
func generateSignature(message: [UInt8]) throws -> [UInt8] { | ||
return try invokeFnReturningArray(fn: { (b,bl) in signal_privatekey_sign(b,bl,handle,message,message.count) }) | ||
return try invokeFnReturningArray(fn: { (b,bl) in signal_privatekey_sign(b,bl,nativeHandle(),message,message.count) }) | ||
} | ||
|
||
func keyAgreement(other_key: PublicKey) throws -> [UInt8] { | ||
return try invokeFnReturningArray(fn: { (b,bl) in signal_privatekey_agree(b,bl,handle,other_key.nativeHandle()) }) | ||
return try invokeFnReturningArray(fn: { (b,bl) in signal_privatekey_agree(b,bl,nativeHandle(),other_key.nativeHandle()) }) | ||
} | ||
|
||
func getPublicKey() throws -> PublicKey { | ||
return try invokeFnReturningPublicKey(fn: { (k) in signal_privatekey_get_public_key(k, handle) }) | ||
return try invokeFnReturningPublicKey(fn: { (k) in signal_privatekey_get_public_key(k, nativeHandle()) }) | ||
} | ||
|
||
internal func nativeHandle() -> OpaquePointer? { | ||
return handle | ||
} | ||
|
||
internal func leakNativeHandle() -> OpaquePointer? { | ||
let save = handle | ||
handle = nil | ||
return save | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.