Skip to content

Commit 35ec8d1

Browse files
authored
fix: Fix base64 encoding of unicode characters. (#613)
1 parent 35fa033 commit 35ec8d1

File tree

3 files changed

+66
-8
lines changed

3 files changed

+66
-8
lines changed
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import PlatformEncoding from '../../src/platform/PlatformEncoding';
2+
3+
it('can base64 a basic ASCII string', () => {
4+
const encoding = new PlatformEncoding();
5+
expect(encoding.btoa('toaster')).toEqual('dG9hc3Rlcg==');
6+
});
7+
8+
it('can base64 a unicode string containing multi-byte character', () => {
9+
const encoding = new PlatformEncoding();
10+
expect(encoding.btoa('✇⽊❽⾵⊚▴ⶊ↺➹≈⋟⚥⤅⊈ⲏⷨ⾭Ⲗ⑲▯ⶋₐℛ⬎⿌🦄')).toEqual(
11+
'4pyH4r2K4p294r614oqa4pa04raK4oa64p654omI4ouf4pql4qSF4oqI4rKP4reo4r6t4rKW4pGy4pav4raL4oKQ4oSb4qyO4r+M8J+mhA==',
12+
);
13+
});

packages/sdk/react-native/src/polyfills/btoa.ts

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,10 @@
1+
/* eslint-disable no-bitwise */
12
import { fromByteArray } from 'base64-js';
23

3-
function convertToByteArray(s: string) {
4-
const b = [];
5-
for (let i = 0; i < s.length; i += 1) {
6-
b.push(s.charCodeAt(i));
7-
}
8-
return Uint8Array.from(b);
9-
}
4+
import toUtf8Array from './toUtf8Array';
105

116
export function btoa(s: string) {
12-
return fromByteArray(convertToByteArray(s));
7+
return fromByteArray(toUtf8Array(s));
138
}
149

1510
export function base64FromByteArray(a: Uint8Array) {
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/* eslint-disable no-plusplus */
2+
/* eslint-disable no-bitwise */
3+
4+
// Originally from: https://github.com/google/closure-library/blob/a1f5a029c1b32eb4793a2d920aa52abc085e1bf7/closure/goog/crypt/crypt.js
5+
6+
// Once React Native versions uniformly support TextEncoder this code can be removed.
7+
8+
// Copyright 2008 The Closure Library Authors. All Rights Reserved.
9+
//
10+
// Licensed under the Apache License, Version 2.0 (the "License");
11+
// you may not use this file except in compliance with the License.
12+
// You may obtain a copy of the License at
13+
//
14+
// http://www.apache.org/licenses/LICENSE-2.0
15+
//
16+
// Unless required by applicable law or agreed to in writing, software
17+
// distributed under the License is distributed on an "AS-IS" BASIS,
18+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19+
// See the License for the specific language governing permissions and
20+
// limitations under the License.
21+
22+
export default function toUtf8Array(str: string): Uint8Array {
23+
const out: number[] = [];
24+
let p = 0;
25+
for (let i = 0; i < str.length; i += 1) {
26+
let c = str.charCodeAt(i);
27+
if (c < 128) {
28+
out[p++] = c;
29+
} else if (c < 2048) {
30+
out[p++] = (c >> 6) | 192;
31+
out[p++] = (c & 63) | 128;
32+
} else if (
33+
(c & 0xfc00) === 0xd800 &&
34+
i + 1 < str.length &&
35+
(str.charCodeAt(i + 1) & 0xfc00) === 0xdc00
36+
) {
37+
// Surrogate Pair
38+
c = 0x10000 + ((c & 0x03ff) << 10) + (str.charCodeAt(++i) & 0x03ff);
39+
out[p++] = (c >> 18) | 240;
40+
out[p++] = ((c >> 12) & 63) | 128;
41+
out[p++] = ((c >> 6) & 63) | 128;
42+
out[p++] = (c & 63) | 128;
43+
} else {
44+
out[p++] = (c >> 12) | 224;
45+
out[p++] = ((c >> 6) & 63) | 128;
46+
out[p++] = (c & 63) | 128;
47+
}
48+
}
49+
return Uint8Array.from(out);
50+
}

0 commit comments

Comments
 (0)