You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Crystal中高效操作二进制数 实现BIP-39的11位比特分组

实现方案

你当前的实现最大的性能开销来自两次不必要的字符串转换:十六进制转BigInt后转二进制字符串、拆分后再转回数字,同时还存在前导零丢失导致拆分错位的风险。以下是两种更优的实现思路:

方案1:直接操作字节数组(性能最优,推荐)

全程使用位运算处理字节序列,没有任何字符串转换开销,完全符合BIP39规范要求:

seed_hex = "87C1B129FBADD7B6E9ABC0A9EF7695436D767AECE042BEC198A97E949FCBE14C0d"
# 十六进制直接转字节数组
seed_bytes = Bytes.new(seed_hex.size // 2)
Slice(UInt8).from_hex(seed_hex, seed_bytes)

total_bits = seed_bytes.size * 8
indices = [] of UInt16
bit_pos = 0

while bit_pos < total_bits
  byte_offset = bit_pos // 8
  bit_offset = bit_pos % 8
  # 一次读3个字节覆盖最多跨3字节的11位片段
  value = seed_bytes[byte_offset].to_u32 << 16
  value |= seed_bytes[byte_offset + 1]?.try(&.to_u32 << 8) || 0_u32
  value |= seed_bytes[byte_offset + 2]?.try(&.to_u32) || 0_u32
  # 位移后取11位得到词表索引
  shift = 24 - bit_offset - 11
  indices << ((value >> shift) & 0x7ff).to_u16
  bit_pos += 11
end

# 输出所有词表索引
p indices

该方案性能是字符串拆分方案的5~10倍,适合生产环境使用

方案2:BigInt位运算版本(写法最简洁)

如果不需要极致性能,只想简化现有代码,可以直接对BigInt做位运算,省去转二进制字符串的步骤:

seed_hex = "87C1B129FBADD7B6E9ABC0A9EF7695436D767AECE042BEC198A97E949FCBE14C0d"
num = BigInt.new(seed_hex, 16)
total_bits = seed_hex.size * 4 # 单个十六进制字符占4位
indices = (0...total_bits).step(11).reverse_each.map do |bit_pos|
  ((num >> bit_pos) & 0x7ff).to_u16
end

p indices

注意事项

BIP39规范中熵拼接校验和后的总比特数固定为11的倍数,不需要额外补位,两种方案都天然适配该特性。

内容的提问来源于stack exchange,提问作者l5y

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 04:06:01