如何对取值小于8的uint8切片进行3位紧凑编解码?
3位紧凑编解码uint8切片的简便实现
嘿,这个问题我之前也碰到过——把每个值小于8(正好占3位)的uint8切片做紧凑编解码,确实不用搞那种繁琐的手动字节拼接!我给你分享几个Go里简洁的实现思路,核心就是利用位运算直接操作每个bit的位置,比你想的要简单很多:
编码方法
编码的核心逻辑是把每个3位的数值依次填充到输出字节的对应bit位里,8位字节可以容纳2个完整的3位数值(占6位),剩下的2位会和下一个数值的部分bit跨字节存储。这里直接用位运算就能搞定,不用复杂的字节拼接:
import "fmt" func EncodeThreeBitSlice(input []uint8) ([]byte, error) { if len(input) == 0 { return nil, nil } // 校验输入值是否都小于8 for _, val := range input { if val >= 8 { return nil, fmt.Errorf("value %d exceeds 3-bit range (0-7)", val) } } totalBits := len(input) * 3 // 计算需要的字节数,向上取整 outputLen := (totalBits + 7) / 8 output := make([]byte, outputLen) for i, val := range input { startBit := i * 3 byteIdx := startBit / 8 // 字节内从高位开始的偏移位(比如startBit%8=0时,占用字节的bit7、6、5) bitOffset := 7 - (startBit % 8) // 将当前值的3位写入对应位置 output[byteIdx] |= (val & 0x07) << bitOffset // 处理跨字节的情况:当当前字节剩余空间不足3位时,把剩下的bit写入下一个字节 if bitOffset < 2 { nextByteIdx := byteIdx + 1 if nextByteIdx < len(output) { remainingBits := 3 - (bitOffset + 1) output[nextByteIdx] |= (val & 0x07) >> (bitOffset + 1) } } } return output, nil }
编码逻辑说明
- 先校验输入值是否都在0-7范围内,避免非法数据导致编码错误
- 计算总bit数后向上取整得到输出字节的长度
- 逐个处理每个输入值,计算它在输出字节中的位置:
startBit是当前值的起始bit位置(从0开始计数)byteIdx是该起始bit所在的字节索引bitOffset是在字节内从高位开始的偏移量
- 处理跨字节的情况:如果当前字节剩余空间不足3位,就把剩下的bit移到下一个字节的高位
解码方法
解码是编码的逆过程,需要从字节切片中依次取出每个3位的数值。这里需要注意:解码时必须知道原始切片的元素个数(因为编码后的字节可能有填充的无效bit),所以可以把个数作为参数传入,或者在编码后的切片开头存储这个数值。
func DecodeThreeBitSlice(input []byte, elementCount int) ([]uint8, error) { if elementCount <= 0 { return nil, nil } totalRequiredBits := elementCount * 3 if len(input)*8 < totalRequiredBits { return nil, fmt.Errorf("input byte slice is too short for %d elements", elementCount) } output := make([]uint8, elementCount) for i := range output { startBit := i * 3 byteIdx := startBit / 8 bitOffset := 7 - (startBit % 8) // 先从当前字节取出对应位 val := (input[byteIdx] >> bitOffset) & 0x07 // 处理跨字节的情况:从下一个字节取出剩余的bit if bitOffset < 2 { nextByteIdx := byteIdx + 1 if nextByteIdx < len(input) { remainingBits := 3 - (bitOffset + 1) val |= (input[nextByteIdx] >> (8 - remainingBits)) & ((1 << remainingBits) - 1) } } output[i] = val } return output, nil }
解码逻辑说明
- 先校验输入字节切片的长度是否足够容纳指定个数的3位数值
- 逐个计算每个输出值对应的bit位置,先从当前字节取出部分,再处理跨字节的剩余bit
- 把取出的bit组合成完整的3位数值,存入输出切片
使用示例
import "log" func main() { original := []uint8{1, 3, 5, 7, 2, 4} encoded, err := EncodeThreeBitSlice(original) if err != nil { log.Fatal(err) } fmt.Printf("Encoded bytes: %v\n", encoded) decoded, err := DecodeThreeBitSlice(encoded, len(original)) if err != nil { log.Fatal(err) } fmt.Printf("Original: %v\nDecoded: %v\n", original, decoded) // 输出: // Original: [1 3 5 7 2 4] // Decoded: [1 3 5 7 2 4] }
这种方法完全基于位运算,没有复杂的字节拼接逻辑,代码简洁且效率很高,非常适合处理这种固定长度的紧凑编码需求。
内容的提问来源于stack exchange,提问作者Jasper
相关产品推荐
相关产品推荐

