1{-# LANGUAGE DeriveDataTypeable #-}
5-- Copyright : (c) 2008 Antoine Latter
9-- Maintainer : aslatter@gmail.com
10-- Stability : experimental
11-- Portability : portable
14-- This library is useful for creating, comparing, parsing and
15-- printing Universally Unique Identifiers.
16-- See <http://en.wikipedia.org/wiki/UUID> for the general idea.
42import Data.Generics.Basics
48import Prelude hiding (null)
49import qualified Prelude
51import Data.List (splitAt, foldl', unfoldr)
54import Foreign.Storable
60import qualified Data.ByteString.Lazy as BS
61import Data.ByteString.Lazy (ByteString)
63import qualified Data.Digest.SHA1 as SHA1
66 {uuid_timeLow :: {-# UNPACK #-} !Word32
67 ,uuid_timeMid :: {-# UNPACK #-} !Word16
68 ,uuid_timeHigh :: {-# UNPACK #-} !Word16 -- includes version number
69 ,uuid_clockSeqHi :: {-# UNPACK #-} !Word8 -- includes reserved field
70 ,uuid_clokcSeqLow :: {-# UNPACK #-} !Word8
71 ,uuid_node :: {-# UNPACK #-} !Node
72 } deriving (Eq, Ord, Typeable)
74instance Random UUID where
75 random g = let (timeLow, g1) = random g
76 (timeMid, g2) = random g1
77 (timeHigh, g3) = random g2
78 (seqHigh, g4) = random g3
79 (seqLow, g5) = random g4
80 (node, g6) = random g5
81 seqHighReserved = (seqHigh .&. reservedMask) .|. reserved
82 timeHighVersion = (timeHigh .&. versionMask) .|. versionRandom
83 in (UUID timeLow timeMid timeHighVersion seqHighReserved seqLow node, g6)
85 randomR _ = random -- range is ignored
87versionMask :: Word16 -- 0000 1111 1111 1111
90versionRandom :: Word16
91versionRandom = shiftL 12 4
94versionSHA = shiftL 12 5
96reservedMask :: Word8 -- 0011 1111
103 {-# UNPACK #-} !Word8
104 {-# UNPACK #-} !Word8
105 {-# UNPACK #-} !Word8
106 {-# UNPACK #-} !Word8
107 {-# UNPACK #-} !Word8
108 {-# UNPACK #-} !Word8
109 deriving (Eq, Ord, Typeable)
111instance Random Node where
112 random g = let (w1, g1) = random g
118 in (Node w1 w2 w3 w4 w5 w6, g6)
119 randomR _ = random -- neglect range
122nodeToList :: Node -> [Word8]
123nodeToList (Node w1 w2 w3 w4 w5 w6) = [w1, w2, w3, w4, w5, w6]
125listToNode :: [Word8] -> Maybe Node
126listToNode [w1, w2, w3, w4, w5, w6] = return $ Node w1 w2 w3 w4 w5 w6
127listToNode _ = Nothing
129instance Show UUID where
132instance Read UUID where
133 readsPrec _ str = case fromString (take 36 str) of
135 Just u -> [(u,drop 36 str)]
138instance Storable UUID where
140 alignment _ = 4 -- not sure what to put here
143 tl <- peek $ castPtr p
144 tm <- peekByteOff p 4
145 th <- peekByteOff p 6
146 ch <- peekByteOff p 8
147 cl <- peekByteOff p 9
148 node <- peekByteOff p 10
149 return $ UUID tl tm th ch cl node
151 poke p (UUID tl tm th ch cl node) = do
157 pokeByteOff p 10 node
159instance Storable Node where
161 alignment _ = 1 -- ???
164 w1 <- peek $ castPtr p
165 w2 <- peekByteOff p 1
166 w3 <- peekByteOff p 2
167 w4 <- peekByteOff p 3
168 w5 <- peekByteOff p 4
169 w6 <- peekByteOff p 5
170 return $ Node w1 w2 w3 w4 w5 w6
172 poke p (Node w1 w2 w3 w4 w5 w6) = do
180-- Binary instance in network byte-order
181instance Binary UUID where
182 put (UUID tl tm th ch cl n) = do
197 return $ UUID tl tm th ch cl node
199instance Binary Node where
200 put (Node w1 w2 w3 w4 w5 w6) = do
215 return $ Node w1 w2 w3 w4 w5 w6
218-- My goal with this instance was to make it work just enough to do what
219-- I want when used with the HStringTemplate library.
220instance Data UUID where
221 toConstr uu = mkConstr uuidType (show uu) [] (error "fixity")
222 gunfold _ _ = error "gunfold"
223 dataTypeOf _ = uuidType
225uuidType = mkNorepType "Data.UUID.UUID"
228-- |Creates a new 'UUID'. If \/dev\/urandom is available, it will be used.
229-- Otherwise a UUID will be generated based on the current time and the
230-- hardware MAC address, if available.
232generate = generateRandom
234-- |Create a new 'UUID'. If \/dev\/urandom is available, it will be used.
235-- Otherwise a psuedorandom generator will be used.
236generateRandom :: IO UUID
237generateRandom = randomIO -- not a good solution, as someone could have changed the global generator
239-- |Create a new 'UUID'. The UUID will be generated based on the
240-- current time and the hardware MAC address, if available.
241generateTime :: IO UUID
242generateTime = error "Data.UUID.generateTime: not yet implemented"
244-- |Generate a 'UUID' within the specified namespace out of the given
248generateNamed :: UUID -- ^Namespace
249 -> [Word8] -- ^Object
251generateNamed namespace object =
252 let chunk = BS.unpack (encode namespace) ++ object
253 SHA1.Word160 w1 w2 w3 w4 w5 = SHA1.hash chunk
257 th = (versionSHA .|.) $ (versionMask .&.) $ high16 w2
258 ch = (reserved .|.) $ (reservedMask .&.) $ high8 $ high16 w3
259 cl = low8 $ high16 w3
260 node = Node (high8 (low16 w3))
266 in UUID tl tm th ch cl node
268-- HASH 0 1 - 2 3 : w1
274low16 :: Word32 -> Word16
275low16 = fromIntegral . (.&. 0x0000FFFF)
277high16 :: Word32 -> Word16
278high16 = fromIntegral . flip shiftR 16
280low8 :: Word16 -> Word8
281low8 = fromIntegral . (.&. 0x00FF)
283high8 :: Word16 -> Word8
284high8 = fromIntegral . flip shiftR 4
286-- this may be overkill ...
288 "low8/low16" forall x . low8 (low16 x) = fromIntegral (x .&. 0x000000FF)
289 "high8/low16" forall x . high8 (low16 x) = fromIntegral (shiftR (x .&. 0x0000FF00) 4)
290 "low8/high16" forall x . low8 (high16 x) = fromIntegral (shiftR (x .&. 0x00FF0000) 8)
291 "high8/high16" forall x . high8 (high16 x) = fromIntegral (shiftR x 12)
294unsafeFromString :: String -> UUID
295unsafeFromString = fromJust . fromString
297-- |The namespace for DNS addresses
299namespaceDNS = unsafeFromString "6ba7b810-9dad-11d1-80b4-00c04fd430c8"
301-- |The namespace for URLs
303namespaceURL = unsafeFromString "6ba7b811-9dad-11d1-80b4-00c04fd430c8"
306namespaceOID = unsafeFromString "6ba7b812-9dad-11d1-80b4-00c04fd430c8"
309namespaceX500 = unsafeFromString "6ba7b814-9dad-11d1-80b4-00c04fd430c8"
311-- |Returns 'True' if the passed-in 'UUID' is the null UUID.
314 where nullUuid = UUID 0 0 0 0 0 $ Node 0 0 0 0 0 0
317-- |If the passed in 'String' can be parsed as a 'UUID', it will be.
318-- The hyphens may not be omitted.
322-- fromString \"c2cc10e1-57d6-4b6f-9899-38d972112d8c\"
325-- Hex digits may be upper or lower-case.
326fromString :: String -> Maybe UUID
327fromString xs | validFmt = Just uuid
328 | otherwise = Nothing
329 where validFmt = length ws == 5 &&
330 map length ws == [8,4,4,4,12] &&
331 all isHexDigit (concat ws) &&
333 ws = splitList '-' xs
334 [tl, tm, th, c, n] = ws
335 ns = unfoldUntil Prelude.null (splitAt 2) n :: [String]
336 node = listToNode $ map hexVal ns :: Maybe Node
337 uuid = UUID (hexVal tl) (hexVal tm) (hexVal th) (hexVal $ take 2 c) (hexVal $ drop 2 c) (fromJust $ node)
339-- | Convert a string to a hex value, assuming the string is already validated.
340hexVal :: Num a => String -> a
341hexVal = fromInteger . foldl' (\n c -> 16*n + digitToInteger c) 0
343digitToInteger :: Char -> Integer
344digitToInteger = fromIntegral . digitToInt
346-- |Returns a 'String' representation of the passed in 'UUID'.
347-- Hex digits occuring in the output will be either upper or
348-- lower-case depending on system defaults and locale.
349toString :: UUID -> String
350toString = toStringLower
353-- | Convert a UUID into a hypenated string using upper-case letters.
354-- example: toStringUpper $ fromString "550e8400-e29b-41d4-a716-446655440000"
355toStringUpper :: UUID -> String
356toStringUpper (UUID tl tm th ch cl n) = printf "%08X-%04X-%04X-%02X%02X-%s" tl tm th ch cl ns
357 where ns = concatMap hexb $ nodeToList n
358 hexb x = printf "%02X" x :: String
360-- | Convert a UUID into a hypenated string using lower-case letters.
361-- example: toStringLower $ fromString "550e8400-e29b-41d4-a716-446655440000"
362toStringLower :: UUID -> String
363toStringLower (UUID tl tm th ch cl n) = printf "%08x-%04x-%04x-%02x%02x-%s" tl tm th ch cl ns
364 where ns = concatMap hexb $ nodeToList n
365 hexb x = printf "%02x" x :: String
367-- remove all occurances of the input element in the inpt list.
368-- non of the sub-lists are empty.
369splitList :: Eq a => a -> [a] -> [[a]]
370splitList c xs = let ys = dropWhile (== c) xs
371 in case span (/= c) ys of
373 (sub,rest) -> sub : splitList c rest
375unfoldUntil :: (b -> Bool) -> (b -> (a, b)) -> b -> [a]
376unfoldUntil p f n = unfoldr g n
377 where g m | p m = Nothing
378 | otherwise = Just $ f m
380-- I'm not sure how fast these instances are, but they'll do for now.
382instance Random Word8 where
383 random g = randomBoundedIntegral g
384 randomR r g = randomRIntegral r g
386instance Random Word16 where
387 random g = randomBoundedIntegral g
388 randomR r g = randomRIntegral r g
390instance Random Word32 where
391 random g = randomBoundedIntegral g
392 randomR r g = randomRIntegral r g
394randomBoundedIntegral :: (RandomGen g, Bounded a, Integral a) => g -> (a, g)
395randomBoundedIntegral g = let (n, g1) = randomR (fromIntegral l, fromIntegral u) g
397 retVal = fromIntegral n `asTypeOf` (l `asTypeOf` u)
402randomRIntegral :: (RandomGen g, Integral a) => (a, a) -> g -> (a, g)
403randomRIntegral (l, u) g = let (val, g1) = randomR (fromIntegral l, fromIntegral u) g
405 in (fromIntegral val, g1)
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list