Вопрос

I am trying to to little endian conversion in haskell, so that I can turn a Word16 into two Word8 (for example 258 = 1*256 + 2, so result should be [2, 1]). I then pack the result into a ByteString.

I created the following code for this purpose:

import Data.Word
import Data.Bits

getByte b num = shift (relevantBits b num) (shiftNum b)
    where bitMask b = sum $ map (2^) [8*b-8 .. 8*b-1]
          relevantBits b num = num .&. bitMask b
          shiftNum b = 8-8*b

encodeWord16 x = [getByte 1 x, getByte 2 x]

input :: Word16
input = 355

output :: [Word8]
output = encodeWord16 input

The function getByte gets Byte number b from a number num. The function encodeWord16 uses this helper function to do the little endian conversion.

This however does not compile, I get the error:

Couldn't match expected type `Word8' with actual type `Word16'
In the first argument of `encodeWord16', namely `input'
In the expression: encodeWord16 input
In an equation for `output': output = encodeWord16 input

I (very unsystematically) tried to achieve the desired result by randomly distributing fromIntegral expressions, but obviously my understanding of the haskell type system is not good enough to solve this problem. Is there a systematic way to approach this problem? Basically I want the function encodeWord16 to have the type signature Word16 -> [Word8].

Это было полезно?

Решение 3

How about extract those bytes directly? Like this:

encodeWord16 x = [ x .&. 0xFF, (x .&. 0xFF00) `shiftR` 8 ]

If you want the signature of encodeWord16 be Word16 -> [Word8], then add map fromIntegral before it, like this:

encodeWord16 :: Word16 -> [Word8]
encodeWord16 x = map fromIntegral [ x .&. 0xFF, (x .&. 0xFF00) `shiftR` 8 ]

Другие советы

fromIntegral can be used for conversion between various integral types.

fromIntegral :: (Num b, Integral a) => a -> b

encodeWord16 :: Word16 -> [Word8]
encodeWord16 x = map fromIntegral [getByte 1 x, getByte 2 x]

It'd be nicer though to have getByte return Word8-s:

getByte :: Int -> Word16 -> Word8
getByte b num = fromIntegral $ shift (relevantBits b num) (shiftNum b)
    -- where ...

Instead of coding the conversion by hand, you might want to use the predefined functions to do so.

import Data.Word
import Data.ByteString.Builder
import Data.ByteString.Lazy (unpack)

encodeWord16 :: Word16 -> [Word8]
encodeWord16 = unpack . toLazyByteString . word16LE

binary contains the following code:

-- Words16s are written as 2 bytes in big-endian (network) order
instance Binary Word16 where
    put     = putWord16be

(http://hackage.haskell.org/package/binary-0.7.1.0/docs/Data-Binary.html#g:1)

-- | Write a Word16 in big endian format
putWord16be :: Word16 -> Builder
putWord16be w = writeN 2 $ \p -> do
    poke p               (fromIntegral (shiftr_w16 w 8) :: Word8)
    poke (p `plusPtr` 1) (fromIntegral (w)              :: Word8)

(http://hackage.haskell.org/package/binary-0.7.1.0/docs/Data-Binary-Builder.html#g:5)

So you can just use it like this:

> encode (355 :: Word16)
"\SOHc"
> toLazyByteString $ putWord16be 355
"\SOHc"
> index (encode (355 :: Word16)) 0
1
> index (toLazyByteString $ putWord16be 355) 0
1
> index (encode (355 :: Word16)) 1
99
> index (toLazyByteString $ putWord16be 355) 1
99
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top