惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
Tenable Blog
D
DataBreaches.Net
S
Secure Thoughts
B
Blog
S
Schneier on Security
Y
Y Combinator Blog
P
Proofpoint News Feed
C
Cybersecurity and Infrastructure Security Agency CISA
C
Cyber Attacks, Cyber Crime and Cyber Security
D
Darknet – Hacking Tools, Hacker News & Cyber Security
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
F
Full Disclosure
Engineering at Meta
Engineering at Meta
L
LangChain Blog
T
Threatpost
阮一峰的网络日志
阮一峰的网络日志
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
The Hacker News
The Hacker News
月光博客
月光博客
大猫的无限游戏
大猫的无限游戏
Cyberwarzone
Cyberwarzone
T
The Exploit Database - CXSecurity.com
雷峰网
雷峰网
博客园 - 司徒正美
Help Net Security
Help Net Security
www.infosecurity-magazine.com
www.infosecurity-magazine.com
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Application and Cybersecurity Blog
Application and Cybersecurity Blog
P
Privacy International News Feed
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
V
Vulnerabilities – Threatpost
Cisco Talos Blog
Cisco Talos Blog
L
LINUX DO - 热门话题
酷 壳 – CoolShell
酷 壳 – CoolShell
T
Threat Research - Cisco Blogs
Recent Announcements
Recent Announcements
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
H
Heimdal Security Blog
Jina AI
Jina AI
C
Cisco Blogs
Attack and Defense Labs
Attack and Defense Labs
Microsoft Security Blog
Microsoft Security Blog
L
Lohrmann on Cybersecurity
aimingoo的专栏
aimingoo的专栏
D
Docker
I
Intezer
C
Check Point Blog
Cloudbric
Cloudbric
小众软件
小众软件
V2EX - 技术
V2EX - 技术

hsfzxjy 的博客

解决 VSCode + CMake + MSVC 编译器信息乱码的问题 使用 3090 部署 1.58bit 动态量化版 DeepSeek R1 671b 如何在 VS Code DevContainer 中配置 HTTP 代理 如何在跳板机背后的服务器上使用 VS Code Remote - Containers Cohesive Digests for Ints and Floats Rust 中的隐匿概念 —— Place(位置) 美术馆 一尺之槌,日取其半,1075日而竭 老生常谈:使用 Cloudflare 自选 IP 加速站点访问 辩义 State、Nation 与 Country 折腾 NPU·第1章 —— 搭建 Level Zero 开发环境 折腾 NPU·第0章 —— Intel NPU 概述与 Level-Zero 新增域名 monad.run CSS 中为特定字符设置不同字体 Arbitary Lifetime Transmutation via Rust Unsoundness Dijkstra 算法的延伸 Manacher 回文计数算法 硬卧 Go Fact: Zero-sized Field at the Rear of a Struct Has Non-zero Size Display *big.Rat Losslessly and Smartly in Golang 代码的仪式 Building Electron From Scratch 中式亲属称谓研究之一:构建半群 Some Notes on Kotlin Coroutines Git sparse-checkout and partial clones for Mega-Repos 辩义“封建” Diving from the CUDA Error 804 into a bug of libnvidia-container Modern Cryptography, GPG and Integration with Git(hub) Move the Root Partition of Ubuntu A New Programmer Kicks a Roadblock Git-based Dependencies in Dart and Go Reversy Naming 人类一败涂地 Invalid Golang Pointers Can Bite You Even If You Don't Dereference Side Project(副业) A Flaw of Promoting Complex Trait Bounds in Rust Initialize Process Pool Worker with Individual Value Rust - Python FFI From Scratch [Extending Hexo For My Site] Part 1 [Extending Hexo For My Site] Part 0 Debug a 'torch.tensor(1).cuda()' hanging 不自由的互联网 Retrieve Contents over HTTP without curl or wget [Unravelling mocona] Part 1 - Verbosity or Anti-Pattern [Unravelling mocona] Part 0 - Preface Understanding pickle in Python Rough Notes on Deploying Vaultwarden & NextCloud Bookmarks 语言狂热者与实用主义者 Demystify the randomness in CUDA kernels Performant Bulk Mutations in IndexedDB Auto Rebuild .pyx Files with pyximport Cython and Threads Obtain a Random Available TCP Port with Bash Information Theory: KL Divergence Information Theory: Entropy and Mutual Information 铁板烧 西郊线 Proof of the Gumbel Max Trick Option::as_ref Rc, RefCell and Interior Mutability Visualizing Correlation 三月十日杂感 三月一日杂感 二月十一日杂感 一月二十六日杂感 SS Configuration 一月七日杂感 四月·病 Haskell 笔记:State Monad Haskell 笔记:Monad 引论 Haskell 笔记:Applicative Haskell 笔记:Category Theory and Functor Haskell 笔记:data, type, newtype Haskell 笔记:folds 使用 Aria2 在 Ubuntu 中下载百度云资源 从伪并行的 Python 多线程说起 一个 Reentrant Error 引发的对 Python 信号机制的探索和思考 Linux 文件权限 HSFZMUN 4.0 部署小记 午后雨·科大 最后的雨夜·广州 揭秘·变态的平方根倒数算法 神坑·Python 装饰类无限递归 Python“黑魔法”之 Encoding & Decoding Ubuntu 重新映射键盘布局 为什么我要翻墙 Python“黑魔法”之 Generator Coroutines 数学美 之 判断线段相交的最简方法 除夕杂感 17 行代码实现的简易 Javascript 字符串模板 Python“黑魔法”之 Meta Classes 诗集 生活,需要被“发现” 家书·十八岁成人礼 炫技?还是需求? 【译】响应式图片的现状 【译】“为什么有这么多的编程语言?” Wisecity 商赛总结——也谈前端自动化测试 记一次 DoS 诈骗网站的经历 那一年,我们望向星空
将 Base64 编码的数据快速转换为 Uint8Array
2024-08-04 · via hsfzxjy 的博客

为了方便节省请求数,有时我们会将二进制数据以 Base64 编码的形式嵌入 HTML 或 JavaScript 中,再于运行时解码成 Uint8Array,进行后续运算。然而浏览器没有提供直接的 API 来完成这种解码转换,需要我们自己实现。本文介绍两种快速的解码方法。

数据准备

const data = Array.from({ length: 100 * 1024 }, () => Math.floor(Math.random() * 256))
const raw = String.fromCharCode(...data)
const b64data = btoa(raw)

为测试不同解码方法的性能,我们生成了一组 100KB 的随机数据,并将其转换为 Base64 编码的字符串 b64datab64data 将作为解码函数的输入,以测试不同解码方法的性能。

方法一:循环拷贝

function decode1(b64data) {
const raw = atob(b64data)
const buf = new Uint8Array(raw.length)
for (let i = 0; i < raw.length; i++) buf[i] = raw.charCodeAt(i)

return buf
}

方法一最为直接。首先使用 atob 函数将 b64data 解码为包含原始数据的字符串 raw,再通过一个循环拷贝 raw 的各个字符点位到字节数组 buf 中。这种方式的计算密集部分都是由 JS 完成的,尽管 V8 引擎有 JIT,运行性能仍是不理想:

console.time("decode1")
decode1(b64data)
console.timeEnd("decode1")

方法二:使用 TextEncoder

注意 该方法解码的结果和原始数据是不等价的,请阅读下面分析后再决定是否使用。

function decode2(b64data) {
const raw = atob(b64data)
const buf = new Uint8Array(raw.length * 2)
const { written } = new TextEncoder().encodeInto(raw, buf)
return buf.subarray(0, written)
}

有没有其他捷径能取代方法一中的热循环呢?答案是使用 TextEncoder.encodeInto(str, buf) 函数。这个函数会将字符串 str 编码为 UTF-8 并写入到字节数组 buf 中。这一切都是在底层的 C++ 代码中完成的,因此性能会更好。以相同方式测试,可以观察到 decode2 的耗时仅有 decode1 的 60%。

console.time("decode2")
decode2(b64data)
console.timeEnd("decode2")

decode2 的弊端和应用场景

读者可能会奇怪,为什么 decode2 中将 buf 的长度初始化为 raw.length * 2 而非 raw.length 呢?这是因为 TextEncoder.encodeInto 会将字符串编码为 UTF-8,我们得到的其实是原始数据的 UTF-8 表示。由于 b64data 编码自字符串 raw,而 raw 的每个字符范围为 [0, 255],在 UTF-8 编码后,字符范围为 [128, 255] 的部分会变成两个字节。因此,我们需要将 buf 的长度初始化为 raw.length * 2 作为最大字节数上限。TextEncoder.encodeInto 返回的 written 字段会告诉我们实际写入的字节数,我们可以通过 buf.subarray(0, written) 来截取有效部分。

可惜,TextEncoder 默认只能编码 UTF-8。如果它支持 Latin-1 编码,decode2 的解码结果便会和原始数据完全等价。那既然 decode2 的结果和原始数据不等价,那它岂不是无用?也不尽然。

  • 如果原始数据的每个字节范围都是 [0, 127],那么 decode2 的结果和原始数据是等价的。这是因为 ASCII 字符在 UTF-8 编码中仍然是单字节的。因此,如果你的数据确保了这一点,那么 decode2 会是一个更快的解码方法。

  • 如果数据的下游能接受这种“异化”后的解码结果,decode2 也可被使用。一个例子是解码后的数据将作为 WebAssembly 模块的输入。我们可以在编写 WASM 代码时提前考虑到这一点,实现一个简单的“双字节 UTF-8 -> 单字节”的转换函数,即可享受 decode2 的性能,并同时兼顾数据的正确性。


作者:hsfzxjy
链接:
许可:CC BY-NC-ND 4.0.
著作权归作者所有。本文不允许被用作商业用途,非商业转载请注明出处。