从Bytestring获取任意切片位

时间:2013-03-11 20:04:01

标签: haskell binary bits bytestring bitstream

我想使用惰性Bytestring来表示比特流。我需要能够有效地从该流中获取任意位片。例如,我可能有一个长度为10的ByteString,我想要从原来的ByteString中切出一个新的ByteString,其中包含24-36位。

问题是ByteStringsWord8 s的数组,因此难以获取不是8的倍数的范围。我能够提出的最好的方法是使用Data.BinaryData.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下降到该索引
  • 如果位范围的低端不是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

3 个答案:

答案 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