-
-
Notifications
You must be signed in to change notification settings - Fork 96
Expand file tree
/
Copy pathArrayBuffer.swift
More file actions
164 lines (149 loc) · 5.07 KB
/
Copy pathArrayBuffer.swift
File metadata and controls
164 lines (149 loc) · 5.07 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
//
// ArrayBuffer.swift
// NitroModules
//
// Created by Marc Rousavy on 17.07.24.
//
import Foundation
/// Holds instances of `std::shared_ptr<ArrayBuffer>`, which can be passed
/// between native and JS **without copy**.
///
/// See `data`, `size` and `isOwner`.
public typealias ArrayBuffer = margelo.nitro.ArrayBufferHolder
// pragma MARK: Wrap
extension ArrayBuffer {
/**
* Create a new `ArrayBuffer` that wraps the given `data` of the given `size`
* without performing a copy.
* When the `ArrayBuffer` is no longer used, `onDelete` will be called, in which
* you as a caller are responsible for deleting `data`.
*/
public static func wrap(
dataWithoutCopy data: UnsafeMutablePointer<UInt8>,
size: Int,
onDelete delete: @escaping () -> Void
) -> ArrayBuffer {
// Convert escaping Swift closure to a `void*`
let swiftClosure = SwiftClosure(wrappingClosure: delete)
// Create ArrayBuffer with our wrapped Swift closure to make it callable as a C-function pointer
return ArrayBuffer.wrap(data, size, swiftClosure)
}
/**
* Create a new `ArrayBuffer` that wraps the given `data` of the given `size`
* without performing a copy.
* When the `ArrayBuffer` is no longer used, `onDelete` will be called, in which
* you as a caller are responsible for deleting `data`.
*/
public static func wrap(
dataWithoutCopy data: UnsafeMutableRawPointer,
size: Int,
onDelete delete: @escaping () -> Void
) -> ArrayBuffer {
return ArrayBuffer.wrap(
dataWithoutCopy: data.assumingMemoryBound(to: UInt8.self),
size: size,
onDelete: delete)
}
}
// pragma MARK: Allocate
extension ArrayBuffer {
/**
* Allocate a new buffer of the given `size`.
* If `initializeToZero` is `true`, all bytes are set to `0`, otherwise they are left untouched.
*/
public static func allocate(size: Int, initializeToZero: Bool = false) -> ArrayBuffer {
let data = UnsafeMutablePointer<UInt8>.allocate(capacity: size)
if initializeToZero {
data.initialize(repeating: 0, count: size)
}
let deleteFunc = SwiftClosure {
data.deallocate()
}
return ArrayBuffer.wrap(data, size, deleteFunc)
}
}
// pragma MARK: Copy
extension ArrayBuffer {
/**
* Copy the given `UnsafePointer<UInt8>` into a new **owning** `ArrayBuffer`.
*/
public static func copy(
of other: UnsafePointer<UInt8>,
size: Int
) -> ArrayBuffer {
// 1. Create new `UnsafeMutablePointer<UInt8>`
let copy = UnsafeMutablePointer<UInt8>.allocate(capacity: size)
// 2. Copy over data
copy.initialize(from: other, count: size)
// 3. Create memory safe destroyer
let deleteFunc = SwiftClosure {
copy.deallocate()
}
return ArrayBuffer.wrap(copy, size, deleteFunc)
}
/**
* Copy the given `ArrayBuffer` into a new **owning** `ArrayBuffer`.
*/
public static func copy(of other: ArrayBuffer) -> ArrayBuffer {
return ArrayBuffer.copy(of: other.data, size: other.size)
}
/**
* Copy the given `Data` into a new **owning** `ArrayBuffer`.
*/
public static func copy(data: Data) throws -> ArrayBuffer {
// 1. Create new `ArrayBuffer` of same size
let size = data.count
let arrayBuffer = ArrayBuffer.allocate(size: size)
// 2. Copy all bytes from `Data` into our new `ArrayBuffer`
try data.withUnsafeBytes { rawPointer in
guard let baseAddress = rawPointer.baseAddress else {
throw RuntimeError.error(withMessage: "Cannot get baseAddress of Data!")
}
memcpy(arrayBuffer.data, baseAddress, size)
}
return arrayBuffer
}
}
// pragma MARK: Data
extension ArrayBuffer {
/**
* Wrap this `ArrayBuffer` in a `Data` instance, without performing a copy.
* - `copyIfNeeded`: If this `ArrayBuffer` is **non-owning**, the foreign
* data may needs to be copied to be safely used outside of the scope of the caller function.
* This flag controls that.
*/
public func toData(copyIfNeeded: Bool) -> Data {
let shouldCopy = copyIfNeeded && !self.isOwner
if shouldCopy {
// COPY DATA
return Data.init(bytes: self.data, count: self.size)
} else {
// WRAP DATA
// 1. Get the std::shared_ptr<ArrayBuffer>
var sharedPointer = self.getArrayBuffer()
// 2. Create a Data object WRAPPING our pointer
return Data(
bytesNoCopy: self.data, count: self.size,
deallocator: .custom({ buffer, size in
// 3. Capture the std::shared_ptr<ArrayBuffer> in the deallocator lambda so it stays alive.
// As soon as this lambda gets called, the `sharedPointer` gets deleted causing the
// underlying `ArrayBuffer` to be freed.
sharedPointer.reset()
}))
}
}
}
// pragma MARK: Helper
extension ArrayBuffer {
/**
* Returns an **owning** version of this `ArrayBuffer`.
* If this `ArrayBuffer` already is **owning**, it is returned as-is.
* If this `ArrayBuffer` is **non-owning**, it is _copied_.
*/
public func asOwning() -> ArrayBuffer {
if !isOwner {
return ArrayBuffer.copy(of: self)
}
return self
}
}