-
Notifications
You must be signed in to change notification settings - Fork 4
/
buffer.cljs
210 lines (187 loc) · 7.1 KB
/
buffer.cljs
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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
(ns fress.impl.buffer
(:require [fress.util :as util]))
(defprotocol IBuffer
(getByte [this index])
(getBytes [this off length])
(reset [this]))
(defprotocol IBufferReader
(getBytesRead [this])
(notifyBytesRead [this ^int count])
(readUnsignedByte [this])
(readSignedByte [this])
(readUnsignedBytes [this length] "return unsigned byte view on memory")
(readSignedBytes [this length] "return signed byte view on memory"))
(defprotocol IBufferWriter
(getFreeCapacity [this] "remaining free bytes to write")
(room? [this length])
(getBytesWritten [this])
(writeByte [this byte])
(writeBytes [this bytes] [this bytes offset length])
(notifyBytesWritten [this ^int count]))
(defprotocol IStreamingWriter
(toByteArray [this] "get byte-array of current buffer contents. does not close.")
(flushTo [this out]
[this out offset]
"write bytes to externally provided arraybuffer source at the given offset"))
;; wasm users must write single object
(deftype
^{:doc "An interface for reading bytes from a Uint8Array"}
BufferReader
[u8arr ^number backing-offset ^number bytesRead]
IBuffer
(reset [this]
(do
(set! (.-bytesRead this) 0)
(set! (.-open? this) true)))
IBufferReader
(getBytesRead ^number [this] bytesRead)
(notifyBytesRead [this ^number n] (set! (.-bytesRead this) (+ bytesRead n)))
(readUnsignedByte ^number [this]
(let [byte (aget u8arr (+ backing-offset bytesRead))]
(if (undefined? byte)
(throw (js/Error. "EOF"))
(do
(notifyBytesRead this 1)
byte))))
(readSignedByte ^number [this]
(let [byteview (js/Int8Array. (.-buffer u8arr))
byte (aget byteview (+ backing-offset bytesRead))]
(if (undefined? byte)
(throw (js/Error. "EOF"))
(do
(notifyBytesRead this 1)
byte))))
(readSignedBytes [this length]
(let [bytes (js/Int8Array. (.-buffer u8arr) (+ backing-offset bytesRead) length)]
(notifyBytesRead this length)
bytes))
(readUnsignedBytes [this length]
(let [bytes (js/Uint8Array. (.-buffer u8arr) (+ backing-offset bytesRead) length)]
(notifyBytesRead this length)
bytes)))
(deftype
^{:doc
"Backed by a plain array, 'BytesOutputStream' grows as bytes are written,
is only realized into an byte-array when close() is called.
In future can use ArrayBuffer.transfer()"}
BytesOutputStream [^array arr ^number bytesWritten]
IDeref
(-deref [this] (toByteArray this))
IBuffer
(reset [this] (set! (.-bytesWritten this) 0))
IStreamingWriter
(flushTo [this buf] (flushTo this buf 0))
(flushTo [this buf ptr]
(assert (some? (.-buffer buf)) "flushTo requires an arraybuffer backed typed-array")
(assert (util/valid-pointer? ptr) (str "buffer/flushTo given invalid pointer:" (pr-str ptr)))
(let [bytes (if (== (alength arr) bytesWritten)
arr
(.slice arr 0 bytesWritten))]
(assert (= (alength bytes) bytesWritten))
(.set buf bytes ptr)))
(toByteArray [this]
(if (== bytesWritten (alength arr))
(js/Uint8Array. arr)
(js/Uint8Array. (.slice arr 0 bytesWritten))))
IBufferWriter
(room? ^boolean [this _] true)
(getBytesWritten ^number [this] bytesWritten)
(notifyBytesWritten [this ^number n]
(assert (int? n) "written byte count must be an int")
(set! (.-bytesWritten this) (+ n bytesWritten)))
(writeByte [this byte]
(if (<= bytesWritten (alength arr))
(aset arr bytesWritten byte)
(.push arr byte))
(notifyBytesWritten this 1))
(writeBytes [this bytes]
(loop [i 0]
(when-let [byte (aget bytes i)]
(writeByte this byte)
(recur (inc i)))))
(writeBytes [this bytes offset length]
(loop [i offset]
(if-let [byte (and (< (- i offset) length) (aget bytes i))]
(do
(writeByte this byte)
(recur (inc i)))))))
(defn byte-stream [] (BytesOutputStream. #js[] 0))
(defn with-capacity [n] (BytesOutputStream. (make-array n) 0))
(deftype ^{:doc "used on fixed size buffers"}
BufferWriter
[backing ^number backing-offset ^number bytesWritten]
IBuffer
(reset [this] (set! (.-bytesWritten this) 0))
(getByte [this index]
(assert (and (int? index) (<= 0 index)))
(aget (js/Uint8Array. (.. backing -buffer)) (+ backing-offset index)))
(getBytes [this offset length]
(js/Uint8Array. (.-buffer backing) (+ offset backing-offset) length))
IBufferWriter
(getFreeCapacity ^number [this] (- (.. backing -buffer -byteLength) backing-offset bytesWritten))
(room? ^boolean [this length]
(let [free (getFreeCapacity this)]
(<= length free)))
(getBytesWritten ^number [this] bytesWritten)
(notifyBytesWritten [this ^number n]
(assert (int? n) "written byte count must be an int")
(set! (.-bytesWritten this) (+ n bytesWritten)))
(writeByte [this byte]
(if (room? this 1)
(do
(aset (js/Int8Array. (.. backing -buffer)) bytesWritten byte)
(notifyBytesWritten this 1)
this)
(throw (js/Error. "BufferWriter out of room"))))
(writeBytes [this bytes] (writeBytes this bytes 0 (alength bytes)))
(writeBytes [this bytes ^number offset ^number length]
(assert (int? length))
(if (room? this length)
(let [i8array (js/Int8Array. (.. backing -buffer))]
(.set i8array (.subarray bytes offset (+ offset length)) (+ bytesWritten backing-offset))
(notifyBytesWritten this length)
this)
(throw (js/Error. "BufferWriter out of room")))))
(defn readable-buffer
"Build a BufferReader over a collection of bytes."
([backing](readable-buffer backing 0))
([backing backing-offset]
(cond
(some? (.-buffer backing))
(BufferReader. backing (or backing-offset 0) 0)
(instance? js/ArrayBuffer backing)
(readable-buffer (js/Uint8Array. backing) backing-offset)
(implements? IBufferReader backing)
backing
(vector? backing)
(readable-buffer (js/Uint8Array. (into-array backing)) backing-offset)
(array? backing)
(readable-buffer (js/Uint8Array. backing) backing-offset)
(instance? BytesOutputStream backing)
(readable-buffer (toByteArray backing) backing-offset)
(instance? BufferWriter backing)
(readable-buffer (.-backing backing) backing-offset)
:else
(throw
(js/Error.
(str "invalid input type " (type backing) " passed to readable-buffer.\n"
"Input must be a typed array, array-buffer, or IBufferWriter instance"))))))
(defn writable-buffer
"Build a BufferWriter over a typed-array. If nil, returns a BytesOutputStream."
([](writable-buffer nil nil))
([backing](writable-buffer backing 0))
([backing backing-offset]
(cond
(nil? backing)
(byte-stream)
(implements? IBufferWriter backing)
backing
(instance? js/ArrayBuffer backing)
(writable-buffer (js/Int8Array. backing) backing-offset)
(some? (.-buffer backing))
(BufferWriter. backing (int (or backing-offset 0)) 0)
:else
(throw
(js/Error.
(str "invalid input type " (type backing) " passed to writable-buffer.\n"
"Input must be a typed array, array-buffer, or nil"))))))