-
Notifications
You must be signed in to change notification settings - Fork 547
/
Copy pathbufferBrowser.ts
191 lines (177 loc) · 6.13 KB
/
bufferBrowser.ts
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
/*!
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
* Licensed under the MIT License.
*/
import * as base64js from "base64-js";
/**
* Converts a Uint8Array to a string of the provided encoding
* Useful when the array might be an {@link IsoBuffer}.
*
* @param arr - The array to convert.
* @param encoding - Optional target encoding; only "utf8" and "base64" are
* supported, with "utf8" being default.
* @returns The converted string.
*
* @deprecated Moved to the `@fluidframework-internal/client-utils` package.
*/
export function Uint8ArrayToString(arr: Uint8Array, encoding?: string): string {
switch (encoding) {
case "base64": {
return base64js.fromByteArray(arr);
}
case "utf8":
case "utf-8":
case undefined: {
return new TextDecoder().decode(arr);
}
default: {
throw new Error("invalid/unsupported encoding");
}
}
}
/**
* Converts a {@link https://en.wikipedia.org/wiki/Base64 | base64} or
* {@link https://en.wikipedia.org/wiki/UTF-8 | utf-8} string to array buffer.
*
* @param encoding - The input string's encoding.
*
* @deprecated Moved to the `@fluidframework-internal/client-utils` package.
*/
export const stringToBuffer = (input: string, encoding: string): ArrayBufferLike =>
IsoBuffer.from(input, encoding).buffer;
/**
* Convert binary blob to string format
*
* @param blob - the binary blob
* @param encoding - output string's encoding
* @returns the blob in string format
*
* @deprecated Moved to the `@fluidframework-internal/client-utils` package.
*/
export const bufferToString = (blob: ArrayBufferLike, encoding: string): string =>
IsoBuffer.from(blob).toString(encoding);
/**
* Determines if an object is an array buffer.
*
* @remarks Will detect and reject TypedArrays, like Uint8Array.
* Reason - they can be viewport into Array, they can be accepted, but caller has to deal with
* math properly (i.e. Take into account byteOffset at minimum).
* For example, construction of new TypedArray can be in the form of new TypedArray(typedArray) or
* new TypedArray(buffer, byteOffset, length), but passing TypedArray will result in fist path (and
* ignoring byteOffice, length).
*
* @param obj - The object to determine if it is an ArrayBuffer.
*
* @deprecated Moved to the `@fluidframework-internal/client-utils` package.
*/
export function isArrayBuffer(obj: any): obj is ArrayBuffer {
const maybe = obj as (Partial<ArrayBuffer> & Partial<Uint8Array>) | undefined;
return (
obj instanceof ArrayBuffer ||
(typeof maybe === "object" &&
maybe !== null &&
typeof maybe.byteLength === "number" &&
typeof maybe.slice === "function" &&
maybe.byteOffset === undefined &&
maybe.buffer === undefined)
);
}
/**
* Minimal implementation of Buffer for our usages in the browser environment.
*
* @deprecated Moved to the `@fluidframework-internal/client-utils` package.
*/
export class IsoBuffer extends Uint8Array {
/**
* Convert the buffer to a string.
* Only supports encoding the whole string (unlike the Node Buffer equivalent)
* and only utf8 and base64 encodings.
*
* @param encoding - The encoding to use.
*/
public toString(encoding?: string): string {
return Uint8ArrayToString(this, encoding);
}
/**
* Deprecated
* @param value - (string | ArrayBuffer)
* @param encodingOrOffset - (string | number)
* @param length - (number)
*/
static from(value, encodingOrOffset?, length?): IsoBuffer {
if (typeof value === "string") {
return IsoBuffer.fromString(value, encodingOrOffset as string | undefined);
// Capture any typed arrays, including Uint8Array (and thus - IsoBuffer!)
} else if (value !== null && typeof value === "object" && isArrayBuffer(value.buffer)) {
// The version of the from function for the node buffer, which takes a buffer or typed array
// as first parameter, does not have any offset or length parameters. Those are just silently
// ignored and not taken into account
return IsoBuffer.fromArrayBuffer(value.buffer, value.byteOffset, value.byteLength);
} else if (isArrayBuffer(value)) {
return IsoBuffer.fromArrayBuffer(value, encodingOrOffset as number | undefined, length);
} else {
// eslint-disable-next-line unicorn/error-message
throw new TypeError();
}
}
static fromArrayBuffer(
arrayBuffer: ArrayBuffer,
byteOffset?: number,
byteLength?: number,
): IsoBuffer {
const offset = byteOffset ?? 0;
const validLength = byteLength ?? arrayBuffer.byteLength - offset;
if (
offset < 0 ||
offset > arrayBuffer.byteLength ||
validLength < 0 ||
validLength + offset > arrayBuffer.byteLength
) {
// eslint-disable-next-line unicorn/error-message
throw new RangeError();
}
return new IsoBuffer(arrayBuffer, offset, validLength);
}
static fromString(str: string, encoding?: string): IsoBuffer {
switch (encoding) {
case "base64": {
const sanitizedString = this.sanitizeBase64(str);
const encoded = base64js.toByteArray(sanitizedString);
return new IsoBuffer(encoded.buffer);
}
case "utf8":
case "utf-8":
case undefined: {
const encoded = new TextEncoder().encode(str);
return new IsoBuffer(encoded.buffer);
}
default: {
throw new Error("invalid/unsupported encoding");
}
}
}
static isBuffer(obj: any): boolean {
throw new Error("unimplemented");
}
/**
* Sanitize a base64 string to provide to base64-js library.
* {@link https://www.npmjs.com/package/base64-js} is not as tolerant of the same malformed base64 as Node'
* Buffer is.
*/
private static sanitizeBase64(str: string): string {
let sanitizedStr = str;
// Remove everything after padding - Node buffer ignores everything
// after any padding whereas base64-js does not
sanitizedStr = sanitizedStr.split("=")[0];
// Remove invalid characters - Node buffer strips invalid characters
// whereas base64-js replaces them with "A"
sanitizedStr = sanitizedStr.replace(/[^\w+-/]/g, "");
// Check for missing padding - Node buffer tolerates missing padding
// whereas base64-js does not
if (sanitizedStr.length % 4 !== 0) {
const paddingArray = ["", "===", "==", "="];
sanitizedStr += paddingArray[sanitizedStr.length % 4];
}
return sanitizedStr;
}
}