module Data.Multihash.Digest where import Control.Applicative ((<$>)) import Data.Attoparsec.ByteString (Parser, parseOnly) import qualified Data.Attoparsec.ByteString as A import qualified Data.ByteString as BS import Data.ByteString.Builder (Builder, byteString, toLazyByteString) import qualified Data.ByteString.Builder as BB import qualified Data.ByteString.Lazy as BL import Data.Monoid ((<>)) import Data.Word (Word8) data MultihashDigest = MultihashDigest { algorithm :: !HashAlgorithm , length :: !Length , digest :: !Digest } deriving (Show, Eq) type Length = Int type Digest = BS.ByteString data HashAlgorithm = SHA1 | SHA256 | SHA512 | SHA3 | BLAKE2B | BLAKE2S deriving (Show, Read, Eq, Enum, Bounded) fromCode :: Int -> HashAlgorithm fromCode 0x11 = SHA1 fromCode 0x12 = SHA256 fromCode 0x13 = SHA512 fromCode 0x14 = SHA3 fromCode 0x40 = BLAKE2B fromCode 0x41 = BLAKE2S fromCode _ = error "Unknown hash funciton code" toCode :: HashAlgorithm -> Int toCode SHA1 = 0x11 toCode SHA256 = 0x12 toCode SHA512 = 0x13 toCode SHA3 = 0x14 toCode BLAKE2B = 0x40 toCode BLAKE2S = 0x41 encode :: HashAlgorithm -> Digest -> BL.ByteString encode h d = toLazyByteString $ encoder h d encoder :: HashAlgorithm -> Digest -> Builder encoder h d = (BB.word8 . fromIntegral $ toCode h) <> (BB.word8 . fromIntegral $ BS.length d) <> byteString d decode :: BS.ByteString -> Either String MultihashDigest decode = parseOnly decoder decoder :: Parser MultihashDigest decoder = do h <- (fromCode . fromIntegral <$> A.anyWord8) l <- (fromIntegral <$> A.anyWord8) d <- A.take l return $ MultihashDigest h l d