@@ -20,9 +20,14 @@ module Data.UUID(UUID
21 ,toStringUpper
21 ,toStringUpper
22 ,toStringLower
22 ,toStringLower
23− ,generate
23+ ,generate
24 ,generateRandom
24 ,generateRandom
25 ,generateTime
25 ,generateTime
@@ -48,6 +53,14 @@ import Data.List (splitAt, foldl', unfoldr)
48import Foreign.Ptr
53import Foreign.Ptr
49import Foreign.Storable
54import Foreign.Storable
57+import Data.Binary.Put
58+import Data.Binary.Get
60+import qualified Data.ByteString.Lazy as BS
61+import Data.ByteString.Lazy (ByteString)
63+import qualified Data.Digest.SHA1 as SHA1
52data UUID = UUID
65data UUID = UUID
53 {uuid_timeLow :: {-# UNPACK #-} !Word32
66 {uuid_timeLow :: {-# UNPACK #-} !Word32
@@ -77,6 +90,9 @@ versionMask = 0x0FFF
77versionRandom :: Word16
90versionRandom :: Word16
78versionRandom = shiftL 12 4
91versionRandom = shiftL 12 4
94+versionSHA = shiftL 12 5
80reservedMask :: Word8 -- 0011 1111
96reservedMask :: Word8 -- 0011 1111
81reservedMask = 0x3F
97reservedMask = 0x3F
@@ -102,6 +118,7 @@ instance Random Node where
102 in (Node w1 w2 w3 w4 w5 w6, g6)
118 in (Node w1 w2 w3 w4 w5 w6, g6)
103 randomR _ = random -- neglect range
119 randomR _ = random -- neglect range
105nodeToList :: Node -> [Word8]
122nodeToList :: Node -> [Word8]
106nodeToList (Node w1 w2 w3 w4 w5 w6) = [w1, w2, w3, w4, w5, w6]
123nodeToList (Node w1 w2 w3 w4 w5 w6) = [w1, w2, w3, w4, w5, w6]
@@ -160,6 +177,44 @@ instance Storable Node where
160 pokeByteOff p 4 w5
177 pokeByteOff p 4 w5
161 pokeByteOff p 5 w6
178 pokeByteOff p 5 w6
180+-- Binary instance in network byte-order
181+instance Binary UUID where
182+ put (UUID tl tm th ch cl n) = do
197+ return $ UUID tl tm th ch cl node
199+instance Binary Node where
200+ put (Node w1 w2 w3 w4 w5 w6) = do
215+ return $ Node w1 w2 w3 w4 w5 w6
163-- My goal with this instance was to make it work just enough to do what
218-- My goal with this instance was to make it work just enough to do what
164-- I want when used with the HStringTemplate library.
219-- I want when used with the HStringTemplate library.
165instance Data UUID where
220instance Data UUID where
@@ -186,6 +241,73 @@ generateRandom = randomIO -- not a good solution, as someone could have changed
186generateTime :: IO UUID
241generateTime :: IO UUID
187generateTime = error "Data.UUID.generateTime: not yet implemented"
242generateTime = error "Data.UUID.generateTime: not yet implemented"
244+-- |Generate a 'UUID' within the specified namespace out of the given
248+generateNamed :: UUID -- ^Namespace
249+ -> [Word8] -- ^Object
251+generateNamed 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
271+-- 12 13 - 14 15 : w4
272+-- 16 17 - 18 19 : w5
274+low16 :: Word32 -> Word16
275+low16 = fromIntegral . (.&. 0x0000FFFF)
277+high16 :: Word32 -> Word16
278+high16 = fromIntegral . flip shiftR 16
280+low8 :: Word16 -> Word8
281+low8 = fromIntegral . (.&. 0x00FF)
283+high8 :: Word16 -> Word8
284+high8 = 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)
294+unsafeFromString :: String -> UUID
295+unsafeFromString = fromJust . fromString
297+-- |The namespace for DNS addresses
299+namespaceDNS = unsafeFromString "6ba7b810-9dad-11d1-80b4-00c04fd430c8"
301+-- |The namespace for URLs
303+namespaceURL = unsafeFromString "6ba7b811-9dad-11d1-80b4-00c04fd430c8"
306+namespaceOID = unsafeFromString "6ba7b812-9dad-11d1-80b4-00c04fd430c8"
308+namespaceX500 :: UUID
309+namespaceX500 = unsafeFromString "6ba7b814-9dad-11d1-80b4-00c04fd430c8"
189-- |Returns 'True' if the passed-in 'UUID' is the null UUID.
311-- |Returns 'True' if the passed-in 'UUID' is the null UUID.
190null :: UUID -> Bool
312null :: UUID -> Bool
191null = (== nullUuid)
313null = (== nullUuid)
@@ -252,9 +374,8 @@ splitList c xs = let ys = dropWhile (== c) xs
253unfoldUntil :: (b -> Bool) -> (b -> (a, b)) -> b -> [a]
375unfoldUntil :: (b -> Bool) -> (b -> (a, b)) -> b -> [a]
254unfoldUntil p f n = unfoldr g n
376unfoldUntil p f n = unfoldr g n
255− where g m = case p m of
377+ where g m | p m = Nothing
257− False -> Just $ f m
378+ | otherwise = Just $ f m
259-- I'm not sure how fast these instances are, but they'll do for now.
380-- I'm not sure how fast these instances are, but they'll do for now.