Skip to content

Commit

Permalink
Merge pull request #1 from signalapp/jrose/clone-on-demand
Browse files Browse the repository at this point in the history
Clone unowned object handles on demand
  • Loading branch information
jrose-signal authored Oct 2, 2020
2 parents f4b571c + d5e8aa7 commit 9729dd9
Show file tree
Hide file tree
Showing 14 changed files with 272 additions and 164 deletions.
24 changes: 12 additions & 12 deletions Sources/SwiftSignal/Address.swift
Original file line number Diff line number Diff line change
@@ -1,33 +1,33 @@
import SignalFfi
import Foundation

class ProtocolAddress {
private var handle: OpaquePointer?

internal func nativeHandle() -> OpaquePointer? {
return handle
}

class ProtocolAddress: ClonableHandleOwner {
init(name: String, device_id: UInt32) throws {
var handle: OpaquePointer?
try CheckError(signal_address_new(&handle,
name,
device_id))
super.init(owned: handle!)
}

internal override init(unowned handle: OpaquePointer?) {
super.init(unowned: handle)
}

internal init(clone_from: OpaquePointer?) throws {
try CheckError(signal_address_clone(&handle, clone_from))
internal override class func cloneNativeHandle(_ newHandle: inout OpaquePointer?, currentHandle: OpaquePointer?) -> SignalFfiErrorRef? {
return signal_address_clone(&newHandle, currentHandle)
}

deinit {
internal override class func destroyNativeHandle(_ handle: OpaquePointer) {
signal_address_destroy(handle)
}

func getName() throws -> String {
return try invokeFnReturningString(fn: { (b) in signal_address_get_name(handle, b) })
return try invokeFnReturningString(fn: { (b) in signal_address_get_name(nativeHandle(), b) })
}

func getDeviceId() throws -> UInt32 {
return try invokeFnReturningInteger(fn: { (i) in signal_address_get_device_id(handle, i) })
return try invokeFnReturningInteger(fn: { (i) in signal_address_get_device_id(nativeHandle(), i) })
}
}

Expand Down
98 changes: 98 additions & 0 deletions Sources/SwiftSignal/ClonableHandleOwner.swift
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
}
4 changes: 2 additions & 2 deletions Sources/SwiftSignal/IdentityKey.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ class IdentityKeyPair {
var privkey_ptr : OpaquePointer?
try CheckError(signal_identitykeypair_deserialize(&pubkey_ptr, &privkey_ptr, bytes, bytes.count))

pubkey = PublicKey(raw_ptr: pubkey_ptr)
privkey = PrivateKey(raw_ptr: privkey_ptr)
pubkey = PublicKey(owned: pubkey_ptr!)
privkey = PrivateKey(owned: privkey_ptr!)
}

func serialize() throws -> [UInt8] {
Expand Down
35 changes: 15 additions & 20 deletions Sources/SwiftSignal/PrivateKey.swift
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
}
}
36 changes: 15 additions & 21 deletions Sources/SwiftSignal/PublicKey.swift
Original file line number Diff line number Diff line change
@@ -1,32 +1,36 @@
import SignalFfi
import Foundation

class PublicKey {
private var handle: OpaquePointer?

class PublicKey: ClonableHandleOwner {
init(_ bytes: [UInt8]) throws {
var handle: OpaquePointer?
try CheckError(signal_publickey_deserialize(&handle, bytes, bytes.count))
super.init(owned: handle!)
}

internal init(raw_ptr: OpaquePointer?) {
handle = raw_ptr
internal override init(owned handle: OpaquePointer) {
super.init(owned: handle)
}

internal init(clone_from: OpaquePointer?) throws {
try CheckError(signal_publickey_clone(&handle, clone_from))
internal override init(unowned handle: OpaquePointer?) {
super.init(unowned: handle)
}

deinit {
override class func destroyNativeHandle(_ handle: OpaquePointer) {
signal_publickey_destroy(handle)
}

override class func cloneNativeHandle(_ newHandle: inout OpaquePointer?, currentHandle: OpaquePointer?) -> SignalFfiErrorRef? {
return signal_publickey_clone(&newHandle, currentHandle)
}

func serialize() throws -> [UInt8] {
return try invokeFnReturningArray(fn: { (b,bl) in signal_publickey_serialize(handle,b,bl) })
return try invokeFnReturningArray(fn: { (b,bl) in signal_publickey_serialize(nativeHandle(),b,bl) })
}

func verifySignature(message: [UInt8], signature: [UInt8]) throws -> Bool {
var result : UInt8 = 0
try CheckError(signal_publickey_verify(handle, &result, message, message.count, signature, signature.count))
try CheckError(signal_publickey_verify(nativeHandle(), &result, message, message.count, signature, signature.count))

if result == 1 {
return true
Expand All @@ -37,19 +41,9 @@ class PublicKey {

func compareWith(other_key: PublicKey) throws -> Int32 {
var result : Int32 = 0
try CheckError(signal_publickey_compare(&result, handle, other_key.handle))
try CheckError(signal_publickey_compare(&result, nativeHandle(), other_key.nativeHandle()))
return result
}

internal func nativeHandle() -> OpaquePointer? {
return handle
}

internal func leakNativeHandle() -> OpaquePointer? {
let save = handle;
handle = nil
return save
}
}

extension PublicKey: Equatable {
Expand Down
36 changes: 19 additions & 17 deletions Sources/SwiftSignal/SenderKeyName.swift
Original file line number Diff line number Diff line change
@@ -1,43 +1,45 @@
import SignalFfi
import Foundation

class SenderKeyName {
private var handle: OpaquePointer?
class SenderKeyName: ClonableHandleOwner {
override class func destroyNativeHandle(_ handle: OpaquePointer) {
signal_sender_key_name_destroy(handle)
}

override class func cloneNativeHandle(_ newHandle: inout OpaquePointer?, currentHandle: OpaquePointer?) -> SignalFfiErrorRef? {
return signal_sender_key_name_clone(&newHandle, currentHandle)
}

init(group_name: String, sender_name: String, device_id: UInt32) throws {
var handle: OpaquePointer?
try CheckError(signal_sender_key_name_new(&handle, group_name, sender_name, device_id))
super.init(owned: handle!)
}

init(group_name: String, sender: ProtocolAddress) throws {
var handle: OpaquePointer?
try CheckError(signal_sender_key_name_new(&handle, group_name, sender.getName(), sender.getDeviceId()))
super.init(owned: handle!)
}

internal init(raw_ptr: OpaquePointer?) {
handle = raw_ptr
internal override init(owned handle: OpaquePointer) {
super.init(owned: handle)
}

internal init(clone_from: OpaquePointer?) throws {
try CheckError(signal_sender_key_name_clone(&handle, clone_from))
}

deinit {
signal_sender_key_name_destroy(handle)
internal override init(unowned handle: OpaquePointer?) {
super.init(unowned: handle)
}

func getGroupId() throws -> String {
return try invokeFnReturningString(fn: { (b) in signal_sender_key_name_get_group_id(handle, b) })
return try invokeFnReturningString(fn: { (b) in signal_sender_key_name_get_group_id(nativeHandle(), b) })
}

func getSenderName() throws -> String {
return try invokeFnReturningString(fn: { (b) in signal_sender_key_name_get_sender_name(handle, b) })
return try invokeFnReturningString(fn: { (b) in signal_sender_key_name_get_sender_name(nativeHandle(), b) })
}

func getSenderDeviceId() throws -> UInt32 {
return try invokeFnReturningInteger(fn: { (i) in signal_sender_key_name_get_sender_device_id(handle, i) })
}

internal func nativeHandle() -> OpaquePointer? {
return handle
return try invokeFnReturningInteger(fn: { (i) in signal_sender_key_name_get_sender_device_id(nativeHandle(), i) })
}
}

Expand Down
Loading

0 comments on commit 9729dd9

Please sign in to comment.