【发布时间】:2013-03-11 20:04:01
【问题描述】:
我想用一个懒惰的Bytestring 来表示一个比特流。我需要能够有效地从该流中获取任意位片。例如,我可能有一个长度为 10 的ByteString,我想从原始ByteString 中分割一个新的ByteString,其中包含24-36 位。
问题在于ByteStrings 是Word8s 的数组,因此取不是8 的倍数的范围很困难。我能想到的最好的就是这个,使用Data.Binary 和Data.Binary.Bits。请注意,get32BitRange 专门用于范围
get32BitRange :: Int -> Int -> ByteString -> ByteString
get32BitRange lo hi = runPut . putWord32be
. runGet (runBitGet . block $ word8 (8 - (lo `quot` 8)) *> word32be len)
. drop offset
where len = hi - lo
lo' = lo `div` 8
offset = fromIntegral lo' - 1
算法是:
- 找到第一个包含我想要的位的
Word8的索引 - 从
ByteString下降到该索引 - 如果位范围的低端不是 8 的倍数,
Word8开头会有一些多余的位,所以跳过那些 - 获取(hi - lo)位,并存储在
Word32 - 将
Word32放入ByteString
它看起来有点难看,有没有更有效的方法可以从ByteString 中获取任意片段?
编辑:这是一个更高效的版本
get32BitRange :: Int -> Int -> ByteString -> Word32
get32BitRange lo hi = runGet get
where get = runBitGet . block $ byteString byteOff *> word8 bitOff *> word32be len
len = hi - lo
(byteOff, bitOff) = lo `quotRem` 8
【问题讨论】:
-
你知道吗?如果它包含
Bool,那么简单、无聊的旧UArray已经使用了一个非常紧凑的表示?为什么不使用它? -
@DanielWagner:我没有想到这一点,这将是解决我的问题的一个优雅方案,但不幸的是我需要使用懒惰的
ByteStrings,我认为我不能在转换为UAarray或未装箱的Vector时保持懒惰。我可以尝试一个盒装的表示,看看它是如何公平的,但效率是关键。
标签: haskell binary bits bytestring bitstream