我想使用惰性Bytestring
来表示比特流。我需要能够有效地从该流中获取任意位片。例如,我可能有一个长度为10的ByteString
,我想要从原来的ByteString
中切出一个新的ByteString
,其中包含24-36位。
问题是ByteStrings
是Word8
s的数组,因此难以获取不是8的倍数的范围。我能够提出的最好的方法是使用Data.Binary
和Data.Binary.Bits
。请注意,get32BitRange
专门针对范围< = 32。
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
下降到该索引Word8
的开头会有一些多余的位,所以跳过那些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
答案 0 :(得分:2)
我认为其他解决方案更好但您可以使用内部模块来获取基础结构:http://hackage.haskell.org/packages/archive/bytestring/0.10.2.0/doc/html/src/Data-ByteString-Internal.html#ByteString
data ByteString = PS {-# UNPACK #-} !(ForeignPtr Word8) -- payload
{-# UNPACK #-} !Int -- offset
{-# UNPACK #-} !Int -- length
然后,您可以使用标准指针工具生成ByteString
指向您想要的位置,方法是直接操作ForeignPtr
...
答案 1 :(得分:1)
使用ByteString
作为API类型无法提高效率,因为它不会带有您想要的“位”真正从第一个字节偏移开始的信息。
最好的办法是制作一个包装类型:
data BitStream =
BitStream {
info :: ByteString,
-- values from 0-7: ignore all bits in the first byte up to
-- but not including this offset
firstBitOffset :: !Int,to but not including this offset
-- values from 0-7: ignore all bits in the last byte after
-- but not including this offset
lastBitOffset :: !Int
}
然后你可以围绕这个设计一个基于位的API。
答案 2 :(得分:1)
我要将此标记为已解决。这就是我最终使用的:
get32BitRange :: Int -> Int -> ByteString -> Word32
get32BitRange lo hi = assert (lo < hi) $
runGet (runBitGet bitGet)
where bitGet = block $ byteString byteOff
*> word8 bitOff
*> word32be len
len = hi - lo
(byteOff, bitOff) = lo `quotRem` 8