-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
338 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
describe('base64', () => { | ||
const data: Array<[string, string, string]> = [ | ||
['', '', ''], | ||
['v', 'dg==', 'dg'], | ||
['vtils', 'dnRpbHM=', 'dnRpbHM'], | ||
[ | ||
'vtils.base64Encode', | ||
'dnRpbHMuYmFzZTY0RW5jb2Rl', | ||
'dnRpbHMuYmFzZTY0RW5jb2Rl', | ||
], | ||
[ | ||
'JavaScript 工具库', | ||
'SmF2YVNjcmlwdCDlt6XlhbflupM=', | ||
'SmF2YVNjcmlwdCDlt6XlhbflupM', | ||
], | ||
[ | ||
'JavaScript\n工具库', | ||
'SmF2YVNjcmlwdArlt6XlhbflupM=', | ||
'SmF2YVNjcmlwdArlt6XlhbflupM', | ||
], | ||
['\0', 'AA==', 'AA'], | ||
['1', 'MQ==', 'MQ'], | ||
['-1', 'LTE=', 'LTE'], | ||
[ | ||
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#0^&*();:<>,. []{}', | ||
'YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWjAxMjM0NTY3ODkhQCMwXiYqKCk7Ojw+LC4gW117fQ==', | ||
'YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWjAxMjM0NTY3ODkhQCMwXiYqKCk7Ojw-LC4gW117fQ', | ||
], | ||
[ | ||
'😁😎=-#@`.,?/|{*+😁', | ||
'8J+YgfCfmI49LSNAYC4sPy98eyor8J+YgQ==', | ||
'8J-YgfCfmI49LSNAYC4sPy98eyor8J-YgQ', | ||
], | ||
[ | ||
'❥(ゝω・✿ฺ)※▓●²♠⑲Ⅲ∵molÇùㄡεətsフぽㅚ㉢д╢┉(๑╹◡╹)ノ"""', | ||
'4p2lKOOCnc+J44O74py/4Li6KeKAu+KWk+KXj8Ky4pmg4pGy4oWi4oi1bW9sw4fDueOEoc61yZl0c+ODleOBveOFmuOJotC04pWi4pSJKOC5keKVueKXoeKVuSnvvokiIiI=', | ||
'4p2lKOOCnc-J44O74py_4Li6KeKAu-KWk-KXj8Ky4pmg4pGy4oWi4oi1bW9sw4fDueOEoc61yZl0c-ODleOBveOFmuOJotC04pWi4pSJKOC5keKVueKXoeKVuSnvvokiIiI', | ||
], | ||
['a\u{10126}ĉc车头', 'YfCQhKbEiWPovablpLQ=', 'YfCQhKbEiWPovablpLQ'], | ||
] | ||
|
||
beforeEach(() => { | ||
jest.resetModules() | ||
}) | ||
|
||
describe('在 NodeJS 环境中', () => { | ||
test('编码正常', async () => { | ||
const { base64Encode } = await import('./base64') | ||
data.forEach(([str, encodedStr]) => { | ||
expect(base64Encode(str)).toBe(encodedStr) | ||
}) | ||
}) | ||
|
||
test('解码正常', async () => { | ||
const { base64Decode } = await import('./base64') | ||
data.forEach(([str, encodedStr]) => { | ||
expect(base64Decode(encodedStr)).toBe(str) | ||
}) | ||
}) | ||
|
||
test('URL 编码正常', async () => { | ||
const { base64UrlEncode } = await import('./base64') | ||
data.forEach(([str, , encodedUrlStr]) => { | ||
expect(base64UrlEncode(str)).toBe(encodedUrlStr) | ||
}) | ||
}) | ||
|
||
test('URL 解码正常', async () => { | ||
const { base64UrlDecode } = await import('./base64') | ||
data.forEach(([str, , encodedUrlStr]) => { | ||
expect(base64UrlDecode(encodedUrlStr)).toBe(str) | ||
}) | ||
}) | ||
}) | ||
|
||
describe('不在 NodeJS 环境中但有 atob, btoa', () => { | ||
const bufferFrom = Buffer.from | ||
beforeAll(() => { | ||
Object.defineProperty(Buffer, 'from', { | ||
value: null, | ||
}) | ||
}) | ||
afterAll(() => { | ||
Object.defineProperty(Buffer, 'from', { | ||
value: bufferFrom, | ||
}) | ||
}) | ||
|
||
test('编码正常', async () => { | ||
const { base64Encode } = await import('./base64') | ||
data.forEach(([str, encodedStr]) => { | ||
expect(base64Encode(str)).toBe(encodedStr) | ||
}) | ||
}) | ||
|
||
test('解码正常', async () => { | ||
const { base64Decode } = await import('./base64') | ||
data.forEach(([str, encodedStr]) => { | ||
expect(base64Decode(encodedStr)).toBe(str) | ||
}) | ||
}) | ||
|
||
test('URL 编码正常', async () => { | ||
const { base64UrlEncode } = await import('./base64') | ||
data.forEach(([str, , encodedUrlStr]) => { | ||
expect(base64UrlEncode(str)).toBe(encodedUrlStr) | ||
}) | ||
}) | ||
|
||
test('URL 解码正常', async () => { | ||
const { base64UrlDecode } = await import('./base64') | ||
data.forEach(([str, , encodedUrlStr]) => { | ||
expect(base64UrlDecode(encodedUrlStr)).toBe(str) | ||
}) | ||
}) | ||
}) | ||
|
||
describe('不在 NodeJS 环境中也没有 atob, btoa', () => { | ||
const bufferFrom = Buffer.from | ||
const globalWindow = { ...global.window } | ||
beforeAll(() => { | ||
Object.defineProperty(Buffer, 'from', { | ||
value: null, | ||
}) | ||
jest.spyOn(global, 'window', 'get').mockImplementation( | ||
() => | ||
({ | ||
...globalWindow, | ||
atob: undefined, | ||
btoa: undefined, | ||
} as any), | ||
) | ||
}) | ||
afterAll(() => { | ||
Object.defineProperty(Buffer, 'from', { | ||
value: bufferFrom, | ||
}) | ||
jest.restoreAllMocks() | ||
}) | ||
|
||
test('编码正常', async () => { | ||
const { base64Encode } = await import('./base64') | ||
data.forEach(([str, encodedStr]) => { | ||
expect(base64Encode(str)).toBe(encodedStr) | ||
}) | ||
}) | ||
|
||
test('解码正常', async () => { | ||
const { base64Decode } = await import('./base64') | ||
data.forEach(([str, encodedStr]) => { | ||
expect(base64Decode(encodedStr)).toBe(str) | ||
}) | ||
}) | ||
|
||
test('URL 编码正常', async () => { | ||
const { base64UrlEncode } = await import('./base64') | ||
data.forEach(([str, , encodedUrlStr]) => { | ||
expect(base64UrlEncode(str)).toBe(encodedUrlStr) | ||
}) | ||
}) | ||
|
||
test('URL 解码正常', async () => { | ||
const { base64UrlDecode } = await import('./base64') | ||
data.forEach(([str, , encodedUrlStr]) => { | ||
expect(base64UrlDecode(encodedUrlStr)).toBe(str) | ||
}) | ||
}) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
/** | ||
* base64.js | ||
* Dan Kogai (https://github.com/dankogai) | ||
* Licensed under the BSD 3-Clause License | ||
* https://github.com/dankogai/js-base64/blob/master/LICENSE.md | ||
* | ||
* Modified by Jay Fong | ||
*/ | ||
|
||
type XToY = (value: string) => string | ||
|
||
const canUseBufferFrom = | ||
typeof Buffer !== 'undefined' && typeof Buffer.from === 'function' | ||
|
||
const base64Chars = | ||
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' | ||
|
||
const base64Table: { [key: string]: number } = {} | ||
for (let i = 0; i < base64Chars.length; i++) { | ||
base64Table[base64Chars[i]] = i | ||
} | ||
|
||
const fromCharCode = String.fromCharCode | ||
|
||
// binaryToAscii | ||
function binaryToAsciiReplacer(str: string) { | ||
const padlen = [0, 2, 1][str.length % 3] | ||
const ord = | ||
(str.charCodeAt(0) << 16) | | ||
((str.length > 1 ? str.charCodeAt(1) : 0) << 8) | | ||
(str.length > 2 ? str.charCodeAt(2) : 0) | ||
const chars = [ | ||
base64Chars.charAt(ord >>> 18), | ||
base64Chars.charAt((ord >>> 12) & 63), | ||
padlen >= 2 ? '=' : base64Chars.charAt((ord >>> 6) & 63), | ||
padlen >= 1 ? '=' : base64Chars.charAt(ord & 63), | ||
] | ||
return chars.join('') | ||
} | ||
const binaryToAscii: XToY = | ||
(typeof window !== 'undefined' && window.btoa) || | ||
(value => value.replace(/[\s\S]{1,3}/g, binaryToAsciiReplacer)) | ||
|
||
// utf8ToBinary | ||
// eslint-disable-next-line no-control-regex | ||
const utf8ToBinaryRegExp = /[\uD800-\uDBFF][\uDC00-\uDFFFF]|[^\x00-\x7F]/g | ||
function utf8ToBinaryReplacer(str: string) { | ||
if (str.length < 2) { | ||
const cc = str.charCodeAt(0) | ||
return cc < 0x80 | ||
? str | ||
: cc < 0x800 | ||
? fromCharCode(0xc0 | (cc >>> 6)) + fromCharCode(0x80 | (cc & 0x3f)) | ||
: fromCharCode(0xe0 | ((cc >>> 12) & 0x0f)) + | ||
fromCharCode(0x80 | ((cc >>> 6) & 0x3f)) + | ||
fromCharCode(0x80 | (cc & 0x3f)) | ||
} | ||
const cc = | ||
0x10000 + | ||
(str.charCodeAt(0) - 0xd800) * 0x400 + | ||
(str.charCodeAt(1) - 0xdc00) | ||
return ( | ||
fromCharCode(0xf0 | ((cc >>> 18) & 0x07)) + | ||
fromCharCode(0x80 | ((cc >>> 12) & 0x3f)) + | ||
fromCharCode(0x80 | ((cc >>> 6) & 0x3f)) + | ||
fromCharCode(0x80 | (cc & 0x3f)) | ||
) | ||
} | ||
const utf8ToBinary: XToY = value => | ||
value.replace(utf8ToBinaryRegExp, utf8ToBinaryReplacer) | ||
|
||
// utf8ToAscii | ||
const utf8ToAscii: XToY = value => binaryToAscii(utf8ToBinary(value)) | ||
|
||
// asciiToBinary | ||
function asciiToBinaryReplacer(str: string) { | ||
const len = str.length | ||
const padlen = len % 4 | ||
const n = | ||
(len > 0 ? base64Table[str.charAt(0)] << 18 : 0) | | ||
(len > 1 ? base64Table[str.charAt(1)] << 12 : 0) | | ||
(len > 2 ? base64Table[str.charAt(2)] << 6 : 0) | | ||
(len > 3 ? base64Table[str.charAt(3)] : 0) | ||
const chars = [ | ||
fromCharCode(n >>> 16), | ||
fromCharCode((n >>> 8) & 0xff), | ||
fromCharCode(n & 0xff), | ||
] | ||
chars.length -= [0, 0, 2, 1][padlen] | ||
return chars.join('') | ||
} | ||
const asciiToBinary: XToY = | ||
(typeof window !== 'undefined' && window.atob) || | ||
(value => value.replace(/\S{1,4}/g, asciiToBinaryReplacer)) | ||
|
||
// binaryToUtf8 | ||
const binaryToUtf8RegExp = /[\xC0-\xDF][\x80-\xBF]|[\xE0-\xEF][\x80-\xBF]{2}|[\xF0-\xF7][\x80-\xBF]{3}/g | ||
function binaryToUtf8Replacer(str: string) { | ||
switch (str.length) { | ||
case 4: { | ||
const cp = | ||
((0x07 & str.charCodeAt(0)) << 18) | | ||
((0x3f & str.charCodeAt(1)) << 12) | | ||
((0x3f & str.charCodeAt(2)) << 6) | | ||
(0x3f & str.charCodeAt(3)) | ||
const offset = cp - 0x10000 | ||
return ( | ||
fromCharCode((offset >>> 10) + 0xd800) + | ||
fromCharCode((offset & 0x3ff) + 0xdc00) | ||
) | ||
} | ||
case 3: | ||
return fromCharCode( | ||
((0x0f & str.charCodeAt(0)) << 12) | | ||
((0x3f & str.charCodeAt(1)) << 6) | | ||
(0x3f & str.charCodeAt(2)), | ||
) | ||
default: | ||
return fromCharCode( | ||
((0x1f & str.charCodeAt(0)) << 6) | (0x3f & str.charCodeAt(1)), | ||
) | ||
} | ||
} | ||
const binaryToUtf8: XToY = value => | ||
value.replace(binaryToUtf8RegExp, binaryToUtf8Replacer) | ||
|
||
// asciiToUtf8 | ||
const asciiToUtf8: XToY = value => | ||
binaryToUtf8(asciiToBinary(value.replace(/=+$/, ''))) | ||
|
||
/** | ||
* base64Encode。 | ||
* | ||
* @param value 值 | ||
* @returns 返回结果 | ||
*/ | ||
export function base64Encode(value: string): string { | ||
if (canUseBufferFrom) { | ||
return Buffer.from(value, 'utf8').toString('base64') | ||
} | ||
|
||
return utf8ToAscii(value) | ||
} | ||
|
||
/** | ||
* base64Encode。 | ||
* | ||
* @param value 值 | ||
* @returns 返回结果 | ||
*/ | ||
export function base64Decode(value: string): string { | ||
if (canUseBufferFrom) { | ||
return Buffer.from(value, 'base64').toString('utf8') | ||
} | ||
|
||
return asciiToUtf8(value) | ||
} | ||
|
||
export function base64UrlEncode(value: string): string { | ||
return base64Encode(value) | ||
.replace(/\+/g, '-') | ||
.replace(/\//g, '_') | ||
.replace(/=+$/, '') | ||
} | ||
|
||
export function base64UrlDecode(value: string): string { | ||
return base64Decode(value.replace(/-/g, '+').replace(/_/g, '/')) | ||
} |