Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Common function for ByteString and ByteString.Lazy in Haskell

Tags:

haskell

I implemented function that reads ByteString and converts it in hex format. E.g. given "AA10" it converts it to [170, 16]

import qualified Data.ByteString.Lazy as BSL
import qualified Data.ByteString.Internal as BS (w2c)

rHex :: BSL.ByteString -> BSL.ByteString
rHex bs
    | BSL.null bs = BSL.empty
    | BSL.null rest' = fromHex c1 `BSL.cons` BSL.empty
    | otherwise = rChunk c1 c2 `BSL.cons` rHex rest
          where (c1, rest') = (BSL.head bs, BSL.tail bs)
                (c2, rest) = (BSL.head rest', BSL.tail rest')
                rChunk c1 c2 = (fromHex c1) * 16 + fromHex c2
fromHex = fromIntegral . digitToInt . BS.w2c

But than I realized that I need same function but for simple ByteString, not Lazy. The only approach I came to is something like this:

import qualified Data.ByteString.Lazy as BSL
import qualified Data.ByteString as BS
import qualified Data.ByteString.Internal as BS (w2c)

rHex' funcs@(null, empty, cons, head, tail, fromHex) bs
    | null bs = empty
    | null rest' = fromHex c1 `cons` empty
    | otherwise = rChunk c1 c2 `cons` rHex' funcs rest
          where (c1, rest') = (head bs, tail bs)
                (c2, rest) = (head rest', tail rest')
                rChunk c1 c2 = (fromHex c1) * 16 + fromHex c2

fromHex = fromIntegral . digitToInt . BS.w2c

rHexBSL :: BSL.ByteString -> BSL.ByteString
rHexBSL = rHex' (BSL.null, BSL.empty, BSL.cons, BSL.head, BSL.tail, fromHex)

rHexBS :: BS.ByteString -> BS.ByteString
rHexBS = rHex' (BS.null, BS.empty, BS.cons, BS.head, BS.tail, fromHex)

So I pass all needed functions directly in rHex' in functions rHexBSL rHexBS. Is there more Haskell way to make a common function for Bytestring and Bytestring.Lazy? Maybe create type class or something?

like image 436
uv.nikita Avatar asked Feb 06 '14 08:02

uv.nikita


2 Answers

I would simplify this by working with [Word8] and using pack and unpack for each type of ByteString to get what you want -- e.g.:

toHex :: Word8 -> Word8 -> Word8
toHex a b = (fromHex a) * 16 + fromHex b

hexify :: [Word8] -> [Word8] -> [Word8]
hexify (a:b:cs) = toHex a b : hexify cs
hexify [b]      = toHex 0 b
hexify []       = []

rHexBSL :: BSL.ByteString -> BSL.ByteString
rHexBSL = BSL.pack . hexify . BSL.unpack

rHexBS :: BS.ByteString -> BS.ByteString
rHexBS = BS.pack . hexify . BS.unpack

And I think doing it this way has a good chance of enabling fusion to make the operations efficient.

That said, it is instructive to see how Bryan does it in his base16-bytestring` package.

  • encode for Data.ByteString
  • encode for Data.ByteString.Lazy
like image 138
ErikR Avatar answered Jan 02 '23 13:01

ErikR


You can always use the toStrict and fromStrict functions from Data.ByteString.Lazy to convert back and forth between strict and lazy types. In this particular case:

rHexBS = BSL.toStrict . hHexBSL . BSL.fromStrict
like image 41
Danny Navarro Avatar answered Jan 02 '23 11:01

Danny Navarro