{-# OPTIONS_GHC -fno-cse #-} -- | -- Module : Data.UUID.V1 -- Copyright : (c) 2008 Jason Dusek -- -- License : BSD-style -- -- Maintainer : aslatter@gmail.com -- Stability : experimental -- Portability : portable -- -- RFC 4122 Version 1 UUID state machine. module Data.UUID.V1(nextUUID) where import Data.Time import Data.Bits import Data.Word import Data.Binary import Data.IORef import System.IO import System.IO.Unsafe import System.Info.MAC import Data.MAC import Data.UUID.Internal -- | Returns a new UUID derived from the local hardware MAC -- address and the current system time. -- Is generated according to the Version 1 UUID sepcified in -- RFC 4122. -- -- Returns nothing if the hardware MAC address could not -- be discovered. nextUUID :: IO (Maybe UUID) nextUUID = do res <- stepTime mac <- 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' _ -> 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 stepTime = do State c0 h0 <- readIORef state h1 <- fmap hundredsOfNanosSinceGregorianReform getCurrentTime if h1 > h0 then do writeIORef state $ State 0 h1 return $ Just (0, h1) else do let c1 = succ c0 if c1 < 2^14 then do writeIORef state $ State c1 h0 return $ Just (c1, h0) else do return Nothing {-# NOINLINE state #-} state = unsafePerformIO $ do h0 <- fmap hundredsOfNanosSinceGregorianReform getCurrentTime newIORef $ State 0 h0 data State = State {-# UNPACK #-} !Word16 {-# UNPACK #-} !Word64 deriving (Show) hundredsOfNanosSinceGregorianReform :: UTCTime -> Word64 hundredsOfNanosSinceGregorianReform t = floor $ 10000000 * dt where 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