packages feed

uuid 1.1.1 → 1.2.0

raw patch · 12 files changed

+842/−318 lines, 12 filesdep +HUnitdep +QuickCheckdep +containersdep ~Cryptodep ~basedep ~binarynew-component:exe:benchuuidnew-component:exe:testuuid

Dependencies added: HUnit, QuickCheck, containers, criterion, parallel

Dependency ranges changed: Crypto, base, binary, bytestring, maccatcher, random, time

Files

+ CHANGES view
@@ -0,0 +1,23 @@+1.2.0+  (Contributors: Antoine Latter & Mark Lentczner)++- added functions toByteString and fromByteString+- added 'nil' UUID+- added unit tests and benchmarks, built when configured -ftest+- major speed up of to/from functions (as well as in general)+- added version-3 generation (deterministic based on MD5)+- major changes to internal representation+	- now uses four strict Word32 values+	- internal ByteSource classes for easy construction (see Builder.hs)+- Storable instance now stores in memory as system libraries in C do:+    16 bytes derived from the network order of the fields, no matter what+    the host native endianess is.+- fixed bugs in V1 time and clock stepping, and V1 generated values+- builds cleanly under GHC's -Wall+- added CHANGES file++1.1.1++- no longer exporting 'null' from the prelude+- add 'null' predicate on UUIDs+- documentation fix (thanks Mark Lentczner)
+ CONTRIBUTORS view
@@ -0,0 +1,6 @@+In order of appearance:++Antoine Latter+Jason Dusek+Tim Newsham+Mark Lentczner
Data/UUID.hs view
@@ -12,6 +12,7 @@ -- This library is useful for comparing, parsing and -- printing Universally Unique Identifiers. -- See <http://en.wikipedia.org/wiki/UUID> for the general idea.+-- See <http://tools.ietf.org/html/rfc4122> for the specification. -- -- For generating UUIDs, check out 'Data.UUID.V1', 'Data.UUID.V5' and -- 'System.Random'.@@ -19,7 +20,10 @@ module Data.UUID(UUID                 ,toString                 ,fromString+                ,toByteString+                ,fromByteString                 ,null+                ,nil                 ) where  import Prelude hiding (null)
+ Data/UUID/Builder.hs view
@@ -0,0 +1,88 @@+{-# 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
Data/UUID/Internal.hs view
@@ -1,8 +1,8 @@-{-# LANGUAGE DeriveDataTypeable, CPP #-}+{-# LANGUAGE DeriveDataTypeable, TypeFamilies, CPP #-}  -- | -- Module      : Data.UUID--- Copyright   : (c) 2008 Antoine Latter+-- Copyright   : (c) 2008-2009 Antoine Latter -- -- License     : BSD-style --@@ -13,27 +13,23 @@ module Data.UUID.Internal     (UUID(..)     ,null-    ,Node(..)-    ,nullNode-    ,nodeToList-    ,listToNode+    ,nil+    ,fromByteString+    ,toByteString     ,fromString     ,toString-    ,versionMask-    ,reservedMask-    ,reserved+    ,buildFromBytes+    ,buildFromWords     ) where  import Prelude hiding (null) import qualified Prelude -import Data.Word+import Control.Monad (liftM4) import Data.Char import Data.Maybe import Data.Bits-import Data.List (splitAt, foldl', unfoldr)--import Data.Typeable+import Data.List (elemIndices)  #if MIN_VERSION_base(4,0,0) import Data.Data@@ -41,196 +37,129 @@ import Data.Generics.Basics #endif -import Foreign.Ptr import Foreign.Storable  import Data.Binary import Data.Binary.Put import Data.Binary.Get+import qualified Data.ByteString.Lazy as Lazy +import Data.UUID.Builder+ import System.Random -import Text.Printf -#ifndef STRICT-#define SLOT(x) x-#else-#define SLOT(x) {-# UNPACK #-} !x-#endif- -- |The UUID type.  A 'Random' instance is provided which produces--- version 4 UUIDs as specified in RFC 4122.  The 'Storable' and --- 'Binary' instances are compatable with RFC 4122.  The 'Binary'--- instance serializes to network byte order.-data UUID = UUID-    {uuid_timeLow  :: SLOT(Word32)-    ,uuid_timeMid  :: SLOT(Word16)-    ,uuid_timeHigh :: SLOT(Word16) -- includes version number-    ,uuid_clockSeqHi :: SLOT(Word8) -- includes reserved field-    ,uuid_clokcSeqLow :: SLOT(Word8)-    ,uuid_node :: SLOT(Node)-    } deriving (Eq, Ord, Typeable)+-- version 4 UUIDs as specified in RFC 4122.  The 'Storable' and+-- 'Binary' instances are compatible with RFC 4122, storing the fields+-- in network order as 16 bytes.+data UUID = UUID !Word32 !Word32 !Word32 !Word32+    deriving (Eq, Ord, Typeable)+{-+    Other representations that we tried are:+         Mimic V1 structure:     !Word32 !Word16 !Word16 !Word16+                                   !Word8 !Word8 !Word8 !Word8 !Word8 !Word8+         Sixteen bytes:          !Word8 ... (x 16)+         Simple list of bytes:   [Word8]+         ByteString (strict)     ByteString+         Immutable array:        UArray Int Word8+         Vector:                 UArr Word8+    None was as fast, overall, as the representation used here.+-} --- |Returns true if the passed-in UUID is the null UUID.-null :: UUID -> Bool-null (UUID 0 0 0 0 0 node) | nullNode node = True-null _ = False -instance Random UUID where-    random g = let (timeLow, g1)  = randomBoundedIntegral g-                   (timeMid, g2)  = randomBoundedIntegral g1-                   (timeHigh, g3) = randomBoundedIntegral g2-                   (seqHigh, g4)  = randomBoundedIntegral g3-                   (seqLow, g5)   = randomBoundedIntegral g4-                   (node, g6)     = random g5-                   seqHighReserved = (seqHigh .&. reservedMask) .|. reserved-                   timeHighVersion = (timeHigh .&. versionMask) .|. versionRandom-               in (UUID timeLow timeMid timeHighVersion seqHighReserved seqLow node, g6)+--+-- UTILITIES+-- -    randomR _ = random -- range is ignored+-- |Build a Word32 from four Word8 values, presented in big-endian order+word :: Word8 -> Word8 -> Word8 -> Word8 -> Word32+word a b c d =  (fromIntegral a `shiftL` 24)+            .|. (fromIntegral b `shiftL` 16)+            .|. (fromIntegral c `shiftL`  8)+            .|. (fromIntegral d            ) -versionMask :: Word16 -- 0000 1111 1111 1111-versionMask = 0x0FFF+-- |Extract a Word8 from a Word32. Bytes, high to low, are numbered from 3 to 0,+byte :: Int -> Word32 -> Word8+byte i w = fromIntegral (w `shiftR` (i * 8)) -versionRandom :: Word16-versionRandom = 4 `shiftL` 12 -reservedMask :: Word8 -- 0011 1111-reservedMask = 0x3F--reserved :: Word8-reserved = bit 7--data Node = Node-    SLOT(Word8)-    SLOT(Word8)-    SLOT(Word8)-    SLOT(Word8)-    SLOT(Word8)-    SLOT(Word8)- deriving (Eq, Ord, Typeable)--instance Random Node where-    random g = let (w1, g1) = randomBoundedIntegral g-                   (w2, g2) = randomBoundedIntegral g1-                   (w3, g3) = randomBoundedIntegral g2-                   (w4, g4) = randomBoundedIntegral g3-                   (w5, g5) = randomBoundedIntegral g4-                   (w6, g6) = randomBoundedIntegral g5-               in (Node w1 w2 w3 w4 w5 w6, g6)-    randomR _ = random -- neglect range--nullNode :: Node -> Bool-nullNode (Node 0 0 0 0 0 0) = True-nullNode _ = False--nodeToList :: Node -> [Word8]-nodeToList (Node w1 w2 w3 w4 w5 w6) = [w1, w2, w3, w4, w5, w6]+-- |Make a UUID from sixteen Word8 values+makeFromBytes :: Word8 -> Word8 -> Word8 -> Word8+              -> Word8 -> Word8 -> Word8 -> Word8+              -> Word8 -> Word8 -> Word8 -> Word8+              -> Word8 -> Word8 -> Word8 -> Word8+              -> UUID+makeFromBytes b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf+        = UUID w0 w1 w2 w3+    where w0 = word b0 b1 b2 b3+          w1 = word b4 b5 b6 b7+          w2 = word b8 b9 ba bb+          w3 = word bc bd be bf -listToNode :: [Word8] -> Maybe Node-listToNode [w1, w2, w3, w4, w5, w6] = return $ Node w1 w2 w3 w4 w5 w6-listToNode _ = Nothing+-- |Make a UUID from four Word32 values+makeFromWords :: Word32 -> Word32 -> Word32 -> Word32 -> UUID+makeFromWords = UUID -instance Show UUID where-    show = toString+-- |A Builder for constructing a UUID of a given version.+buildFromBytes :: Word8+               -> Word8 -> Word8 -> Word8 -> Word8+               -> Word8 -> Word8 -> Word8 -> Word8+               -> Word8 -> Word8 -> Word8 -> Word8+               -> Word8 -> Word8 -> Word8 -> Word8+               -> UUID+buildFromBytes v b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf =+    makeFromBytes b0 b1 b2 b3 b4 b5 b6' b7 b8' b9 ba bb bc bd be bf+    where b6' = b6 .&. 0x0f .|. (v `shiftL` 4)+          b8' = b8 .&. 0x3f .|. 0x80 -instance Read UUID where-    readsPrec _ str = case fromString (take 36 str) of-      Nothing -> []-      Just u  -> [(u,drop 36 str)]+-- |Build a UUID of a given version from Word32 values.+buildFromWords :: Word8 -> Word32 -> Word32 -> Word32 -> Word32 -> UUID+buildFromWords v w0 w1 w2 w3 = makeFromWords w0 w1' w2' w3+    where w1' = w1 .&. 0xffff0fff .|. ((fromIntegral v) `shiftL` 12)+          w2' = w2 .&. 0x3fffffff .|. 0x80000000  -instance Storable UUID where-    sizeOf _ = 16-    alignment _ = 4 -- not sure what to put here--    peek p = do-      tl   <- peek $ castPtr p-      tm   <- peekByteOff p 4-      th   <- peekByteOff p 6-      ch   <- peekByteOff p 8-      cl   <- peekByteOff p 9-      node <- peekByteOff p 10-      return $ UUID tl tm th ch cl node--    poke p (UUID tl tm th ch cl node) = do-      poke (castPtr p) tl-      pokeByteOff p 4 tm-      pokeByteOff p 6 th-      pokeByteOff p 8 ch-      pokeByteOff p 9 cl-      pokeByteOff p 10 node--instance Storable Node where-    sizeOf _ = 6-    alignment _ = 1 -- ???--    peek p = do-      w1 <- peek $ castPtr p-      w2 <- peekByteOff p 1-      w3 <- peekByteOff p 2-      w4 <- peekByteOff p 3-      w5 <- peekByteOff p 4-      w6 <- peekByteOff p 5-      return $ Node w1 w2 w3 w4 w5 w6--    poke p (Node w1 w2 w3 w4 w5 w6) = do-      poke (castPtr p) w1-      pokeByteOff p 1 w2-      pokeByteOff p 2 w3-      pokeByteOff p 3 w4-      pokeByteOff p 4 w5-      pokeByteOff p 5 w6---- Binary instance in network byte-order-instance Binary UUID where-    put (UUID tl tm th ch cl n) = do-                       putWord32be tl-                       putWord16be tm-                       putWord16be th-                       putWord8 ch-                       putWord8 cl-                       put n--    get = do-      tl <- getWord32be-      tm <- getWord16be-      th <- getWord16be-      ch <- getWord8-      cl <- getWord8-      node <- get-      return $ UUID tl tm th ch cl node--instance Binary Node where-    put (Node w1 w2 w3 w4 w5 w6) = do-                       putWord8 w1-                       putWord8 w2-                       putWord8 w3-                       putWord8 w4-                       putWord8 w5-                       putWord8 w6+-- |Return the bytes that make up the UUID+toList :: UUID -> [Word8]+toList (UUID w0 w1 w2 w3) =+    [byte 3 w0, byte 2 w0, byte 1 w0, byte 0 w0,+     byte 3 w1, byte 2 w1, byte 1 w1, byte 0 w1,+     byte 3 w2, byte 2 w2, byte 1 w2, byte 0 w2,+     byte 3 w3, byte 2 w3, byte 1 w3, byte 0 w3] -    get = do-      w1 <- getWord8-      w2 <- getWord8-      w3 <- getWord8-      w4 <- getWord8-      w5 <- getWord8-      w6 <- getWord8-      return $ Node w1 w2 w3 w4 w5 w6+-- |Construct a UUID from a list of Word8. Returns Nothing if the list isn't+-- exactly sixteen bytes long+fromList :: [Word8] -> Maybe UUID+fromList [b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, ba, bb, bc, bd, be, bf] =+    Just $ makeFromBytes b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf+fromList _ = Nothing  --- My goal with this instance was to make it work just enough to do what--- I want when used with the HStringTemplate library.-instance Data UUID where-    toConstr uu  = mkConstr uuidType (show uu) [] (error "fixity")-    gunfold _ _  = error "gunfold"-    dataTypeOf _ = uuidType+--+-- UUID API+-- -uuidType =  mkNorepType "Data.UUID.UUID"+-- |Returns true if the passed-in UUID is the 'nil' UUID.+null :: UUID -> Bool+null = (== nil)+    -- Note: This actually faster than:+    --      null (UUID 0 0 0 0) = True+    --      null _              = False +-- |The nil UUID, as defined in RFC 4122.+-- It is a UUID of all zeros. @'null' u@ iff @'u' == 'nil'@.+nil :: UUID+nil = UUID 0 0 0 0 +-- |Extract a UUID from a 'ByteString' in network byte order.+-- The argument must be 16 bytes long, otherwise 'Nothing' is returned.+fromByteString :: Lazy.ByteString -> Maybe UUID+fromByteString = fromList . Lazy.unpack +-- |Encode a UUID into a 'ByteString' in network order.+toByteString :: UUID -> Lazy.ByteString+toByteString = Lazy.pack . toList  -- |If the passed in 'String' can be parsed as a 'UUID', it will be. -- The hyphens may not be omitted.@@ -242,25 +171,30 @@ -- -- Hex digits may be upper or lower-case. fromString :: String -> Maybe UUID-fromString xs | validFmt  = Just uuid+fromString xs | validFmt  = fromString' xs               | otherwise = Nothing-  where validFmt = length ws == 5 &&-                   map length ws == [8,4,4,4,12] &&-                   all isHexDigit (concat ws) &&-                   isJust node-        ws = splitList '-' xs-        [tl, tm, th, c, n] = ws-        ns = unfoldUntil Prelude.null (splitAt 2) n :: [String]-        node = listToNode $ map hexVal ns :: Maybe Node-        uuid = UUID (hexVal tl) (hexVal tm) (hexVal th) (hexVal $ take 2 c) (hexVal $ drop 2 c) (fromJust $ node)---- | Convert a string to a hex value, assuming the string is already validated.-hexVal :: Num a => String -> a-hexVal = fromInteger . foldl' (\n c -> 16*n + digitToInteger c) 0+  where validFmt = elemIndices '-' xs == [8,13,18,23] -digitToInteger :: Char -> Integer-digitToInteger = fromIntegral . digitToInt+fromString' :: String -> Maybe UUID+fromString' s0 = do+    (w0, s1) <- hexWord s0+    (w1, s2) <- hexWord s1+    (w2, s3) <- hexWord s2+    (w3, s4) <- hexWord s3+    if s4 /= "" then Nothing+                else Just $ UUID w0 w1 w2 w3+    where hexWord :: String -> Maybe (Word32, String)+          hexWord s = Just (0, s) >>= hexByte >>= hexByte+                                  >>= hexByte >>= hexByte +          hexByte :: (Word32, String) -> Maybe (Word32, String)+          hexByte (w, '-':ds) = hexByte (w, ds)+          hexByte (w, hi:lo:ds)+              | bothHex   = Just ((w `shiftL` 8) .|. octet, ds)+              | otherwise = Nothing+              where bothHex = isHexDigit hi && isHexDigit lo+                    octet = fromIntegral (16 * digitToInt hi + digitToInt lo)+          hexByte _ = Nothing  -- | Convert a UUID into a hypenated string using lower-case letters. -- Example:@@ -269,35 +203,94 @@ --  toString $ fromString \"550e8400-e29b-41d4-a716-446655440000\" -- @ toString :: UUID -> String-toString (UUID tl tm th ch cl n) = printf "%08x-%04x-%04x-%02x%02x-%s" tl tm th ch cl ns-    where ns = concatMap hexb $ nodeToList n-          hexb x = printf "%02x" x :: String+toString (UUID w0 w1 w2 w3) = hexw w0 $ hexw' w1 $ hexw' w2 $ hexw w3 ""+    where hexw :: Word32 -> String -> String+          hexw  w s = hexn w 28 : hexn w 24 : hexn w 20 : hexn w 16+                    : hexn w 12 : hexn w  8 : hexn w  4 : hexn w  0 : s +          hexw' :: Word32 -> String -> String+          hexw' w s = '-' : hexn w 28 : hexn w 24 : hexn w 20 : hexn w 16+                    : '-' : hexn w 12 : hexn w  8 : hexn w  4 : hexn w  0 : s --- remove all occurances of the input element in the inpt list.--- none of the sub-lists are empty.-splitList :: Eq a => a -> [a] -> [[a]]-splitList c xs = let ys = dropWhile (== c) xs-                 in case span (/= c) ys of-                      ([],_) -> []-                      (sub,rest) -> sub : splitList c rest+          hexn :: Word32 -> Int -> Char+          hexn w r = intToDigit $ fromIntegral ((w `shiftR` r) .&. 0xf) --- the passed-in predicate signals when to stop unfolding-unfoldUntil :: (b -> Bool) -> (b -> (a, b)) -> b -> [a]-unfoldUntil p f n = unfoldr g n- where g m | p m       = Nothing-           | otherwise = Just $ f m +--+-- Class Instances+-- --- no random intance for Data.Word types :-(--- this will work, though+instance Random UUID where+    random g = (fromGenNext w0 w1 w2 w3 w4, g4)+        where (w0, g0) = next g+              (w1, g1) = next g0+              (w2, g2) = next g1+              (w3, g3) = next g2+              (w4, g4) = next g3+    randomR _ = random -- range is ignored -randomBoundedIntegral :: (RandomGen g, Bounded a, Integral a) => g -> (a, g)-randomBoundedIntegral g =-    let (n, g1) = randomR (fromIntegral l, fromIntegral u) g-        _ = n :: Integer-        retVal = fromIntegral n `asTypeOf` (l `asTypeOf` u)-        u = maxBound-        l = minBound-    in (retVal, g1)+-- |Build a UUID from the results of five calls to next on a StdGen.+-- While next on StdGet returns an Int, it doesn't provide 32 bits of+-- randomness. This code relies on at last 28 bits of randomness in the+-- and optimizes its use so as to make only five random values, not six.+fromGenNext :: Int -> Int -> Int -> Int -> Int -> UUID+fromGenNext w0 w1 w2 w3 w4 =+    buildFromBytes 4 /-/ (ThreeByte w0)+                     /-/ (ThreeByte w1)+                     /-/ w2    -- use all 4 bytes because we know the version+                               -- field will "cover" the upper, non-random bits+                     /-/ (ThreeByte w3)+                     /-/ (ThreeByte w4)++-- |A ByteSource to extract only three bytes from an Int, since next on StdGet+-- only returns 31 bits of randomness.+type instance ByteSink ThreeByte g = Takes3Bytes g+newtype ThreeByte = ThreeByte Int+instance ByteSource ThreeByte where+    f /-/ (ThreeByte w) = f b1 b2 b3+        where b1 = fromIntegral (w `shiftR` 16)+              b2 = fromIntegral (w `shiftR` 8)+              b3 = fromIntegral w+++instance Show UUID where+    show = toString++instance Read UUID where+    readsPrec _ str = case fromString (take 36 str) of+      Nothing -> []+      Just u  -> [(u,drop 36 str)]+++instance Storable UUID where+    sizeOf _ = 16+    alignment _ = 1 -- UUIDs are stored as individual octets++    peekByteOff p off =+      mapM (peekByteOff p) [off..(off+15)] >>= return . fromJust . fromList ++    pokeByteOff p off u =+      sequence_ $ zipWith (pokeByteOff p) [off..] (toList u)+++instance Binary UUID where+    put (UUID w0 w1 w2 w3) =+        putWord32be w0 >> putWord32be w1 >> putWord32be w2 >> putWord32be w3 +    get = liftM4 UUID getWord32be getWord32be getWord32be getWord32be+++-- My goal with this instance was to make it work just enough to do what+-- I want when used with the HStringTemplate library.+instance Data UUID where+    toConstr uu  = mkConstr uuidType (show uu) [] (error "fixity")+    gunfold _ _  = error "gunfold"+    dataTypeOf _ = uuidType++uuidType :: DataType+uuidType =  mkNoRepType "Data.UUID.UUID"++#if !(MIN_VERSION_base(4,2,0))+mkNoRepType :: String -> DataType+mkNoRepType = mkNorepType+#endif 
+ Data/UUID/Named.hs view
@@ -0,0 +1,67 @@+-- |+-- Module      : Data.UUID.Named+-- Copyright   : (c) 2008 Antoine Latter+--+-- License     : BSD-style+--+-- Maintainer  : aslatter@gmail.com+-- Stability   : experimental+-- Portability : portable+--+--+-- This module implements Version 3/5 UUIDs as specified+-- in RFC 4122.+--+-- These UUIDs identify an object within a namespace,+-- and are deterministic.+--+-- The namespace is identified by a UUID.  Several sample+-- namespaces are enclosed.++module Data.UUID.Named+    (generateNamed+    ,namespaceDNS+    ,namespaceURL+    ,namespaceOID+    ,namespaceX500+    ) where++import Data.UUID.Internal++import Data.Binary+import Data.Maybe++import qualified Data.ByteString.Lazy as BS++-- |Generate a 'UUID' within the specified namespace out of the given+-- object.+generateNamed :: ([Word8] -> (Word32, Word32, Word32, Word32)) -- ^Hash+              -> Word8   -- ^Version+              ->  UUID   -- ^Namespace+              -> [Word8] -- ^Object+              -> UUID+generateNamed hash version namespace object =+    let chunk = BS.unpack (toByteString namespace) ++ object+        (w1, w2, w3, w4) = hash chunk+    in buildFromWords version w1 w2 w3 w4++++unsafeFromString :: String -> UUID+unsafeFromString = fromJust . fromString++-- |The namespace for DNS addresses+namespaceDNS :: UUID+namespaceDNS = unsafeFromString "6ba7b810-9dad-11d1-80b4-00c04fd430c8"++-- |The namespace for URLs+namespaceURL :: UUID+namespaceURL = unsafeFromString "6ba7b811-9dad-11d1-80b4-00c04fd430c8"++-- |The namespace for ISO OIDs+namespaceOID :: UUID+namespaceOID = unsafeFromString "6ba7b812-9dad-11d1-80b4-00c04fd430c8"++-- |The namespace for X.500 DNs+namespaceX500 :: UUID+namespaceX500 = unsafeFromString "6ba7b814-9dad-11d1-80b4-00c04fd430c8"
Data/UUID/V1.hs view
@@ -1,4 +1,5 @@ {-# OPTIONS_GHC -fno-cse #-}+{-# LANGUAGE TypeFamilies #-}  -- | -- Module      : Data.UUID.V1@@ -19,15 +20,15 @@  import Data.Bits import Data.Word-import Data.Binary  import Data.IORef import System.IO import System.IO.Unsafe -import System.Info.MAC+import qualified System.Info.MAC as SysMAC import Data.MAC +import Data.UUID.Builder import Data.UUID.Internal  -- | Returns a new UUID derived from the local hardware MAC@@ -40,48 +41,53 @@ nextUUID :: IO (Maybe UUID) nextUUID = do   res <- stepTime-  mac <- mac+  mac <- SysMAC.mac   case (res, mac) of-    (Just (c, t), Just (MAC a' b' c' d' e' f')) -> do-      let-        (tL, tM, tH) = word64ToTimePieces t-        (cL, cH) = word16ToClockSeqPieces c-      return $ Just $ UUID tL tM tH cH cL $ Node a' b' c' d' e' f'+    (Just (c, t), Just m) -> return $ Just $ makeUUID t c m     _ -> return Nothing  --- |The bit layout and version number here used are described in clause 13 of---  ITU X.667, from September 2004.-word64ToTimePieces :: Word64 -> (Word32, Word16, Word16) -word64ToTimePieces w = (lo, mi, hi) - where-  lo = fromIntegral $ (w `shiftL` 32) `shiftR` 32-  mi = fromIntegral $ (w `shiftL` 16) `shiftR` 48-  hi = (fromIntegral $ w `shiftR` 48) `setBit` 15 +makeUUID :: Word64 -> Word16 -> MAC -> UUID+makeUUID time clock mac =+    buildFromBytes 1 /-/ tLow /-/ tMid /-/ tHigh /-/ clock /-/ (MACSource mac)+    where tLow = (fromIntegral time) :: Word32+          tMid = (fromIntegral (time `shiftR` 32)) :: Word16+          tHigh = (fromIntegral (time `shiftR` 48)) :: Word16   +newtype MACSource = MACSource MAC+instance ByteSource MACSource where+    z /-/ (MACSource (MAC a b c d e f)) = z a b c d e f+type instance ByteSink MACSource g = Takes3Bytes (Takes3Bytes g) ++-- |Approximates the clock algorithm in RFC 4122, section 4.2+-- Isn't system wide or thread safe, nor does it properly randomize+-- the clock value on initialization.+stepTime :: IO (Maybe (Word16, Word64)) stepTime = do   State c0 h0 <- readIORef state   h1 <- fmap hundredsOfNanosSinceGregorianReform getCurrentTime-  if h1 > h0 +  if h1 > h0     then  do-      writeIORef state $ State 0 h1-      return $ Just (0, h1)+      writeIORef state $ State c0 h1+      return $ Just (c0, h1)     else  do       let         c1 = succ c0-      if c1 < 2^14+      if c1 <= 0x3fff -- when clock is initially randomized,+                      -- then this test will need to change         then  do-          writeIORef state $ State c1 h0-          return $ Just (c1, h0)+          writeIORef state $ State c1 h1+          return $ Just (c1, h1)         else  do           return Nothing   {-# NOINLINE state #-}+state :: IORef State state = unsafePerformIO $ do   h0 <- fmap hundredsOfNanosSinceGregorianReform getCurrentTime-  newIORef $ State 0 h0 +  newIORef $ State 0 h0 -- the 0 should be a random number   data State = State@@ -97,12 +103,5 @@   gregorianReform = UTCTime (fromGregorian 1582 10 15) 0   dt = t `diffUTCTime` gregorianReform ---- |Per clause 13 of ITU X.667, from September 2004.-word16ToClockSeqPieces :: Word16 -> (Word8, Word8)-word16ToClockSeqPieces w = (lo, hi)- where-  lo = fromIntegral $ (w `shiftL` 8) `shiftR` 8-  hi = (fromIntegral $ w `shiftR` 8) `setBit` 7   
+ Data/UUID/V3.hs view
@@ -0,0 +1,67 @@+-- |+-- Module      : Data.UUID.V3+-- Copyright   : (c) 2010 Antoine Latter+--+-- License     : BSD-style+--+-- Maintainer  : aslatter@gmail.com+-- Stability   : experimental+-- Portability : portable+--+--+-- This module implements Version 3 UUIDs as specified+-- in RFC 4122.+--+-- These UUIDs identify an object within a namespace,+-- and are deterministic.+--+-- The namespace is identified by a UUID.  Several sample+-- namespaces are enclosed.++module Data.UUID.V3+    (generateNamed+    ,Shared.namespaceDNS+    ,Shared.namespaceURL+    ,Shared.namespaceOID+    ,Shared.namespaceX500+    ) where++import Data.Word+import Data.Bits++import Data.UUID.Internal+import qualified Data.UUID.Named as Shared+import qualified Data.Digest.MD5 as MD5+++-- |Generate a 'UUID' within the specified namespace out of the given+-- object.+--+-- Uses an MD5 hash. The UUID is built from first 128 bits of the hash of+-- the namespace UUID and the name (as a series of Word8).+generateNamed :: UUID    -- ^Namespace+              -> [Word8] -- ^Object+              -> UUID+generateNamed = Shared.generateNamed hash 3++hash :: [Word8] -> (Word32, Word32, Word32, Word32)+hash bytes+    = case map f $ chunk 4 $ MD5.hash bytes of+        w1:w2:w3:w4:_ -> (w1, w2, w3, w4)+        _ -> error "Data.UUID.V3.hash: fatal error"++ where f [b1, b2, b3, b4]+           = sum+             [ fromIntegral b4+             , fromIntegral b3 `shiftL` 8+             , fromIntegral b2 `shiftL` 16+             , fromIntegral b1 `shiftL` 24+             ]+       f _ = error "Data.UUID.V3.hash: fatal error"++chunk :: Int -> [a] -> [[a]]+chunk n = go n []+ where go _ [] [] = []+       go _ ys [] = reverse ys:[]+       go 0 ys xs = reverse ys:go n [] xs+       go m ys (x:xs) = go (m-1) (x:ys) xs
Data/UUID/V5.hs view
@@ -7,8 +7,8 @@ -- Maintainer  : aslatter@gmail.com -- Stability   : experimental -- Portability : portable--- --- +--+-- -- This module implements Version 5 UUIDs as specified -- in RFC 4122. --@@ -20,82 +20,28 @@  module Data.UUID.V5     (generateNamed-    ,namespaceDNS-    ,namespaceURL-    ,namespaceOID-    ,namespaceX500+    ,Shared.namespaceDNS+    ,Shared.namespaceURL+    ,Shared.namespaceOID+    ,Shared.namespaceX500     ) where-                     -import Data.UUID.Internal--import Data.Binary-import Data.Bits-import Data.Maybe--import qualified Data.ByteString.Lazy as BS-import Data.ByteString.Lazy (ByteString)+import Data.Word +import Data.UUID.Internal+import qualified Data.UUID.Named as Shared import qualified Data.Digest.SHA1 as SHA1   -- |Generate a 'UUID' within the specified namespace out of the given -- object. ----- Uses a SHA1 hash.+-- Uses a SHA1 hash. The UUID is built from first 128 bits of the hash of+-- the namespace UUID and the name (as a series of Word8). generateNamed :: UUID    -- ^Namespace               -> [Word8] -- ^Object               -> UUID-generateNamed namespace object =-    let chunk = BS.unpack (encode namespace) ++ object-        SHA1.Word160 w1 w2 w3 w4 w5 = SHA1.hash chunk--        tl = w1-        tm = high16 w2-        th = (versionSHA .|.) $ (versionMask .&.) $ low16 w2-        ch = (reserved .|.) $ (reservedMask .&.) $ high8 $ high16 w3-        cl = low8  $ high16 w3-        node = Node (high8 (low16 w3))-                    (low8  (low16 w3))-                    (high8 (high16 w4))-                    (low8 (high16 w4))-                    (high8 (low16 w4))-                    (low8 (low16 w4))-    in UUID tl tm th ch cl node---- HASH 0  1  - 2  3  : w1---      4  5  - 6  7  : w2---      8  9  - 10 11 : w3---      12 13 - 14 15 : w4---      16 17 - 18 19 : w5--low16 :: Word32 -> Word16-low16 = fromIntegral . (.&. 0x0000FFFF)--high16 :: Word32 -> Word16-high16 = fromIntegral . flip shiftR 16--low8 :: Word16 -> Word8-low8 = fromIntegral . (.&. 0x00FF)--high8 :: Word16 -> Word8-high8 = fromIntegral . flip shiftR 8--versionSHA = 5 `shiftL` 12--unsafeFromString :: String -> UUID-unsafeFromString = fromJust . fromString---- |The namespace for DNS addresses-namespaceDNS :: UUID-namespaceDNS = unsafeFromString "6ba7b810-9dad-11d1-80b4-00c04fd430c8"---- |The namespace for URLs-namespaceURL :: UUID-namespaceURL = unsafeFromString "6ba7b811-9dad-11d1-80b4-00c04fd430c8"--namespaceOID :: UUID-namespaceOID = unsafeFromString "6ba7b812-9dad-11d1-80b4-00c04fd430c8"--namespaceX500 :: UUID-namespaceX500 = unsafeFromString "6ba7b814-9dad-11d1-80b4-00c04fd430c8"+generateNamed = Shared.generateNamed hash 5+ where hash bytes+           = case SHA1.hash bytes of+               SHA1.Word160 w1 w2 w3 w4 _w5 -> (w1, w2, w3, w4)
+ tests/BenchUUID.hs view
@@ -0,0 +1,126 @@+import Control.Parallel.Strategies+import Criterion.Main+import Data.Char (ord)+import Data.Maybe (fromJust)+import Data.Word+import qualified Data.Set as Set+import qualified Data.ByteString.Lazy as BL+import qualified Data.ByteString.Lazy.Internal as BL+import qualified Data.UUID.Internal as U+import qualified Data.UUID.V1 as U+import qualified Data.UUID.V3 as U3+import qualified Data.UUID.V5 as U5+import System.Random+++instance NFData BL.ByteString where+    rnf BL.Empty        = ()+    rnf (BL.Chunk _ ts) = rnf ts++instance NFData U.UUID where+    rnf u = ()+++++main :: IO ()+main = do+        u1 <- randomIO+        let s1 = U.toString u1+            b1 = U.toByteString u1+            n1 = (map (fromIntegral . ord) "http://www.haskell.org/") :: [Word8]+            nil2 = fromJust $+                        U.fromString "00000000-0000-0000-0000-000000000000"+            u2a = fromJust $ U.fromString "169a5a43-c051-4a16-98f4-08447ddd5dc0"+            u2b = fromJust $ U.fromByteString $+                        BL.pack [0x16, 0x9a, 0x5a, 0x43, 0xc0, 0x51, 0x4a, 0x16,+                                 0x98, 0xf4, 0x08, 0x44, 0x7d, 0xdd, 0x5d, 0xc0]+            u3  = fromJust $ U.fromString "dea6f619-1038-438b-b4af-f1cdec1e6e23"+        defaultMain [+            bgroup "testing" [+                bench "null non-nil"   $ whnf U.null u1,+                bench "null nil"       $ whnf U.null U.nil,+                bench "null nil2"      $ whnf U.null nil2,+                bench "eq same"        $ whnf (==u2a) u2b,+                bench "eq differ"      $ whnf (==u2a) u3+                ],+            bgroup "conversion" [+                bench "toString"       $ nf U.toString u1,+                bench "fromString"     $ nf U.fromString s1,+                bench "toByteString"   $ nf U.toByteString u1,+                bench "fromByteString" $ nf U.fromByteString b1+                ],+            bgroup "generation" [+                bench "V1" $ nfIO U.nextUUID,+                bench "V4" $ nfIO (randomIO :: IO U.UUID),+                bench "V3" $ nf   (U3.generateNamed U3.namespaceURL) n1,+                bench "V5" $ nf   (U5.generateNamed U5.namespaceURL) n1+                ],+            bench "set making" $ nf Set.fromList uuids+            ]++-- 50 uuids, so tests can be repeatable+uuids :: [U.UUID]+uuids +    = map (fromJust . U.fromString)+        [+         "35d42593-1fca-4465-b588-a2e78cb996ba",+         "1e97e407-eca7-4c5d-a947-6fbe9dc168b6",+         "a41fd7ce-a053-4c0a-a742-77c95b85da2a",+         "f7e3913a-0fd7-4355-a92f-d73f9b046efa",+         "8961a35d-55c2-42f3-8680-08fce3986647",+         "96246c58-d0b4-4e56-a543-356bd59686a9",+         "72c46194-648c-4b1e-a9fb-2eba060ab43b",+         "0fc252d4-a37b-4eca-9309-e3d2a59a3a22",+         "a8aceb5a-6a8e-43f3-85bb-9653a3c1ebcd",+         "b23d1118-6bc8-4add-9d56-99634d78949f",+         "5f8c7896-9c4f-4d7e-a4d2-961bda298012",+         "219a4137-7bc5-42b1-ac95-490948a978e1",+         "5af5024f-fbe9-45ab-991d-49b655994437",+         "569dfb33-185d-4a3c-99c4-bc2b83250a7c",+         "43a58442-aa51-4a5d-8e00-b8a83b5fc5b0",+         "2865ced1-b54d-4725-8f01-b408f4617424",+         "b8cfaff0-4dee-4f32-af2f-0469b3e535fc",+         "63f45bc0-f303-4f1a-b0d6-76f876be626d",+         "d171eaf3-f20d-45d6-9268-0cc22dfbe887",+         "7c28f457-ad27-494a-8642-6e47e7f3efb8",+         "f8de9193-66ff-49e8-9826-fc50858d7855",+         "2af85f28-cace-4740-b8bb-2b5860f5fdb3",+         "b12a7a22-edb3-4694-b8f4-0532eaad6112",+         "5e052a08-8e49-4668-bbe7-77cdbc4679a3",+         "42d5a68d-3f08-4e39-9b8c-71cc17c538ed",+         "ba2b1487-b3a4-4a19-98cc-59530f36613f",+         "e4a5c569-b8ac-4851-8ad2-fbdb89986be2",+         "35b4a1b6-b5ca-4646-803b-c337ee730d9a",+         "f0df1206-05d9-49d6-a726-d49fb253e645",+         "9656a0f0-89b2-4cec-bb1c-fdf5633e1cd7",+         "d13382f5-04a1-422d-94d6-e47219425816",+         "b8d0c762-4c6b-4bd0-ad0b-f68988a87166",+         "06360e85-f18a-44f6-aa72-45f1e60b6b77",+         "347491ca-62b3-48e8-ac94-6ffebe1318ed",+         "014339d0-7b2d-4dda-914b-14ee5cb4391b",+         "dc57931b-4744-41be-b3c4-e24dbeeb606a",+         "2c9fdcf0-e1d6-4a2d-951d-4766b99032cb",+         "b6bde422-eea8-4231-bf1c-ee0e56699511",+         "921073c5-f7c1-4583-ac03-aeb7aef8662b",+         "eff6a517-aeb1-453e-9810-1b4b324c5a1e",+         "eadaae8c-cbf8-4e0b-ab80-e34284b07dad",+         "4d307c36-70d9-453f-8455-ec7e2ba405ed",+         "53ddef9a-2413-4f7f-8363-cff56ee17c6c",+         "4dd4c27a-b300-4a00-ab87-eef505275492",+         "4a5d7001-f0c1-4e2f-8362-8833bda2114b",+         "0c46f438-9365-406a-b04e-34436806ec25",+         "b35469cf-05f8-40ff-a38b-117604347957",+         "f1c54df0-5f59-4891-b3c6-82bac0d814ca",+         "8091837c-6456-42c3-a686-f4731a41d4f9",+         "2a0e2efb-a11c-4a44-81ee-3efc37379b48"+        ]++#if !(MIN_VERSION_criterion(0,4,0))+nf f arg = B ( rnf . f) arg+whnf f arg = B f arg+#endif++#if !(MIN_VERSION_criterion(0,4,1))+nfIO act = rnf `fmap` act+#endif
+ tests/TestUUID.hs view
@@ -0,0 +1,162 @@+import Control.Monad (replicateM)+import Data.Bits+import qualified Data.ByteString.Lazy as B+import Data.Char (ord)+import Data.List (nub, (\\))+import Data.Maybe+import Data.Word+import qualified Data.UUID as U+import qualified Data.UUID.V1 as U+import qualified Data.UUID.V3 as U3+import qualified Data.UUID.V5 as U5+import Test.HUnit+import Test.QuickCheck+import System.IO+import System.Random+++isValidVersion :: Int -> U.UUID -> Bool+isValidVersion v u = lenOK && variantOK && versionOK+    where bs = U.toByteString u+          lenOK = B.length bs == 16+          variantOK = (B.index bs 8) .&. 0xc0 == 0x80+          versionOK = (B.index bs 6) .&. 0xf0 == fromIntegral (v `shiftL` 4)+++instance Arbitrary U.UUID where+    arbitrary = (fst . random) `fmap` rand+    coarbitrary = undefined++instance Arbitrary Word8 where+    arbitrary = (fromIntegral . fst . randomR (0,255::Int)) `fmap` rand+    coarbitrary = undefined+++test_null :: Test+test_null = TestList [+    "nil is null"              ~: assertBool "" (U.null U.nil),+    "namespaceDNS is not null" ~: assertBool "" (not $ U.null U3.namespaceDNS)+    ]++test_nil :: Test+test_nil = TestList [+    "nil string" ~: U.toString U.nil @?= "00000000-0000-0000-0000-000000000000",+    "nil bytes"  ~: U.toByteString U.nil @?= B.pack (replicate 16 0)+    ]++test_conv :: Test+test_conv = TestList [+    "conv bytes to string" ~:+        maybe "" (U.toString) (U.fromByteString b16) @?= s16,+    "conv string to bytes" ~:+        maybe B.empty (U.toByteString) (U.fromString s16) @?= b16+    ]+    where b16 = B.pack [1..16]+          s16 = "01020304-0506-0708-090a-0b0c0d0e0f10"++test_v1 :: [Maybe U.UUID] -> Test+test_v1 v1s = TestList [+    "V1 unique" ~: nub (v1s \\ nub v1s) @?= [],+    "V1 not null" ~: TestList $ map (testUUID (not . U.null))  v1s,+    "V1 valid"    ~: TestList $ map (testUUID (isValidVersion 1)) v1s+    ]+    where testUUID :: (U.UUID -> Bool) -> Maybe U.UUID -> Test+          testUUID p u = maybe False p u ~? show u++test_v3 :: Test+test_v3 = TestList [+    "V3 computation" ~:+          U3.generateNamed U3.namespaceDNS name @?= uV3+    ]+    where name = map (fromIntegral . ord) "www.widgets.com" :: [Word8]+          uV3 = fromJust $ U.fromString "3d813cbb-47fb-32ba-91df-831e1593ac29"++test_v5 :: Test+test_v5 = TestList [+    "V5 computation" ~:+          U5.generateNamed U5.namespaceDNS name @?= uV5+    ]+    where name = map (fromIntegral . ord) "www.widgets.com" :: [Word8]+          uV5 = fromJust $ U.fromString "21f7f8de-8051-5b89-8680-0195ef798b6a"++prop_stringRoundTrip :: Property+prop_stringRoundTrip = label "String round trip" stringRoundTrip+    where stringRoundTrip :: U.UUID -> Bool+          stringRoundTrip u = maybe False (== u) $ U.fromString (U.toString u)++prop_byteStringRoundTrip :: Property+prop_byteStringRoundTrip = label "ByteString round trip" byteStringRoundTrip+    where byteStringRoundTrip :: U.UUID -> Bool+          byteStringRoundTrip u = maybe False (== u)+                                    $ U.fromByteString (U.toByteString u)++prop_stringLength :: Property+prop_stringLength = label "String length" stringLength+    where stringLength :: U.UUID -> Bool+          stringLength u = length (U.toString u) == 36++prop_byteStringLength :: Property+prop_byteStringLength = label "ByteString length" byteStringLength+    where byteStringLength :: U.UUID -> Bool+          byteStringLength u = B.length (U.toByteString u) == 16++prop_randomsDiffer :: Property+prop_randomsDiffer = label "Randoms differ" randomsDiffer+    where randomsDiffer :: (U.UUID, U.UUID) -> Bool+          randomsDiffer (u1, u2) = u1 /= u2++prop_randomNotNull :: Property+prop_randomNotNull = label "Random not null" randomNotNull+    where randomNotNull :: U.UUID -> Bool+          randomNotNull = not. U.null++prop_randomsValid :: Property+prop_randomsValid = label "Random valid" randomsValid+    where randomsValid :: U.UUID -> Bool+          randomsValid = isValidVersion 4++prop_v3NotNull :: Property+prop_v3NotNull = label "V3 not null" v3NotNull+    where v3NotNull :: [Word8] -> Bool+          v3NotNull = not . U.null . U3.generateNamed U3.namespaceDNS++prop_v3Valid :: Property+prop_v3Valid = label "V3 valid" v3Valid+    where v3Valid :: [Word8] -> Bool+          v3Valid = isValidVersion 3 . U3.generateNamed U3.namespaceDNS++prop_v5NotNull :: Property+prop_v5NotNull = label "V5 not null" v5NotNull+    where v5NotNull :: [Word8] -> Bool+          v5NotNull = not . U.null . U5.generateNamed U5.namespaceDNS++prop_v5Valid :: Property+prop_v5Valid = label "V5 valid" v5Valid+    where v5Valid :: [Word8] -> Bool+          v5Valid = isValidVersion 5 . U5.generateNamed U5.namespaceDNS+++main :: IO ()+main = do+    v1s <- replicateM 100 U.nextUUID+    runTestText (putTextToHandle stderr False) (TestList [+        test_null,+        test_nil,+        test_conv,+        test_v1 v1s,+        test_v3,+        test_v5+        ])+    mapM_ quickCheck $ [+        prop_stringRoundTrip,+        prop_byteStringRoundTrip,+        prop_stringLength,+        prop_byteStringLength,+        prop_randomsDiffer,+        prop_randomNotNull,+        prop_randomsValid,+        prop_v3NotNull,+        prop_v3Valid,+        prop_v5NotNull,+        prop_v5Valid+        ]
uuid.cabal view
@@ -1,6 +1,6 @@ Name: uuid-Version: 1.1.1-Copyright: (c) 2008 Antoine Latter+Version: 1.2.0+Copyright: (c) 2008-2009 Antoine Latter Author: Antoine Latter Maintainer: aslatter@gmail.com License: BSD3@@ -17,8 +17,17 @@  Synopsis: For creating, comparing, parsing and printing Universally Unique Identifiers -Homepage: http://community.haskell.org/~aslatter/code/uuid/+Homepage: http://projects.haskell.org/uuid/+Bug-Reports: mailto:aslatter@gmail.com +Extra-Source-Files:+     CHANGES+     CONTRIBUTORS++Flag test+  Description: Build unit test and benchmark programs.+  Default:     False+ Library  Build-Depends: random, binary, bytestring, Crypto, maccatcher,                 time, base >=3, base < 5@@ -26,10 +35,44 @@  Exposed-Modules:    Data.UUID    Data.UUID.V1+   Data.UUID.V3    Data.UUID.V5   Other-Modules:+   Data.UUID.Builder    Data.UUID.Internal+   Data.UUID.Named   Extensions: DeriveDataTypeable+ Ghc-Prof-Options:   -auto-all -caf-all+ Ghc-Shared-Options:+ Ghc-Options:        -Wall++Executable benchuuid+ Main-Is: BenchUUID.hs+ HS-Source-Dirs: tests .+ Extensions: DeriveDataTypeable, CPP+ If flag(test)+  Buildable: True+  Build-Depends: random, binary, bytestring, Crypto, maccatcher,+                 time, base >=3, base < 5, containers,+                 criterion >= 0.3 && < 0.5, parallel >= 2.1 && < 2.3+ Else+  Buildable: False++Executable testuuid+ Main-Is: TestUUID.hs+ HS-Source-Dirs: tests .+ Extensions: DeriveDataTypeable+ Ghc-Prof-Options:   -auto-all -caf-all+ Ghc-Shared-Options:+ Ghc-Options:        -Wall -fno-warn-orphans++ If flag(test)+  Buildable: True+  Build-Depends: random, binary, bytestring, Crypto, maccatcher,+                 time, base >=3, base < 5,+                 HUnit >=1.2 && < 1.3, QuickCheck >=1.2 && < 1.3+ Else+  Buildable: False