【问题标题】:Get Arbitrary Slices of Bits from Bytestring从字节串中获取任意位片
【发布时间】:2013-03-11 20:04:01
【问题描述】:

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

问题在于ByteStringsWord8s 的数组,因此取不是8 的倍数的范围很困难。我能想到的最好的就是这个,使用Data.BinaryData.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


【解决方案1】:

我认为其他解决方案要好得多但是您可以使用 Internal 模块来获取底层结构: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

然后您可以使用标准指针工具生成ByteStrings,通过直接操作ForeignPtr 来精确指向您想要的位置...

【讨论】:

    【解决方案2】:

    您无法将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。

    【讨论】:

    • 这肯定有助于清理我的示例函数,但我对实际提取位切片的方法更感兴趣。
    • 之后你想用它们做什么?
    • 将它们解析为二进制数据,可能是WordInt 类型
    【解决方案3】:

    我将把它标记为已解决。这就是我最终使用的:

    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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-11-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多