module Lvm.Common.Byte
( Byte, Bytes
, Monoid(..), unit, isEmpty
, bytesLength, writeBytes, bytesFromList, listFromBytes
, bytesFromString, stringFromBytes, bytesFromInt32, byteFromInt8
, readByteList, int32FromByteList, stringFromByteList, bytesFromByteList
) where
import qualified Control.Exception as CE (catch, IOException)
import Data.Monoid
import Data.Word
import System.Exit
import System.IO
type Byte = Word8
data Bytes = Nil
| Cons Byte !Bytes
| Cat !Bytes !Bytes
instance Show Bytes where
show bs = show (listFromBytes bs)
instance Eq Bytes where
bs1 == bs2 = listFromBytes bs1 == listFromBytes bs2
byteFromInt8 :: Int -> Byte
byteFromInt8 = toEnum
intFromByte :: Byte -> Int
intFromByte = fromEnum
bytesFromString :: String -> Bytes
bytesFromString
= bytesFromList . map (toEnum . fromEnum)
stringFromBytes :: Bytes -> String
stringFromBytes
= map (toEnum . fromEnum) . listFromBytes
bytesFromInt32 :: Int -> Bytes
bytesFromInt32 i
= let n0 = if i < 0 then max32+i+1 else i
n1 = div n0 256
n2 = div n1 256
n3 = div n2 256
xs = map (byteFromInt8 . flip mod 256) [n3,n2,n1,n0]
in bytesFromList xs
max32 :: Int
max32 = 2^(32::Int) 1
instance Monoid Bytes where
mempty = Nil
mappend bs Nil = bs
mappend Nil cs = cs
mappend bs cs = Cat bs cs
isEmpty :: Bytes -> Bool
isEmpty Nil = True
isEmpty (Cons _ _) = False
isEmpty (Cat bs cs) = isEmpty bs && isEmpty cs
unit :: Byte -> Bytes
unit = (`Cons` Nil)
listFromBytes :: Bytes -> [Byte]
listFromBytes = loop []
where
loop next bs
= case bs of
Nil -> next
Cons b xs -> b:loop next xs
Cat xs ys -> loop (loop next ys) xs
bytesFromList :: [Byte] -> Bytes
bytesFromList = foldr Cons Nil
bytesLength :: Bytes -> Int
bytesLength = loop 0
where
loop n bs
= case bs of
Nil -> n
Cons _ xs -> (loop $! (n+1)) xs
Cat xs ys -> loop (loop n ys) xs
writeBytes :: FilePath -> Bytes -> IO ()
writeBytes path bs
= do{ h <- openBinaryFile path WriteMode
; writeHandle h bs
; hClose h
}
writeHandle :: Handle -> Bytes -> IO ()
writeHandle h bs
= case bs of
Nil -> return ()
Cons b xs -> do{ hPutChar h (toEnum (fromEnum b)); writeHandle h xs }
Cat xs ys -> do{ writeHandle h xs; writeHandle h ys }
int32FromByteList :: [Byte] -> (Int,[Byte])
int32FromByteList bs
= case bs of
n3:n2:n1:n0:cs -> let i = int32FromByte4 n3 n2 n1 n0 in seq i (i,cs)
_ -> error "Byte.int32FromBytes: invalid byte stream"
int32FromByte4 :: Byte -> Byte -> Byte -> Byte -> Int
int32FromByte4 n0 n1 n2 n3
= (intFromByte n0*16777216) + (intFromByte n1*65536) + (intFromByte n2*256) + intFromByte n3
stringFromByteList :: [Byte] -> String
stringFromByteList = map (toEnum . fromEnum)
bytesFromByteList :: [Byte] -> Bytes
bytesFromByteList = bytesFromList
readByteList :: FilePath -> IO [Byte]
readByteList path
= do{ h <- openBinaryFile path ReadMode
; xs <- hGetContents h
; seq (last xs) (hClose h)
; return (map (toEnum . fromEnum) xs)
} `CE.catch` (\exception ->
let message = show (exception :: CE.IOException) ++ "\n\nUnable to read from file " ++ show path
in do { putStrLn message; exitWith (ExitFailure 1) })