-
-
Notifications
You must be signed in to change notification settings - Fork 51
/
Copy pathJSTypedArray.swift
152 lines (133 loc) · 6.22 KB
/
JSTypedArray.swift
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
//
// Created by Manuel Burghard. Licensed unter MIT.
//
import _CJavaScriptKit
/// A protocol that allows a Swift numeric type to be mapped to the JavaScript TypedArray that holds integers of its type
public protocol TypedArrayElement: ConvertibleToJSValue, ConstructibleFromJSValue {
/// The constructor function for the TypedArray class for this particular kind of number
static var typedArrayClass: JSFunction { get }
}
/// A wrapper around all JavaScript [TypedArray](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/TypedArray) classes that exposes their properties in a type-safe way.
/// FIXME: [BigInt-based TypedArrays are currently not supported](https://github.com/swiftwasm/JavaScriptKit/issues/56).
public class JSTypedArray<Element>: JSBridgedClass, ExpressibleByArrayLiteral where Element: TypedArrayElement {
public class var constructor: JSFunction { Element.typedArrayClass }
public var jsObject: JSObject
public subscript(_ index: Int) -> Element {
get {
return Element.construct(from: jsObject[index])!
}
set {
self.jsObject[index] = newValue.jsValue
}
}
/// Initialize a new instance of TypedArray in JavaScript environment with given length.
/// All the elements will be initialized to zero.
///
/// - Parameter length: The number of elements that will be allocated.
public init(length: Int) {
jsObject = Self.constructor.new(length)
}
required public init(unsafelyWrapping jsObject: JSObject) {
self.jsObject = jsObject
}
required public convenience init(arrayLiteral elements: Element...) {
self.init(elements)
}
/// Initialize a new instance of TypedArray in JavaScript environment with given elements.
///
/// - Parameter array: The array that will be copied to create a new instance of TypedArray
public convenience init(_ array: [Element]) {
let jsArrayRef = array.withUnsafeBufferPointer { ptr in
_create_typed_array(Self.constructor.id, ptr.baseAddress!, Int32(array.count))
}
self.init(unsafelyWrapping: JSObject(id: jsArrayRef))
}
/// Convenience initializer for `Sequence`.
public convenience init<S: Sequence>(_ sequence: S) where S.Element == Element {
self.init(Array(sequence))
}
/// Length (in bytes) of the typed array.
/// The value is established when a TypedArray is constructed and cannot be changed.
/// If the TypedArray is not specifying a `byteOffset` or a `length`, the `length` of the referenced `ArrayBuffer` will be returned.
public var lengthInBytes: Int {
Int(jsObject["byteLength"].number!)
}
/// Calls the given closure with a pointer to a copy of the underlying bytes of the
/// array's storage.
///
/// - Note: The pointer passed as an argument to `body` is valid only for the
/// lifetime of the closure. Do not escape it from the closure for later
/// use.
///
/// - Parameter body: A closure with an `UnsafeBufferPointer` parameter
/// that points to the contiguous storage for the array.
/// If `body` has a return value, that value is also
/// used as the return value for the `withUnsafeBytes(_:)` method. The
/// argument is valid only for the duration of the closure's execution.
/// - Returns: The return value, if any, of the `body` closure parameter.
public func withUnsafeBytes<R>(_ body: (UnsafeBufferPointer<Element>) throws -> R) rethrows -> R {
let bytesLength = lengthInBytes
let rawBuffer = malloc(bytesLength)!
defer { free(rawBuffer) }
_load_typed_array(jsObject.id, rawBuffer.assumingMemoryBound(to: UInt8.self))
let length = lengthInBytes / MemoryLayout<Element>.size
let boundPtr = rawBuffer.bindMemory(to: Element.self, capacity: length)
let bufferPtr = UnsafeBufferPointer<Element>(start: boundPtr, count: length)
let result = try body(bufferPtr)
return result
}
}
// MARK: - Int and UInt support
// FIXME: Should be updated to support wasm64 when that becomes available.
func valueForBitWidth<T>(typeName: String, bitWidth: Int, when32: T) -> T {
if bitWidth == 32 {
return when32
} else if bitWidth == 64 {
fatalError("64-bit \(typeName)s are not yet supported in JSTypedArray")
} else {
fatalError("Unsupported bit width for type \(typeName): \(bitWidth) (hint: stick to fixed-size \(typeName)s to avoid this issue)")
}
}
extension Int: TypedArrayElement {
public static var typedArrayClass: JSFunction =
valueForBitWidth(typeName: "Int", bitWidth: Int.bitWidth, when32: JSObject.global.Int32Array).function!
}
extension UInt: TypedArrayElement {
public static var typedArrayClass: JSFunction =
valueForBitWidth(typeName: "UInt", bitWidth: Int.bitWidth, when32: JSObject.global.Uint32Array).function!
}
extension Int8: TypedArrayElement {
public static var typedArrayClass = JSObject.global.Int8Array.function!
}
extension UInt8: TypedArrayElement {
public static var typedArrayClass = JSObject.global.Uint8Array.function!
}
public class JSUInt8ClampedArray: JSTypedArray<UInt8> {
public class override var constructor: JSFunction { JSObject.global.Uint8ClampedArray.function! }
}
extension Int16: TypedArrayElement {
public static var typedArrayClass = JSObject.global.Int16Array.function!
}
extension UInt16: TypedArrayElement {
public static var typedArrayClass = JSObject.global.Uint16Array.function!
}
extension Int32: TypedArrayElement {
public static var typedArrayClass = JSObject.global.Int32Array.function!
}
extension UInt32: TypedArrayElement {
public static var typedArrayClass = JSObject.global.Uint32Array.function!
}
#if !JAVASCRIPTKIT_WITHOUT_BIGINTS
extension Int64: TypedArrayElement {
public static var typedArrayClass = JSObject.global.BigInt64Array.function!
}
extension UInt64: TypedArrayElement {
public static var typedArrayClass = JSObject.global.BigUint64Array.function!
}
#endif
extension Float32: TypedArrayElement {
public static var typedArrayClass = JSObject.global.Float32Array.function!
}
extension Float64: TypedArrayElement {
public static var typedArrayClass = JSObject.global.Float64Array.function!
}