/NodeId.hs

http://github.com/astro/hashvortex · Haskell · 94 lines · 78 code · 16 blank · 0 comment · 2 complexity · fe15b372b04fe95ac6360e299ef28b69 MD5 · raw file

  1. module NodeId where
  2. import Control.Monad
  3. import qualified Data.ByteString as W8
  4. import qualified Data.ByteString.Lazy as LW8
  5. import GHC.Word
  6. import Numeric (showHex, readHex)
  7. import Data.Bits
  8. import System.Random
  9. import Control.DeepSeq
  10. import IntBuf
  11. newtype NodeId = NodeId W8.ByteString
  12. deriving (Eq, Ord)
  13. instance Show NodeId where
  14. show (NodeId b) = pad 40 $ showHex (bufToInteger b) ""
  15. where pad len s | length s < len = pad len $ '0':s
  16. | otherwise = s
  17. instance NFData NodeId where
  18. rnf (NodeId bs) = bs `seq` ()
  19. class NodeIdSource a where
  20. makeNodeId :: a -> NodeId
  21. instance NodeIdSource W8.ByteString where
  22. makeNodeId = NodeId
  23. instance NodeIdSource LW8.ByteString where
  24. makeNodeId = fNodeId . W8.concat . LW8.toChunks
  25. where fNodeId buf = buf `seq` NodeId buf
  26. makeRandomNodeId :: IO NodeId
  27. makeRandomNodeId = (NodeId .
  28. W8.pack .
  29. take 20 .
  30. map fromInteger .
  31. randomRs (0, 255)) `liftM` newStdGen
  32. makeRandomNeighbor :: NodeId -> IO NodeId
  33. makeRandomNeighbor (NodeId nodeIdBuf)
  34. = do gen <- newStdGen
  35. let nodeIdBuf' = fuzz gen nodeIdBuf
  36. nodeIdBuf' `seq`
  37. return $ NodeId nodeIdBuf'
  38. where fuzz :: RandomGen g => g
  39. -> W8.ByteString -> W8.ByteString
  40. fuzz g buf
  41. = let (i, g') = randomR (10, 19) g
  42. (x, g'') = randomR (0, 255) g
  43. in replace buf i (xor $ fromInteger x)
  44. replace :: W8.ByteString -> Int
  45. -> (Word8 -> Word8) -> W8.ByteString
  46. replace buf i f
  47. = let (buf', buf'') = W8.splitAt i buf
  48. in case W8.uncons buf'' of
  49. Nothing -> buf
  50. Just (c, buf''') ->
  51. let c' = f c
  52. in W8.concat [buf', W8.singleton c', buf''']
  53. nodeIdToBuf :: NodeId -> LW8.ByteString
  54. nodeIdToBuf (NodeId bs) = LW8.fromChunks [bs]
  55. distance :: NodeId -> NodeId -> Integer
  56. distance (NodeId a) (NodeId b)
  57. = bufToInteger $ W8.pack $ W8.zipWith xor a b
  58. (<->) :: NodeId -> NodeId -> Integer
  59. (<->) = distance
  60. distanceOrder :: NodeId -> NodeId -> Int
  61. distanceOrder a b = let r 0 = 0
  62. r n = 1 + r (n `shiftR` 1)
  63. in r $ distance a b
  64. nodeIdPlus :: NodeId -> Integer -> NodeId
  65. nodeIdPlus (NodeId buf) off = NodeId $ integerToBuf $ bufToInteger buf + off
  66. hexToNodeId :: String -> Maybe NodeId
  67. hexToNodeId s
  68. | length s == 40 = Just $ NodeId $
  69. W8.pack $
  70. map hexToByte $
  71. chunkify 2 s
  72. | otherwise = Nothing
  73. where hexToByte s' = let [(i, "")] = readHex s'
  74. in i
  75. chunkify size s'
  76. | length s' < size = []
  77. | otherwise = let (x, xs) = splitAt size s'
  78. in x : chunkify size xs