diff options
Diffstat (limited to 'src/Network/DHT/Routing.hs')
-rw-r--r-- | src/Network/DHT/Routing.hs | 177 |
1 files changed, 93 insertions, 84 deletions
diff --git a/src/Network/DHT/Routing.hs b/src/Network/DHT/Routing.hs index 0004ac09..1821ca1c 100644 --- a/src/Network/DHT/Routing.hs +++ b/src/Network/DHT/Routing.hs | |||
@@ -25,8 +25,8 @@ | |||
25 | {-# OPTIONS_GHC -fno-warn-orphans #-} | 25 | {-# OPTIONS_GHC -fno-warn-orphans #-} |
26 | module Network.DHT.Routing | 26 | module Network.DHT.Routing |
27 | {- | 27 | {- |
28 | ( -- * Table | 28 | ( -- * BucketList |
29 | Table | 29 | BucketList |
30 | , Info(..) | 30 | , Info(..) |
31 | 31 | ||
32 | -- * Attributes | 32 | -- * Attributes |
@@ -86,8 +86,10 @@ import Text.PrettyPrint.HughesPJClass (pPrint,Pretty) | |||
86 | import qualified Data.ByteString as BS | 86 | import qualified Data.ByteString as BS |
87 | import Data.Bits | 87 | import Data.Bits |
88 | import Data.Ord | 88 | import Data.Ord |
89 | 89 | import Data.Reflection | |
90 | import Network.Address | 90 | import Network.Address |
91 | import Data.Typeable | ||
92 | import Data.Coerce | ||
91 | 93 | ||
92 | -- | Last time the node was responding to our queries. | 94 | -- | Last time the node was responding to our queries. |
93 | -- | 95 | -- |
@@ -167,6 +169,20 @@ bucketQ :: QueueMethods Identity ni (BucketQueue ni) | |||
167 | bucketQ = seqQ | 169 | bucketQ = seqQ |
168 | 170 | ||
169 | 171 | ||
172 | newtype Compare a = Compare (a -> a -> Ordering) | ||
173 | |||
174 | newtype Ordered s a = Ordered a | ||
175 | |||
176 | -- | Hack to avoid UndecidableInstances | ||
177 | newtype Shrink a = Shrink a | ||
178 | |||
179 | instance Reifies s (Compare a) => Eq (Ordered s (Shrink a)) where | ||
180 | a == b = (compare a b == EQ) | ||
181 | |||
182 | instance Reifies s (Compare a) => Ord (Ordered s (Shrink a)) where | ||
183 | compare a b = cmp (coerce a) (coerce b) | ||
184 | where Compare cmp = reflect (Proxy :: Proxy s) | ||
185 | |||
170 | -- | Bucket is also limited in its length — thus it's called k-bucket. | 186 | -- | Bucket is also limited in its length — thus it's called k-bucket. |
171 | -- When bucket becomes full, we should split it in two lists by | 187 | -- When bucket becomes full, we should split it in two lists by |
172 | -- current span bit. Span bit is defined by depth in the routing | 188 | -- current span bit. Span bit is defined by depth in the routing |
@@ -332,14 +348,14 @@ split testNodeIdBit i b = (Bucket ns qs, Bucket ms rs) | |||
332 | 348 | ||
333 | 349 | ||
334 | {----------------------------------------------------------------------- | 350 | {----------------------------------------------------------------------- |
335 | -- Table | 351 | -- BucketList |
336 | -----------------------------------------------------------------------} | 352 | -----------------------------------------------------------------------} |
337 | 353 | ||
338 | defaultBucketCount :: Int | 354 | defaultBucketCount :: Int |
339 | defaultBucketCount = 20 | 355 | defaultBucketCount = 20 |
340 | 356 | ||
341 | data Info ni nid = Info | 357 | data Info ni nid = Info |
342 | { myBuckets :: Table ni | 358 | { myBuckets :: BucketList ni |
343 | , myNodeId :: nid | 359 | , myNodeId :: nid |
344 | , myAddress :: SockAddr | 360 | , myAddress :: SockAddr |
345 | } | 361 | } |
@@ -366,22 +382,13 @@ deriving instance (Show ni, Show nid) => Show (Info ni nid) | |||
366 | -- is always split into two new buckets covering the ranges @0..2 ^ | 382 | -- is always split into two new buckets covering the ranges @0..2 ^ |
367 | -- 159@ and @2 ^ 159..2 ^ 160@. | 383 | -- 159@ and @2 ^ 159..2 ^ 160@. |
368 | -- | 384 | -- |
369 | data Table ni | 385 | data BucketList ni = BucketList !ni !Int ![Bucket ni] |
370 | -- most nearest bucket | ||
371 | = Tip ni Int (Bucket ni) | ||
372 | |||
373 | -- left biased tree branch | ||
374 | | Zero (Table ni) (Bucket ni) | ||
375 | |||
376 | -- right biased tree branch | ||
377 | | One (Bucket ni) (Table ni) | ||
378 | deriving Generic | 386 | deriving Generic |
379 | 387 | ||
380 | mapTable f (One b t) = One (mapBucket f b) (mapTable f t) | 388 | mapTable :: Ord ni => (a -> ni) -> BucketList a -> BucketList ni |
381 | mapTable f (Zero t b) = Zero (mapTable f t) (mapBucket f b) | 389 | mapTable f (BucketList self n bs) = BucketList (f self) n (map (mapBucket f) bs) |
382 | mapTable f (Tip ni n b) = Tip (f ni) n (mapBucket f b) | ||
383 | 390 | ||
384 | instance (Eq ni) => Eq (Table ni) where | 391 | instance (Eq ni) => Eq (BucketList ni) where |
385 | (==) = (==) `on` Network.DHT.Routing.toList | 392 | (==) = (==) `on` Network.DHT.Routing.toList |
386 | 393 | ||
387 | #if 0 | 394 | #if 0 |
@@ -392,19 +399,19 @@ instance Serialize NominalDiffTime where | |||
392 | 399 | ||
393 | #endif | 400 | #endif |
394 | 401 | ||
395 | deriving instance (Show ni) => Show (Table ni) | 402 | deriving instance (Show ni) => Show (BucketList ni) |
396 | 403 | ||
397 | #if 0 | 404 | #if 0 |
398 | 405 | ||
399 | -- | Normally, routing table should be saved between invocations of | 406 | -- | Normally, routing table should be saved between invocations of |
400 | -- the client software. Note that you don't need to store /this/ | 407 | -- the client software. Note that you don't need to store /this/ |
401 | -- 'NodeId' since it is already included in routing table. | 408 | -- 'NodeId' since it is already included in routing table. |
402 | instance (Eq ip, Serialize ip, Ord (NodeId), Serialize (NodeId), Serialize u) => Serialize (Table) | 409 | instance (Eq ip, Serialize ip, Ord (NodeId), Serialize (NodeId), Serialize u) => Serialize (BucketList) |
403 | 410 | ||
404 | #endif | 411 | #endif |
405 | 412 | ||
406 | -- | Shape of the table. | 413 | -- | Shape of the table. |
407 | instance Pretty (Table ni) where | 414 | instance Pretty (BucketList ni) where |
408 | pPrint t | 415 | pPrint t |
409 | | bucketCount < 6 = hcat $ punctuate ", " $ L.map PP.int ss | 416 | | bucketCount < 6 = hcat $ punctuate ", " $ L.map PP.int ss |
410 | | otherwise = brackets $ | 417 | | otherwise = brackets $ |
@@ -415,8 +422,11 @@ instance Pretty (Table ni) where | |||
415 | ss = shape t | 422 | ss = shape t |
416 | 423 | ||
417 | -- | Empty table with specified /spine/ node id. | 424 | -- | Empty table with specified /spine/ node id. |
418 | nullTable :: ni -> Int -> Table ni | 425 | nullTable :: ni -> Int -> BucketList ni |
419 | nullTable ni n = Tip ni (bucketCount (pred n)) (Bucket PSQ.empty (runIdentity $ emptyQueue bucketQ)) | 426 | nullTable ni n = BucketList |
427 | ni | ||
428 | (bucketCount (pred n)) | ||
429 | [Bucket PSQ.empty (runIdentity $ emptyQueue bucketQ)] | ||
420 | where | 430 | where |
421 | bucketCount x = max 0 (min 159 x) | 431 | bucketCount x = max 0 (min 159 x) |
422 | 432 | ||
@@ -424,19 +434,19 @@ nullTable ni n = Tip ni (bucketCount (pred n)) (Bucket PSQ.empty (runIdentity $ | |||
424 | 434 | ||
425 | -- | Test if table is empty. In this case DHT should start | 435 | -- | Test if table is empty. In this case DHT should start |
426 | -- bootstrapping process until table becomes 'full'. | 436 | -- bootstrapping process until table becomes 'full'. |
427 | null :: Table -> Bool | 437 | null :: BucketList -> Bool |
428 | null (Tip _ _ b) = PSQ.null $ bktNodes b | 438 | null (Tip _ _ b) = PSQ.null $ bktNodes b |
429 | null _ = False | 439 | null _ = False |
430 | 440 | ||
431 | -- | Test if table have maximum number of nodes. No more nodes can be | 441 | -- | Test if table have maximum number of nodes. No more nodes can be |
432 | -- 'insert'ed, except old ones becomes bad. | 442 | -- 'insert'ed, except old ones becomes bad. |
433 | full :: Table -> Bool | 443 | full :: BucketList -> Bool |
434 | full (Tip _ n _) = n == 0 | 444 | full (Tip _ n _) = n == 0 |
435 | full (Zero t b) = PSQ.size (bktNodes b) == defaultBucketSize && full t | 445 | full (Zero t b) = PSQ.size (bktNodes b) == defaultBucketSize && full t |
436 | full (One b t) = PSQ.size (bktNodes b) == defaultBucketSize && full t | 446 | full (One b t) = PSQ.size (bktNodes b) == defaultBucketSize && full t |
437 | 447 | ||
438 | -- | Get the /spine/ node id. | 448 | -- | Get the /spine/ node id. |
439 | thisId :: Table -> NodeId | 449 | thisId :: BucketList -> NodeId |
440 | thisId (Tip nid _ _) = nid | 450 | thisId (Tip nid _ _) = nid |
441 | thisId (Zero table _) = thisId table | 451 | thisId (Zero table _) = thisId table |
442 | thisId (One _ table) = thisId table | 452 | thisId (One _ table) = thisId table |
@@ -448,58 +458,57 @@ type NodeCount = Int | |||
448 | 458 | ||
449 | -- | Internally, routing table is similar to list of buckets or a | 459 | -- | Internally, routing table is similar to list of buckets or a |
450 | -- /matrix/ of nodes. This function returns the shape of the matrix. | 460 | -- /matrix/ of nodes. This function returns the shape of the matrix. |
451 | shape :: Table ni -> [Int] | 461 | shape :: BucketList ni -> [Int] |
452 | shape = map (PSQ.size . bktNodes) . toBucketList | 462 | shape = map (PSQ.size . bktNodes) . buckets |
453 | 463 | ||
454 | #if 0 | 464 | #if 0 |
455 | 465 | ||
456 | -- | Get number of nodes in the table. | 466 | -- | Get number of nodes in the table. |
457 | size :: Table -> NodeCount | 467 | size :: BucketList -> NodeCount |
458 | size = L.sum . shape | 468 | size = L.sum . shape |
459 | 469 | ||
460 | -- | Get number of buckets in the table. | 470 | -- | Get number of buckets in the table. |
461 | depth :: Table -> BucketCount | 471 | depth :: BucketList -> BucketCount |
462 | depth = L.length . shape | 472 | depth = L.length . shape |
463 | 473 | ||
464 | #endif | 474 | #endif |
465 | 475 | ||
466 | lookupBucket :: ( FiniteBits nid | 476 | lookupBucket :: ( FiniteBits nid |
467 | , Ord nid | 477 | , Ord nid |
468 | ) => nid -> Table ni -> [Bucket ni] | 478 | ) => (ni -> nid) -> nid -> BucketList ni -> [Bucket ni] |
469 | lookupBucket nid = go 0 [] | 479 | lookupBucket nodeId nid (BucketList self _ bkts) = go 0 [] bkts |
470 | where | 480 | where |
471 | go i bs (Zero table bucket) | 481 | d = complement (nid `xor` nodeId self) |
472 | | testIdBit nid i = bucket : toBucketList table ++ bs | 482 | |
473 | | otherwise = go (succ i) (bucket:bs) table | 483 | go i bs (bucket : buckets) |
474 | go i bs (One bucket table) | 484 | | testIdBit d i = bucket : buckets ++ bs |
475 | | testIdBit nid i = go (succ i) (bucket:bs) table | 485 | | otherwise = go (succ i) (bucket:bs) buckets |
476 | | otherwise = bucket : toBucketList table ++ bs | 486 | go _ bs [] = bs |
477 | go _ bs (Tip _ _ bucket) = bucket : bs | ||
478 | 487 | ||
479 | 488 | ||
480 | compatibleNodeId :: forall ni nid. | 489 | compatibleNodeId :: forall ni nid. |
481 | ( Serialize nid, FiniteBits nid) => | 490 | ( Serialize nid, FiniteBits nid) => |
482 | Table ni -> IO nid | 491 | (ni -> nid) -> BucketList ni -> IO nid |
483 | compatibleNodeId tbl = genBucketSample prefix br | 492 | compatibleNodeId nodeId tbl = genBucketSample prefix br |
484 | where | 493 | where |
485 | br = bucketRange (L.length (shape tbl) - 1) True | 494 | br = bucketRange (L.length (shape tbl) - 1) True |
486 | nodeIdSize = finiteBitSize (undefined :: nid) `div` 8 | 495 | nodeIdSize = finiteBitSize (undefined :: nid) `div` 8 |
487 | bs = BS.pack $ take nodeIdSize $ tablePrefix tbl ++ repeat 0 | 496 | bs = BS.pack $ take nodeIdSize $ tablePrefix (testIdBit . nodeId) tbl ++ repeat 0 |
488 | prefix = either error id $ S.decode bs | 497 | prefix = either error id $ S.decode bs |
489 | 498 | ||
490 | tablePrefix :: Table ni -> [Word8] | 499 | tablePrefix :: (ni -> Word -> Bool) -> BucketList ni -> [Word8] |
491 | tablePrefix = map (packByte . take 8 . (++repeat False)) | 500 | tablePrefix testbit = map (packByte . take 8 . (++repeat False)) |
492 | . chunksOf 8 | 501 | . chunksOf 8 |
493 | . tableBits | 502 | . tableBits testbit |
494 | where | 503 | where |
495 | packByte = foldl1' (.|.) . zipWith bitmask [7,6 .. 0] | 504 | packByte = foldl1' (.|.) . zipWith bitmask [7,6 .. 0] |
496 | bitmask ix True = bit ix | 505 | bitmask ix True = bit ix |
497 | bitmask _ _ = 0 | 506 | bitmask _ _ = 0 |
498 | 507 | ||
499 | tableBits :: Table ni -> [Bool] | 508 | tableBits :: (ni -> Word -> Bool) -> BucketList ni -> [Bool] |
500 | tableBits (One _ tbl) = True : tableBits tbl | 509 | tableBits testbit (BucketList self _ bkts) = |
501 | tableBits (Zero tbl _) = False : tableBits tbl | 510 | zipWith const (map (testbit self) [0..]) |
502 | tableBits (Tip _ _ _) = [] | 511 | bkts |
503 | 512 | ||
504 | chunksOf :: Int -> [e] -> [[e]] | 513 | chunksOf :: Int -> [e] -> [[e]] |
505 | chunksOf i ls = map (take i) (build (splitter ls)) where | 514 | chunksOf i ls = map (take i) (build (splitter ls)) where |
@@ -548,14 +557,14 @@ rank f nid = L.sortBy (comparing (distance nid . f)) | |||
548 | -- 'find_node' and 'get_peers' queries. | 557 | -- 'find_node' and 'get_peers' queries. |
549 | kclosest :: ( FiniteBits nid | 558 | kclosest :: ( FiniteBits nid |
550 | , Ord nid | 559 | , Ord nid |
551 | ) => (ni -> nid) -> Int -> nid -> Table ni -> [ni] | 560 | ) => (ni -> nid) -> Int -> nid -> BucketList ni -> [ni] |
552 | kclosest nodeId k nid tbl = take k $ rank nodeId nid (L.concat bucket) | 561 | kclosest nodeId k nid tbl = take k $ rank nodeId nid (L.concat bucket) |
553 | ++ rank nodeId nid (L.concat everyone) | 562 | ++ rank nodeId nid (L.concat everyone) |
554 | where | 563 | where |
555 | (bucket,everyone) = | 564 | (bucket,everyone) = |
556 | L.splitAt 1 | 565 | L.splitAt 1 |
557 | . L.map (L.map PSQ.key . PSQ.toList . bktNodes) | 566 | . L.map (L.map PSQ.key . PSQ.toList . bktNodes) |
558 | . lookupBucket nid | 567 | . lookupBucket nodeId nid |
559 | $ tbl | 568 | $ tbl |
560 | 569 | ||
561 | 570 | ||
@@ -567,10 +576,10 @@ kclosest nodeId k nid tbl = take k $ rank nodeId nid (L.concat bucket) | |||
567 | splitTip :: -- ( Eq ip , Ord (NodeId) , FiniteBits (NodeId)) => | 576 | splitTip :: -- ( Eq ip , Ord (NodeId) , FiniteBits (NodeId)) => |
568 | Ord ni => | 577 | Ord ni => |
569 | (ni -> Word -> Bool) | 578 | (ni -> Word -> Bool) |
570 | -> ni -> Int -> BitIx -> Bucket ni -> Table ni | 579 | -> ni -> Int -> BitIx -> Bucket ni -> [ Bucket ni ] |
571 | splitTip testNodeBit ni n i bucket | 580 | splitTip testNodeBit ni n i bucket |
572 | | testNodeBit ni i = (One zeros (Tip ni (pred n) ones)) | 581 | | testNodeBit ni i = [zeros , ones ] |
573 | | otherwise = (Zero (Tip ni (pred n) zeros) ones) | 582 | | otherwise = [ones , zeros ] |
574 | where | 583 | where |
575 | (ones, zeros) = split testNodeBit i bucket | 584 | (ones, zeros) = split testNodeBit i bucket |
576 | 585 | ||
@@ -581,23 +590,25 @@ splitTip testNodeBit ni n i bucket | |||
581 | -- paper. The rule requiring additional splits is in section 2.4. | 590 | -- paper. The rule requiring additional splits is in section 2.4. |
582 | modifyBucket | 591 | modifyBucket |
583 | :: -- ( Eq ip , Ord (NodeId) , FiniteBits (NodeId)) => | 592 | :: -- ( Eq ip , Ord (NodeId) , FiniteBits (NodeId)) => |
584 | forall ni nid xs. Ord ni => | 593 | forall ni nid xs. (Ord ni, Bits nid) => |
585 | (nid -> Word -> Bool) | 594 | (nid -> Word -> Bool) |
586 | -> (ni -> Word -> Bool) | 595 | -> (ni -> nid) |
587 | -> nid -> (Bucket ni -> Maybe (xs, Bucket ni)) -> Table ni -> Maybe (xs,Table ni) | 596 | -> nid -> (Bucket ni -> Maybe (xs, Bucket ni)) -> BucketList ni -> Maybe (xs,BucketList ni) |
588 | modifyBucket testIdBit testNodeBit nodeId f = go (0 :: BitIx) | 597 | modifyBucket testIdBit nodeId nid f (BucketList self n bkts) |
598 | = second (BucketList self n) <$> go (0 :: BitIx) bkts | ||
589 | where | 599 | where |
590 | go :: BitIx -> Table ni -> Maybe (xs, Table ni) | 600 | d = nid `xor` nodeId self |
591 | go !i (Zero table bucket) | 601 | |
592 | | testIdBit nodeId i = second (Zero table) <$> f bucket | 602 | go :: BitIx -> [Bucket ni] -> Maybe (xs, [Bucket ni]) |
593 | | otherwise = second (`Zero` bucket) <$> go (succ i) table | 603 | |
594 | go !i (One bucket table ) | 604 | go !i (bucket : buckets@(_:_)) |
595 | | testIdBit nodeId i = second (One bucket) <$> go (succ i) table | 605 | | testIdBit d i = second (bucket :) <$> go (succ i) buckets |
596 | | otherwise = second (`One` table) <$> f bucket | 606 | | otherwise = second (: buckets) <$> f bucket |
597 | go !i (Tip nid n bucket) | 607 | |
598 | | n == 0 = second (Tip nid n) <$> f bucket | 608 | go !i [bucket] |
599 | | otherwise = second (Tip nid n) <$> f bucket | 609 | | (n == 0) = second (: []) <$> f bucket |
600 | <|> go i (splitTip testNodeBit nid n i bucket) | 610 | | otherwise = second (: []) <$> f bucket |
611 | <|> go i (splitTip (testIdBit . nodeId) self n i bucket) | ||
601 | 612 | ||
602 | 613 | ||
603 | -- | Triggering event for atomic table update | 614 | -- | Triggering event for atomic table update |
@@ -642,15 +653,15 @@ deriving instance ( Show ip | |||
642 | -- may be added to a replacement queue and will be inserted if | 653 | -- may be added to a replacement queue and will be inserted if |
643 | -- one of the items in this list time out. | 654 | -- one of the items in this list time out. |
644 | -- | 655 | -- |
645 | -- [ /tbl'/ ] The updated routing 'Table'. | 656 | -- [ /tbl'/ ] The updated routing 'BucketList'. |
646 | -- | 657 | -- |
647 | updateForInbound :: Ord ni => | 658 | updateForInbound :: (Ord ni, Bits nid) => |
648 | KademliaSpace nid ni | 659 | KademliaSpace nid ni |
649 | -> Timestamp -> ni -> Table ni -> (Bool, [ni], Table ni) | 660 | -> Timestamp -> ni -> BucketList ni -> (Bool, [ni], BucketList ni) |
650 | updateForInbound space tm ni tbl = | 661 | updateForInbound space tm ni tbl = |
651 | maybe (False, [],tbl) (\(ps,tbl') -> (True, ps, tbl')) | 662 | maybe (False, [],tbl) (\(ps,tbl') -> (True, ps, tbl')) |
652 | $ modifyBucket (kademliaTestBit space) | 663 | $ modifyBucket (kademliaTestBit space) |
653 | (\ni -> kademliaTestBit space $ kademliaLocation space ni) | 664 | (kademliaLocation space) |
654 | (kademliaLocation space ni) | 665 | (kademliaLocation space ni) |
655 | (updateBucketForInbound tm ni) | 666 | (updateBucketForInbound tm ni) |
656 | tbl | 667 | tbl |
@@ -659,16 +670,16 @@ updateForInbound space tm ni tbl = | |||
659 | -- | 670 | -- |
660 | -- Each (a,(tm,b)) in the returned list indicates that the node /a/ was deleted from the | 671 | -- Each (a,(tm,b)) in the returned list indicates that the node /a/ was deleted from the |
661 | -- routing table and the node /b/, with timestamp /tm/, has taken its place. | 672 | -- routing table and the node /b/, with timestamp /tm/, has taken its place. |
662 | updateForPingResult :: Ord ni => | 673 | updateForPingResult :: (Ord ni, Bits nid) => |
663 | KademliaSpace nid ni | 674 | KademliaSpace nid ni |
664 | -> ni -- ^ The pinged node. | 675 | -> ni -- ^ The pinged node. |
665 | -> Bool -- ^ True if we got a reply, False if it timed out. | 676 | -> Bool -- ^ True if we got a reply, False if it timed out. |
666 | -> Table ni -- ^ The routing table. | 677 | -> BucketList ni -- ^ The routing table. |
667 | -> ( [(ni,(Timestamp, ni))], Table ni ) | 678 | -> ( [(ni,(Timestamp, ni))], BucketList ni ) |
668 | updateForPingResult space ni got_reply tbl = | 679 | updateForPingResult space ni got_reply tbl = |
669 | fromMaybe ([],tbl) | 680 | fromMaybe ([],tbl) |
670 | $ modifyBucket (kademliaTestBit space) | 681 | $ modifyBucket (kademliaTestBit space) |
671 | (\ni -> kademliaTestBit space $ kademliaLocation space ni) | 682 | (kademliaLocation space) |
672 | (kademliaLocation space ni) | 683 | (kademliaLocation space ni) |
673 | (updateBucketForPingResult ni got_reply) | 684 | (updateBucketForPingResult ni got_reply) |
674 | tbl | 685 | tbl |
@@ -684,13 +695,11 @@ tableEntry :: NodeEntry ni -> TableEntry ni | |||
684 | tableEntry (a :-> b) = (a, b) | 695 | tableEntry (a :-> b) = (a, b) |
685 | 696 | ||
686 | -- | Non-empty list of buckets. | 697 | -- | Non-empty list of buckets. |
687 | toBucketList :: Table ni -> [Bucket ni] | 698 | buckets :: BucketList ni -> [Bucket ni] |
688 | toBucketList (Tip _ _ b) = [b] | 699 | buckets (BucketList _ _ bs) = bs |
689 | toBucketList (Zero t b) = b : toBucketList t | ||
690 | toBucketList (One b t) = b : toBucketList t | ||
691 | 700 | ||
692 | toList :: Table ni -> [[TableEntry ni]] | 701 | toList :: BucketList ni -> [[TableEntry ni]] |
693 | toList = L.map (L.map tableEntry . PSQ.toList . bktNodes) . toBucketList | 702 | toList = L.map (L.map tableEntry . PSQ.toList . bktNodes) . buckets |
694 | 703 | ||
695 | data KademliaSpace nid ni = KademliaSpace | 704 | data KademliaSpace nid ni = KademliaSpace |
696 | { -- | Given a node record (probably including IP address), yields a | 705 | { -- | Given a node record (probably including IP address), yields a |