-
Notifications
You must be signed in to change notification settings - Fork 8
/
spec.html
369 lines (352 loc) · 18.4 KB
/
spec.html
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
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
<!DOCTYPE html>
<meta charset="utf-8">
<pre class="metadata">
title: Uint8Array to/from base64
status: proposal
stage: 2
location: https://github.com/tc39/proposal-arraybuffer-base64
copyright: false
contributors: Kevin Gibbons
</pre>
<div id="metadata-block">
<h1>Contributing to this Proposal</h1>
<p>This proposal is developed <a href="https://github.com/tc39/proposal-arraybuffer-base64">on GitHub</a>. A <a href="https://tc39.es/proposal-arraybuffer-base64/">playground is available</a> with examples and a non-production polyfill to allow direct experimentation.</p>
</div>
<emu-clause id="sec-uint8array.prototype.tobase64">
<h1>Uint8Array.prototype.toBase64 ( [ _options_ ] )</h1>
<emu-alg>
1. Let _O_ be the *this* value.
1. Perform ? ValidateUint8Array(_O_).
1. Let _opts_ be ? GetOptionsObject(_options_).
1. Let _alphabet_ be ? Get(_opts_, *"alphabet"*).
1. If _alphabet_ is *undefined*, set _alphabet_ to *"base64"*.
1. If _alphabet_ is not a String, throw a *TypeError* exception.
1. If _alphabet_ is neither *"base64"* nor *"base64url"*, throw a *TypeError* exception.
1. Let _toEncode_ be ? GetUint8ArrayBytes(_O_).
1. If _alphabet_ is *"base64"*, then
1. Let _outAscii_ be the sequence of code points which results from encoding _toEncode_ according to the base64 encoding specified in section 4 of <a href="https://datatracker.ietf.org/doc/html/rfc4648">RFC 4648</a>. Padding is included.
1. Else,
1. Assert: _alphabet_ is *"base64url"*.
1. Let _outAscii_ be the sequence of code points which results from encoding _toEncode_ according to the base64url encoding specified in section 5 of <a href="https://datatracker.ietf.org/doc/html/rfc4648">RFC 4648</a>. Padding is included.
1. Return CodePointsToString(_outAscii_).
</emu-alg>
</emu-clause>
<emu-clause id="sec-uint8array.prototype.tohex">
<h1>Uint8Array.prototype.toHex ( )</h1>
<emu-alg>
1. Let _O_ be the *this* value.
1. Perform ? ValidateUint8Array(_O_).
1. Let _toEncode_ be ? GetUint8ArrayBytes(_O_).
1. Let _out_ be the empty String.
1. For each byte _byte_ of _toEncode_, do
1. Let _hex_ be Number::toString(𝔽(_byte_), 16).
1. Set _hex_ to StringPad(_hex_, 2, *"0"*, ~start~).
1. Set _out_ to the string-concatenation of _out_ and _hex_.
1. Return _out_.
</emu-alg>
</emu-clause>
<emu-clause id="sec-uint8array.frombase64">
<h1>Uint8Array.fromBase64 ( _string_ [ , _options_ ] )</h1>
<emu-alg>
1. If _string_ is not a String, throw a *TypeError* exception.
1. Let _opts_ be ? GetOptionsObject(_options_).
1. Let _alphabet_ be ? Get(_opts_, *"alphabet"*).
1. If _alphabet_ is *undefined*, set _alphabet_ to *"base64"*.
1. If _alphabet_ is not a String, throw a *TypeError* exception.
1. If _alphabet_ is neither *"base64"* nor *"base64url"*, throw a *TypeError* exception.
1. Let _lastChunkHandling_ be ? Get(_opts_, *"lastChunkHandling"*).
1. If _lastChunkHandling_ is *undefined*, set _lastChunkHandling_ to *"loose"*.
1. If _lastChunkHandling_ is not one of *"loose"*, *"strict"*, or *"stop-before-partial"*, throw a *TypeError* exception.
1. Let _result_ be ? FromBase64(_string_, _alphabet_, _lastChunkHandling_).
1. Let _resultLength_ be the length of _result_.[[Bytes]].
1. Let _ta_ be ? <emu-meta suppress-effects="user-code">AllocateTypedArray(*"Uint8Array"*, %Uint8Array%, *"%Uint8Array.prototype%"*, _resultLength_)</emu-meta>.
1. Set the value at each index of _ta_.[[ViewedArrayBuffer]].[[ArrayBufferData]] to the value at the corresponding index of _result_.[[Bytes]].
1. Return _ta_.
</emu-alg>
</emu-clause>
<emu-clause id="sec-uint8array.frombase64into">
<h1>Uint8Array.fromBase64Into ( _string_, _into_ [ , _options_ ] )</h1>
<emu-alg>
1. If _string_ is not a String, throw a *TypeError* exception.
1. Perform ? ValidateUint8Array(_into_).
1. Let _opts_ be ? GetOptionsObject(_options_).
1. Let _alphabet_ be ? Get(_opts_, *"alphabet"*).
1. If _alphabet_ is *undefined*, set _alphabet_ to *"base64"*.
1. If _alphabet_ is not a String, throw a *TypeError* exception.
1. If _alphabet_ is neither *"base64"* nor *"base64url"*, throw a *TypeError* exception.
1. Let _lastChunkHandling_ be ? Get(_opts_, *"lastChunkHandling"*).
1. If _lastChunkHandling_ is *undefined*, set _lastChunkHandling_ to *"loose"*.
1. If _lastChunkHandling_ is not one of *"loose"*, *"strict"*, or *"stop-before-partial"*, throw a *TypeError* exception.
1. Let _taRecord_ be MakeTypedArrayWithBufferWitnessRecord(_into_, ~seq-cst~).
1. If IsTypedArrayOutOfBounds(_taRecord_) is *true*, throw a *TypeError* exception.
1. Let _byteLength_ be TypedArrayByteLength(_taRecord_).
1. Let _maxLength_ be _byteLength_.
1. Let _result_ be ? FromBase64(_string_, _alphabet_, _lastChunkHandling_, _maxLength_).
1. Let _bytes_ be _result_.[[Bytes]].
1. Let _written_ be the length of _bytes_.
1. NOTE: FromBase64 does not invoke any user code, so the ArrayBuffer backing _into_ cannot have been detached or shrunk.
1. Assert: _written_ ≤ _byteLength_.
1. Let _offset_ be _into_.[[ByteOffset]].
1. Let _index_ be 0.
1. Repeat, while _index_ < _written_,
1. Let _byte_ be _bytes_[i].
1. Let _byteIndexInBuffer_ be _index_ + _offset_.
1. Perform SetValueInBuffer(_into_.[[ViewedArrayBuffer]], _byteIndexInBuffer_, ~uint8~, 𝔽(_byte_), *true*, ~unordered~).
1. Let _resultObject_ be OrdinaryObjectCreate(%Object.prototype%).
1. Perform ! CreateDataPropertyOrThrow(_resultObject_, *"read"*, 𝔽(_result_.[[Read]])).
1. Perform ! CreateDataPropertyOrThrow(_resultObject_, *"written"*, 𝔽(_written_)).
1. Return _resultObject_.
</emu-alg>
</emu-clause>
<emu-clause id="sec-uint8array.fromhex">
<h1>Uint8Array.fromHex ( _string_ )</h1>
<emu-alg>
1. If _string_ is not a String, throw a *TypeError* exception.
1. Let _result_ be ? FromHex(_string_).
1. Let _resultLength_ be the length of _result_.[[Bytes]].
1. Let _ta_ be ? <emu-meta suppress-effects="user-code">AllocateTypedArray(*"Uint8Array"*, %Uint8Array%, *"%Uint8Array.prototype%"*, _resultLength_)</emu-meta>.
1. Set the value at each index of _ta_.[[ViewedArrayBuffer]].[[ArrayBufferData]] to the value at the corresponding index of _result_.[[Bytes]].
1. Return _ta_.
</emu-alg>
</emu-clause>
<emu-clause id="sec-uint8array.fromhexinto">
<h1>Uint8Array.fromHexInto ( _string_, _into_ )</h1>
<emu-alg>
1. If _string_ is not a String, throw a *TypeError* exception.
1. Perform ? ValidateUint8Array(_into_).
1. Let _taRecord_ be MakeTypedArrayWithBufferWitnessRecord(_into_, ~seq-cst~).
1. If IsTypedArrayOutOfBounds(_taRecord_) is *true*, throw a *TypeError* exception.
1. Let _byteLength_ be TypedArrayByteLength(_taRecord_).
1. Let _maxLength_ be _byteLength_.
1. Let _result_ be ? FromHex(_string_, _maxLength_).
1. Let _bytes_ be _result_.[[Bytes]].
1. Let _written_ be the length of _bytes_.
1. NOTE: FromHex does not invoke any user code, so the ArrayBuffer backing _into_ cannot have been detached or shrunk.
1. Assert: _written_ ≤ _byteLength_.
1. Let _offset_ be _into_.[[ByteOffset]].
1. Let _index_ be 0.
1. Repeat, while _index_ < _written_,
1. Let _byte_ be _bytes_[i].
1. Let _byteIndexInBuffer_ be _index_ + _offset_.
1. Perform SetValueInBuffer(_into_.[[ViewedArrayBuffer]], _byteIndexInBuffer_, ~uint8~, 𝔽(_byte_), *true*, ~unordered~).
1. Let _resultObject_ be OrdinaryObjectCreate(%Object.prototype%).
1. Perform ! CreateDataPropertyOrThrow(_resultObject_, *"read"*, 𝔽(_result_.[[Read]])).
1. Perform ! CreateDataPropertyOrThrow(_resultObject_, *"written"*, 𝔽(_written_)).
1. Return _resultObject_.
</emu-alg>
</emu-clause>
<emu-clause id="sec-validateuint8array" type="abstract operation">
<h1>
ValidateUint8Array (
_ta_: an ECMAScript language value,
): either a normal completion containing ~unused~ or a throw completion
</h1>
<dl class="header"></dl>
<emu-alg>
1. Perform ? RequireInternalSlot(_ta_, [[TypedArrayName]]).
1. If _ta_.[[TypedArrayName]] is not *"Uint8Array"*, throw a *TypeError* exception.
1. Return ~unused~.
</emu-alg>
</emu-clause>
<emu-clause id="sec-getuint8arraybytes" type="abstract operation">
<h1>
GetUint8ArrayBytes (
_ta_: a Uint8Array,
): either a normal completion containing a List of byte values or a throw completion
</h1>
<dl class="header"></dl>
<emu-alg>
1. Let _buffer_ be _ta_.[[ViewedArrayBuffer]].
1. Let _taRecord_ be MakeTypedArrayWithBufferWitnessRecord(_ta_, ~seq-cst~).
1. If IsTypedArrayOutOfBounds(_taRecord_) is *true*, throw a *TypeError* exception.
1. Let _len_ be TypedArrayLength(_taRecord_).
1. Let _byteOffset_ be _ta_.[[ByteOffset]].
1. Let _bytes_ be a new empty List.
1. Let _index_ be 0.
1. Repeat, while _index_ < _len_,
1. Let _byteIndex_ be _byteOffset_ + _index_.
1. Let _byte_ be ℝ(GetValueFromBuffer(_buffer_, _byteIndex_, ~uint8~, *true*, ~unordered~)).
1. Append _byte_ to _bytes_.
1. Set _index_ to _index_ + 1.
1. Return _bytes_.
</emu-alg>
</emu-clause>
<emu-clause id="sec-helpers">
<h1>Helpers</h1>
<emu-clause id="sec-skipasciiwhitespace" type="abstract operation">
<h1>
SkipAsciiWhitespace (
_string_: a string,
_index_: a non-negative integer,
): a non-negative integer
</h1>
<dl class="header">
</dl>
<emu-alg>
1. Let _length_ be the length of _string_.
1. Repeat, while _index_ < _length_,
1. Let _char_ be the code unit at index _index_ of _string_.
1. If _char_ is neither 0x0009 (TAB), 0x000A (LF), 0x000C (FF), 0x000D (CR), nor 0x0020 (SPACE), then
1. Return _index_.
1. Set _index_ to _index_ + 1.
1. Return _index_.
</emu-alg>
</emu-clause>
<emu-clause id="sec-decodebase64chunk" type="abstract operation">
<h1>
DecodeBase64Chunk (
_chunk_: a string,
optional _throwOnExtraBits_: a boolean,
): either a normal completion containing a List of byte values, or a throw completion
</h1>
<dl class="header">
</dl>
<p>The <dfn id="standard-base64-alphabet">standard base64 alphabet</dfn> is a List whose elements are the code points corresponding to every letter and number in the Unicode Basic Latin block along with *"+"* and *"/"*; that is, it is StringToCodePoints(*"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"*).</p>
<emu-alg>
1. Let _chunkLength_ be the length of _chunk_.
1. If _chunkLength_ is 2, then
1. Set _chunk_ to the string-concatenation of _chunk_ and *"AA"*.
1. Else if _chunkLength_ is 3, then
1. Set _chunk_ to the string-concatenation of _chunk_ and *"A"*.
1. Else,
1. Assert: _chunkLength_ is 4.
1. Let _byteSequence_ be the unique sequence of 3 bytes resulting from decoding _chunk_ as base64 (such that applying the base64 encoding specified in section 4 of <a href="https://datatracker.ietf.org/doc/html/rfc4648">RFC 4648</a> to _byteSequence_ would produce _chunk_).
1. Let _bytes_ be a List whose elements are the elements of _byteSequence_, in order.
1. If _chunkLength_ is 2, then
1. Assert: _throwOnExtraBits_ is present.
1. If _throwOnExtraBits_ is *true* and _bytes_[1] ≠ 0, then
1. Throw a *SyntaxError* exception.
1. Return « _bytes_[0] ».
1. Else if _chunkLength_ is 3, then
1. Assert: _throwOnExtraBits_ is present.
1. If _throwOnExtraBits_ is *true* and _bytes_[2] ≠ 0, then
1. Throw a *SyntaxError* exception.
1. Return « _bytes_[0], _bytes_[1] ».
1. Else,
1. Return _bytes_.
</emu-alg>
</emu-clause>
<emu-clause id="sec-frombase64" type="abstract operation">
<h1>
FromBase64 (
_string_: a string,
_alphabet_: *"base64"* or *"base64url"*,
_lastChunkHandling_: *"loose"*, *"strict"*, or *"stop-before-partial"*,
optional _maxLength_: a non-negative integer,
): either a normal completion containing a Record with fields [[Read]] (an integral Number) and [[Bytes]] (a List of byte values), or a throw completion
</h1>
<dl class="header">
</dl>
<emu-alg>
1. If _maxLength_ is not present, then
1. Let _maxLength_ be 2<sup>53</sup> - 1.
1. NOTE: Because the input is a string, the length of strings is limited to 2<sup>53</sup> - 1 characters, and the output requires no more bytes than the input has characters, this limit can never be reached. However, it is editorially convenient to use a finite value here.
1. NOTE: The order of validation and decoding in the algorithm below is not observable. Implementations are encouraged to perform them in whatever order is most efficient, possibly interleaving validation with decoding, as long as the behaviour is observably equivalent.
1. If _maxLength_ is 0, then
1. Return the Record { [[Read]]: 0, [[Bytes]]: « » }.
1. Let _read_ be 0.
1. Let _bytes_ be « ».
1. Let _chunk_ be the empty String.
1. Let _chunkLength_ be 0.
1. Let _index_ be 0.
1. Let _length_ be the length of _string_.
1. Repeat,
1. Set _index_ to SkipAsciiWhitespace(_string_, _index_).
1. If _index_ = _length_, then
1. If _chunkLength_ > 0, then
1. If _lastChunkHandling_ is *"stop-before-partial"*, then
1. Return the Record { [[Read]]: _read_, [[Bytes]]: _bytes_ }.
1. Else if _lastChunkHandling_ is *"loose"*, then
1. If _chunkLength_ is 1, then
1. Throw a *SyntaxError* exception.
1. Set _bytes_ to the list-concatenation of _bytes_ and ! DecodeBase64Chunk(_chunk_, *false*).
1. Else,
1. Assert: _lastChunkHandling_ is *"strict"*.
1. Throw a *SyntaxError* exception.
1. Return the Record { [[Read]]: _length_, [[Bytes]]: _bytes_ }.
1. Let _char_ be the substring of _string_ from _index_ to _index_ + 1.
1. Set _index_ to _index_ + 1.
1. If _char_ is *"="*, then
1. If _chunkLength_ < 2, then
1. Throw a *SyntaxError* exception.
1. Set _index_ to SkipAsciiWhitespace(_string_, _index_).
1. If _chunkLength_ = 2, then
1. If _index_ = _length_, then
1. If _lastChunkHandling_ is *"stop-before-partial"*, then
1. Return the Record { [[Read]]: _read_, [[Bytes]]: _bytes_ }.
1. Throw a *SyntaxError* exception.
1. Set _char_ to the substring of _string_ from _index_ to _index_ + 1.
1. If _char_ is *"="*, then
1. Set _index_ to SkipAsciiWhitespace(_string_, _index_ + 1).
1. If _index_ < _length_, then
1. Throw a *SyntaxError* exception.
1. If _lastChunkHandling_ is *"strict"*, let _throwOnExtraBits_ be *true*.
1. Else, let _throwOnExtraBits_ be *false*.
1. Set _bytes_ to the list-concatenation of _bytes_ and ? DecodeBase64Chunk(_chunk_, _throwOnExtraBits_).
1. Return the Record { [[Read]]: _length_, [[Bytes]]: _bytes_ }.
1. If _alphabet_ is *"base64url"*, then
1. If _char_ is either *"+"* or *"/"*, throw a *SyntaxError* exception.
1. Else if _char_ is *"-"*, set _char_ to *"+"*.
1. Else if _char_ is *"_"*, set _char_ to *"/"*.
1. If _char_ is not an element of the standard base64 alphabet, throw a *SyntaxError* exception.
1. Let _remaining_ be _maxLength_ - the length of _bytes_.
1. If _remaining_ = 1 and _chunkLength_ = 2, or if _remaining_ = 2 and _chunkLength_ = 3, then
1. Return the Record { [[Read]]: _read_, [[Bytes]]: _bytes_ }.
1. Set _chunk_ to the string-concatenation of _chunk_ and _char_.
1. Set _chunkLength_ to the length of _chunk_.
1. If _chunkLength_ = 4, then
1. Set _bytes_ to the list-concatenation of _bytes_ and ! DecodeBase64Chunk(_chunk_).
1. Set _chunk_ to the empty String.
1. Set _chunkLength_ to 0.
1. Set _read_ to _index_.
1. If the length of _bytes_ = _maxLength_, then
1. Return the Record { [[Read]]: _read_, [[Bytes]]: _bytes_ }.
</emu-alg>
</emu-clause>
<emu-clause id="sec-fromhex" type="abstract operation">
<h1>
FromHex (
_string_: a string,
optional _maxLength_: a non-negative integer,
): either a normal completion containing a Record with fields [[Read]] (an integral Number) and [[Bytes]] (a List of byte values), or a throw completion
</h1>
<dl class="header">
</dl>
<emu-alg>
1. If _maxLength_ is not present, let _maxLength_ be 2<sup>53</sup> - 1.
1. Let _length_ be the length of _string_.
1. If _length_ modulo 2 is not 0, throw a *SyntaxError* exception.
1. Let _bytes_ be « ».
1. Let _index_ be 0.
1. Repeat, while _index_ < _length_,
1. Let _hexits_ be the substring of _string_ from _index_ to _index_ + 2.
1. If _hexits_ contains any code units which are not in *"0123456789abcdefABCDEF"*, throw a *SyntaxError* exception.
1. Set _index_ to _index_ + 2.
1. Let _byte_ be the integer value represented by _hexits_ in base-16 notation, using the letters A-F and a-f for digits with values 10 through 15.
1. Append _byte_ to _bytes_.
1. If the length of _bytes_ is _maxLength_, then
1. Return the Record { [[Read]]: _index_, [[Bytes]]: _bytes_ }.
1. Return the Record { [[Read]]: _index_, [[Bytes]]: _bytes_ }.
</emu-alg>
</emu-clause>
<!-- Copied from ECMA-402/Temporal -->
<emu-clause id="sec-getoptionsobject" aoid="GetOptionsObject">
<h1>GetOptionsObject ( _options_ )</h1>
<emu-alg>
1. If _options_ is *undefined*, then
1. Return OrdinaryObjectCreate(*null*).
1. If Type(_options_) is Object, then
1. Return _options_.
1. Throw a *TypeError* exception.
</emu-alg>
</emu-clause>
</emu-clause>
<emu-annex id="sec-modified-bibliography">
<h1>Bibliography</h1>
<p>NOTE: We need to add RFC 4648 to the bibliography as part of landing this upstream.</p>
<ol>
<li>
<ins><i>RFC 4648 “The Base16, Base32, and Base64 Data Encodings”</i>, available at <<a href="https://datatracker.ietf.org/doc/html/rfc4648">https://datatracker.ietf.org/doc/html/rfc4648</a></ins>>
</li>
</ol>
</emu-annex>