{-# OPTIONS_GHC -fno-cse #-}{-# LANGUAGE TypeFamilies #-}{- |
Module : Data.UUID.V1
Copyright : (c) 2008 Jason Dusek
(c) 2009 Mark Lentczner
(c) 2009-2010,2012 Antoine Latter
License : BSD-style
Maintainer : aslatter@gmail.com
Stability : experimental
Portability : portable
RFC 4122 Version 1 UUID state machine.
The generated UUID is based on the hardware MAC
address and the system clock.
If we cannot lookup the MAC address we seed the
generator with a psuedo-random number.
-}moduleData.UUID.V1(nextUUID)whereimportData.TimeimportData.BitsimportData.WordimportControl.Applicative((<$>),(<*>))importControl.Concurrent.MVarimportSystem.IO.UnsafeimportqualifiedSystem.RandomasRimportqualifiedSystem.Info.MACasSysMACimportData.MACimportData.UUID.BuilderimportData.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 you request UUIDs too quickly.nextUUID::IO(MaybeUUID)nextUUID=dores<-stepTimecaseresofJust(mac,c,t)->return$Just$makeUUIDtcmac_->returnNothingmakeUUID::Word64->Word16->MAC->UUIDmakeUUIDtimeclockmac=buildFromBytes1/-/tLow/-/tMid/-/tHigh/-/clock/-/(MACSourcemac)wheretLow=(fromIntegraltime)::Word32tMid=(fromIntegral(time`shiftR`32))::Word16tHigh=(fromIntegral(time`shiftR`48))::Word16newtypeMACSource=MACSourceMACinstanceByteSourceMACSourcewherez/-/(MACSource(MACabcdef))=zabcdeftypeinstanceByteSinkMACSourceg=Takes3Bytes(Takes3Bytesg)-- |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(MAC,Word16,Word64))stepTime=doh1<-fmaphundredsOfNanosSinceGregorianReformgetCurrentTimemodifyMVarstate$\s@(Statemacc0h0)->ifh1>h0thenreturn(Statemacc0h1,Just(mac,c0,h1))elseletc1=succc0inifc1<=0x3fff-- when clock is initially randomized,-- then this test will need to changethenreturn(Statemacc1h1,Just(mac,c1,h1))elsereturn(s,Nothing){-# NOINLINE state #-}state::MVarStatestate=unsafePerformIO$doh0<-fmaphundredsOfNanosSinceGregorianReformgetCurrentTimemac<-getMacnewMVar$Statemac0h0-- the 0 should be a random number-- SysMAC.mac can fail on some machines.-- In those cases we fake it with a random-- 6 bytes seed.getMac::IOMACgetMac=SysMAC.mac>>=\macM->casemacMofJustm->returnmNothing->randomMacrandomMac::IOMACrandomMac=-- I'm too lazy to thread through-- the random state ...MAC<$>R.randomIO<*>R.randomIO<*>R.randomIO<*>R.randomIO<*>R.randomIO<*>R.randomIOdataState=State{-# UNPACK #-}!MAC{-# UNPACK #-}!Word16{-# UNPACK #-}!Word64deriving(Show)hundredsOfNanosSinceGregorianReform::UTCTime->Word64hundredsOfNanosSinceGregorianReformt=floor$10000000*dtwheregregorianReform=UTCTime(fromGregorian15821015)0dt=t`diffUTCTime`gregorianReform