{-# LANGUAGE BangPatterns #-} {- | Module : Data.Digest.TigerHash Copyright : (c) Orlyuk Nikolay 2010 License : GPL-2 Maintainer : virkony@gmail.com Stability : provisional There comes some kind of description how to use this module. Assume next import: > import Data.Digest.TigerHash Typical instant usage: > instance TigerHashable k => TigerHashable (k, Message) where > tigerHashUpdate ctx_ (key, Message {sender = data0, body = data1}) = do > tigerHashUpdate ctx_ key > tigerHashUpdate ctx_ data0 > tigerHashUpdate ctx_ data1 > > signMessage :: TigerHashable k => k -> Message -> SignedMessage > signMessage pkey msg = SignedMessage { message = msg, sign = tigerHash (pkey, msg) } This is pretty useful when you need to send signed messages over public channel. But using this in a such functional way have its drawbacks. Each time system requires calculation of @hash@ it will issue prepearing of new context for each calculation instead of using the same context. To solve that there is function for processing lazy list: > hashMessageSenders :: [Message] -> [(TigerHash, Message)] > hashMessageSenders msgs = zip (tigerHashList senders) msgs > where senders = map sender msgs This can be used for building hashed storage, which requires hash of each element. Notice that while you expand each node of the list 'tigerHashList' will calculate it's @head@ for you. That's done with intention to loose overhead while hashing files for DC++ . -} module Data.Digest.TigerHash (TigerHash, TigerHashable(..), hexTigerHash, b32TigerHash) where import System.IO.Unsafe import Foreign.ForeignPtr import Foreign.Ptr import Text.Show import Data.ByteString.Internal (inlinePerformIO) import qualified Data.ByteString.Lazy as LBS import Data.Binary import Data.Binary.Put import Data.Binary.Get import qualified Codec.Binary.Base16 as B16 import qualified Codec.Binary.Base32 as B32 import Control.Monad import Data.Digest.TigerHash.Internal -- | render 'TigerHash' to 'String' as hex-dump hexTigerHash :: TigerHash -> String hexTigerHash = B16.encode . LBS.unpack . runPut . put -- | render 'TigerHash' to 'String' using Base32 encoding (as used in magnet-links and etc.) b32TigerHash :: TigerHash -> String b32TigerHash = B32.encode . LBS.unpack . runPut . put instance Show TigerHash where showsPrec _ th = (++) (b32TigerHash th) instance Binary TigerHash where put (TigerHash a b c) = putWord64host a >> putWord64host b >> putWord64host c get = do a <- getWord64host b <- getWord64host c <- getWord64host return (TigerHash a b c) class TigerHashable a where -- | Each 'TigerHashable' data should implement this using 'updateContext' of -- 'TigerContext' class from "Data.Digest.TigerHash.Internal". But usually -- there is enough to just call 'tigerHashUpdate' for data which already -- have instance for 'TigerHashable'. tigerHashUpdate :: (TigerContext (Ptr c)) => Ptr c -> a -> IO () -- | Instant caluculation of Tiger Hash with stack allocated context. tigerHash :: a -> TigerHash tigerHash x = inlinePerformIO . withTigerContext $ \ctx -> do tigerHashUpdate ctx x finalizeContext ctx -- | Same as 'tgerHash', but with Tiger Tree hashing algorithm tigerTreeHash :: a -> TigerHash tigerTreeHash x = inlinePerformIO . withTigerTreeContext $ \ctx -> do tigerHashUpdate ctx x finalizeContext ctx -- | Calculate sequence of hashes where each next is calculated on-demand -- and /only after previous one/ using one context for all calculations. -- Be sure to prepare sequence wich contains /only required for hashing/ -- entries. tigerHashList :: [a] -> [TigerHash] tigerHashList [] = [] tigerHashList (x0:xs) = unsafePerformIO $ do ctx <- newTigerContext let mcomb x mys = unsafeInterleaveIO $ do -- list structure is lazy y <- withForeignPtr ctx $ \ctx_ -> do resetContext ctx_ tigerHashUpdate ctx_ x finalizeContext ctx_ liftM (y:) mys -- no need to resetContext after newContext y0 <- withForeignPtr ctx $ \ctx_ -> do tigerHashUpdate ctx_ x0 finalizeContext ctx_ liftM (y0:) $ foldr mcomb (return []) xs {-# NOINLINE tigerHashList #-} -- | Same as 'tigerHashList', but with Tiger Tree hashing algorithm tigerTreeHashList :: [a] -> [TigerHash] tigerTreeHashList [] = [] tigerTreeHashList (x0:xs) = unsafePerformIO $ do ctx <- newTigerTreeContext let mcomb x mys = unsafeInterleaveIO $ do -- list structure is lazy y <- withForeignPtr ctx $ \ctx_ -> do resetContext ctx_ tigerHashUpdate ctx_ x finalizeContext ctx_ liftM (y:) mys -- no need to resetContext after newContext y0 <- withForeignPtr ctx $ \ctx_ -> do tigerHashUpdate ctx_ x0 finalizeContext ctx_ liftM (y0:) $ foldr mcomb (return []) xs {-# NOINLINE tigerTreeHashList #-}