    {-# LANGUAGE TypeFamilies #-}

    -- Module      : Data.UUID.Builder
    -- Copyright   : (c) 2009 Mark Lentczner
    --
    -- License     : BSD-style
    --
    -- Maintainer  : markl@glyphic.com
    -- Stability   : experimental
    -- Portability : portable
    --
    -- This module provides a system that can call a function that takes
    -- a sequence of some number of Word8 arguments.
    --
    -- The twist is that the Word8 arguments can be supplied directly
    -- from Word8s, or from other sources that may provide more than
    -- one Word8 apiece. Examples are Word16 and Word32 that supply
    -- two and four Word8s respectively. Other ByteSource instances
    -- can be defined.
    --
    -- This module is admittedly overkill. There are only three places
    -- in the uuid package that need to call buildFromBytes with 16
    -- Word8 values, but each place uses Words of different lengths:
    --      version 1 uuids: 32-16-16-16-8-8-8-8-8-8
    --      version 4 uuids: 24-24-32-24-24
    --      version 5 uuids: 32-32-32-32
    -- Originally, these three constructions were hand coded but the
    -- code was ungainly. Using this module makes the code very
    -- concise, and turns out to optimize to just as fast, or faster!

    module Data.UUID.Builder
        (ByteSource(..)
        ,ByteSink
        ,Takes1Byte
        ,Takes2Bytes
        ,Takes3Bytes
        ,Takes4Bytes
        ) where

    import Data.Bits
    import Data.Word

    type Takes1Byte  g = Word8 -> g
    type Takes2Bytes g = Word8 -> Word8 -> g
    type Takes3Bytes g = Word8 -> Word8 -> Word8 -> g
    type Takes4Bytes g = Word8 -> Word8 -> Word8 -> Word8 -> g

    -- | Type of function that a given ByteSource needs.
    -- This function must take as many Word8 arguments as the ByteSource provides
    type family ByteSink w g
    type instance ByteSink Word8  g = Takes1Byte g
    type instance ByteSink Word16 g = Takes2Bytes g
    type instance ByteSink Word32 g = Takes4Bytes g
    type instance ByteSink Int    g = Takes4Bytes g

    -- | Class of types that can add Word8s to a Builder.
    -- Instances for Word8, Word16, Word32 and Int provide 1, 2, 4 and 4 bytes,
    -- respectively, into a ByteSink
    class ByteSource w where
        -- | Apply the source's bytes to the sink
        (/-/) :: ByteSink w g -> w -> g

    infixl 6 /-/

    instance ByteSource Word8 where
        f /-/ w = f w

    instance ByteSource Word16 where
        f /-/ w = f b1 b2
            where b1 = fromIntegral (w `shiftR` 8)
                  b2 = fromIntegral w

    instance ByteSource Word32 where
        f /-/ w = f b1 b2 b3 b4
            where b1 = fromIntegral (w `shiftR` 24)
                  b2 = fromIntegral (w `shiftR` 16)
                  b3 = fromIntegral (w `shiftR` 8)
                  b4 = fromIntegral w

    instance ByteSource Int where
        f /-/ w = f b1 b2 b3 b4
            where b1 = fromIntegral (w `shiftR` 24)
                  b2 = fromIntegral (w `shiftR` 16)
                  b3 = fromIntegral (w `shiftR` 8)
                  b4 = fromIntegral w