@@ -1,135 +0,0 @@
1−{-# OPTIONS_GHC -fno-cse #-}
2−{-# LANGUAGE TypeFamilies #-}
3−
4−{- |
5−Module : Data.UUID.V1
6−Copyright : (c) 2008 Jason Dusek
7− (c) 2009 Mark Lentczner
8− (c) 2009-2010,2012 Antoine Latter
9−
10−License : BSD-style
11−
12−Maintainer : aslatter@gmail.com
13−Stability : experimental
14−Portability : portable
15−
16−RFC 4122 Version 1 UUID state machine.
17−
18−The generated UUID is based on the hardware MAC
19−address and the system clock.
20−
21−If we cannot lookup the MAC address we seed the
22−generator with a pseudo-random number.
23−-}
24−
25−module Data.UUID.V1(nextUUID)
26−where
27−
28−
29−import Data.Bits
30−import Data.Maybe
31−import Data.Time
32−import Data.Word
33−
34−import Control.Applicative ((<$>),(<*>))
35−import Control.Concurrent.MVar
36−import System.IO.Unsafe
37−
38−import qualified System.Random as R
39−
40−import Network.Info
41−
42−import Data.UUID.Types.Internal.Builder
43−import Data.UUID.Types.Internal
44−
45−-- | Returns a new UUID derived from the local hardware MAC
46−-- address and the current system time.
47−-- Is generated according to the Version 1 UUID specified in
48−-- RFC 4122.
49−--
50−-- Returns 'Nothing' if you request UUIDs too quickly.
51−nextUUID :: IO (Maybe UUID)
52−nextUUID = do
53− res <- stepTime
54− case res of
55− Just (mac', c, t) -> return $ Just $ makeUUID t c mac'
56− _ -> return Nothing
57−
58−
59−makeUUID :: Word64 -> Word16 -> MAC -> UUID
60−makeUUID time clock mac' =
61− buildFromBytes 1 /-/ tLow /-/ tMid /-/ tHigh /-/ clock /-/ (MACSource mac')
62− where tLow = (fromIntegral time) :: Word32
63− tMid = (fromIntegral (time `shiftR` 32)) :: Word16
64− tHigh = (fromIntegral (time `shiftR` 48)) :: Word16
65−
66−newtype MACSource = MACSource MAC
67−instance ByteSource MACSource where
68− z /-/ (MACSource (MAC a b c d e f)) = z a b c d e f
69−type instance ByteSink MACSource g = Takes3Bytes (Takes3Bytes g)
70−
71−
72−-- |Approximates the clock algorithm in RFC 4122, section 4.2
73−-- Isn't system wide or thread safe, nor does it properly randomize
74−-- the clock value on initialization.
75−stepTime :: IO (Maybe (MAC, Word16, Word64))
76−stepTime = do
77− h1 <- fmap hundredsOfNanosSinceGregorianReform getCurrentTime
78− modifyMVar state $ \s@(State mac' c0 h0) ->
79− if h1 > h0
80− then
81− return (State mac' c0 h1, Just (mac', c0, h1))
82− else
83− let
84− c1 = succ c0
85− in if c1 <= 0x3fff -- when clock is initially randomized,
86− -- then this test will need to change
87− then
88− return (State mac' c1 h1, Just (mac', c1, h1))
89− else
90− return (s, Nothing)
91−
92−
93−{-# NOINLINE state #-}
94−state :: MVar State
95−state = unsafePerformIO $ do
96− h0 <- fmap hundredsOfNanosSinceGregorianReform getCurrentTime
97− mac' <- getMac
98− newMVar $ State mac' 0 h0 -- the 0 should be a random number
99−
100−-- SysMAC.mac can fail on some machines.
101−-- In those cases we fake it with a random
102−-- 6 bytes seed.
103−getMac :: IO MAC
104−getMac =
105− getNetworkInterfaces >>=
106− return . listToMaybe . filter (minBound /=) . map mac >>=
107− \macM -> case macM of
108− Just m -> return m
109− Nothing -> randomMac
110−
111−randomMac :: IO MAC
112−randomMac =
113− -- I'm too lazy to thread through
114− -- the random state ...
115− MAC
116− <$> (R.randomIO >>= return . (1 .|.)) -- We must set the multicast bit to True. See section 4.5 of the RFC.
117− <*> R.randomIO
118− <*> R.randomIO
119− <*> R.randomIO
120− <*> R.randomIO
121− <*> R.randomIO
122−
123−data State = State
124− {-# UNPACK #-} !MAC
125− {-# UNPACK #-} !Word16
126− {-# UNPACK #-} !Word64
127− deriving (Show)
128−
129−
130−
131−hundredsOfNanosSinceGregorianReform :: UTCTime -> Word64
132−hundredsOfNanosSinceGregorianReform t = floor $ 10000000 * dt
133− where
134− gregorianReform = UTCTime (fromGregorian 1582 10 15) 0
135− dt = t `diffUTCTime` gregorianReform