|
Lines 117-123
Ipv4NixVectorRouting::FlushIpv4RouteCach
|
Link Here
|
|---|
|
| 117 |
} |
117 |
} |
| 118 |
|
118 |
|
| 119 |
Ptr<NixVector> |
119 |
Ptr<NixVector> |
| 120 |
Ipv4NixVectorRouting::GetNixVector (Ptr<Node> source, Ipv4Address dest) |
120 |
Ipv4NixVectorRouting::GetNixVector (Ptr<Node> source, Ipv4Address dest, Ptr<NetDevice> oif) |
| 121 |
{ |
121 |
{ |
| 122 |
NS_LOG_FUNCTION_NOARGS (); |
122 |
NS_LOG_FUNCTION_NOARGS (); |
| 123 |
|
123 |
|
|
Lines 146-152
Ipv4NixVectorRouting::GetNixVector (Ptr<
|
Link Here
|
|---|
|
| 146 |
// otherwise proceed as normal |
146 |
// otherwise proceed as normal |
| 147 |
// and build the nix vector |
147 |
// and build the nix vector |
| 148 |
std::vector< Ptr<Node> > parentVector; |
148 |
std::vector< Ptr<Node> > parentVector; |
| 149 |
BFS (NodeList::GetNNodes (), source, destNode, parentVector); |
149 |
|
|
|
150 |
BFS (NodeList::GetNNodes (), source, destNode, parentVector, oif); |
| 150 |
|
151 |
|
| 151 |
if (BuildNixVector (parentVector, source->GetId (), destNode->GetId (), nixVector)) |
152 |
if (BuildNixVector (parentVector, source->GetId (), destNode->GetId (), nixVector)) |
| 152 |
{ |
153 |
{ |
|
Lines 495-501
Ipv4NixVectorRouting::RouteOutput (Ptr<P
|
Link Here
|
|---|
|
| 495 |
NS_LOG_LOGIC ("Nix-vector not in cache, build: "); |
496 |
NS_LOG_LOGIC ("Nix-vector not in cache, build: "); |
| 496 |
// Build the nix-vector, given this node and the |
497 |
// Build the nix-vector, given this node and the |
| 497 |
// dest IP address |
498 |
// dest IP address |
| 498 |
nixVectorInCache = GetNixVector (m_node, header.GetDestination ()); |
499 |
nixVectorInCache = GetNixVector (m_node, header.GetDestination (), oif); |
| 499 |
|
500 |
|
| 500 |
// cache it |
501 |
// cache it |
| 501 |
m_nixCache.insert (NixMap_t::value_type (header.GetDestination (), nixVectorInCache)); |
502 |
m_nixCache.insert (NixMap_t::value_type (header.GetDestination (), nixVectorInCache)); |
|
Lines 523-540
Ipv4NixVectorRouting::RouteOutput (Ptr<P
|
Link Here
|
|---|
|
| 523 |
uint32_t numberOfBits = nixVectorForPacket->BitCount (m_totalNeighbors); |
524 |
uint32_t numberOfBits = nixVectorForPacket->BitCount (m_totalNeighbors); |
| 524 |
uint32_t nodeIndex = nixVectorForPacket->ExtractNeighborIndex (numberOfBits); |
525 |
uint32_t nodeIndex = nixVectorForPacket->ExtractNeighborIndex (numberOfBits); |
| 525 |
|
526 |
|
| 526 |
// Possibly search here in a cache for this node index |
527 |
// Search here in a cache for this node index |
| 527 |
// and look for a Ipv4Route. If we have it, don't |
528 |
// and look for a Ipv4Route |
| 528 |
// need to do the next 3 lines. |
|
|
| 529 |
rtentry = GetIpv4RouteInCache (header.GetDestination ()); |
529 |
rtentry = GetIpv4RouteInCache (header.GetDestination ()); |
| 530 |
// not in cache |
530 |
|
| 531 |
if (!rtentry) |
531 |
if (!rtentry || !(rtentry->GetOutputDevice () == oif)) |
| 532 |
{ |
532 |
{ |
|
|
533 |
// not in cache or a different specified output |
| 534 |
// device is to be used |
| 535 |
|
| 536 |
// first, make sure we erase existing (incorrect) |
| 537 |
// rtentry from the map |
| 538 |
if (rtentry) |
| 539 |
{ |
| 540 |
m_ipv4RouteCache.erase(header.GetDestination ()); |
| 541 |
} |
| 542 |
|
| 533 |
NS_LOG_LOGIC ("Ipv4Route not in cache, build: "); |
543 |
NS_LOG_LOGIC ("Ipv4Route not in cache, build: "); |
| 534 |
Ipv4Address gatewayIp; |
544 |
Ipv4Address gatewayIp; |
| 535 |
uint32_t index = FindNetDeviceForNixIndex (nodeIndex, gatewayIp); |
545 |
uint32_t index = FindNetDeviceForNixIndex (nodeIndex, gatewayIp); |
|
|
546 |
int32_t interfaceIndex = 0; |
| 536 |
|
547 |
|
| 537 |
uint32_t interfaceIndex = (m_ipv4)->GetInterfaceForDevice(m_node->GetDevice(index)); |
548 |
if (!oif) |
|
|
549 |
{ |
| 550 |
interfaceIndex = (m_ipv4)->GetInterfaceForDevice(m_node->GetDevice(index)); |
| 551 |
} |
| 552 |
else |
| 553 |
{ |
| 554 |
interfaceIndex = (m_ipv4)->GetInterfaceForDevice(oif); |
| 555 |
} |
| 556 |
|
| 557 |
NS_ASSERT_MSG (interfaceIndex != -1, "Interface index not found for device"); |
| 558 |
|
| 538 |
Ipv4InterfaceAddress ifAddr = m_ipv4->GetAddress (interfaceIndex, 0); |
559 |
Ipv4InterfaceAddress ifAddr = m_ipv4->GetAddress (interfaceIndex, 0); |
| 539 |
|
560 |
|
| 540 |
// start filling in the Ipv4Route info |
561 |
// start filling in the Ipv4Route info |
|
Lines 543-549
Ipv4NixVectorRouting::RouteOutput (Ptr<P
|
Link Here
|
|---|
|
| 543 |
|
564 |
|
| 544 |
rtentry->SetGateway (gatewayIp); |
565 |
rtentry->SetGateway (gatewayIp); |
| 545 |
rtentry->SetDestination (header.GetDestination ()); |
566 |
rtentry->SetDestination (header.GetDestination ()); |
| 546 |
rtentry->SetOutputDevice (m_ipv4->GetNetDevice (interfaceIndex)); |
567 |
|
|
|
568 |
if (!oif) |
| 569 |
{ |
| 570 |
rtentry->SetOutputDevice (m_ipv4->GetNetDevice (interfaceIndex)); |
| 571 |
} |
| 572 |
else |
| 573 |
{ |
| 574 |
rtentry->SetOutputDevice (oif); |
| 575 |
} |
| 547 |
|
576 |
|
| 548 |
sockerr = Socket::ERROR_NOTERROR; |
577 |
sockerr = Socket::ERROR_NOTERROR; |
| 549 |
|
578 |
|
|
Lines 582-724
Ipv4NixVectorRouting::RouteInput (Ptr<co
|
Link Here
|
|---|
|
| 582 |
// Get the nix-vector from the packet |
611 |
// Get the nix-vector from the packet |
| 583 |
Ptr<NixVector> nixVector = p->GetNixVector(); |
612 |
Ptr<NixVector> nixVector = p->GetNixVector(); |
| 584 |
|
613 |
|
| 585 |
// make sure it exists, if not something |
614 |
// If nixVector isn't in packet, something went wrong |
| 586 |
// went wrong |
615 |
NS_ASSERT (nixVector); |
| 587 |
if (!nixVector) |
616 |
|
|
|
617 |
// Get the interface number that we go out of, by extracting |
| 618 |
// from the nix-vector |
| 619 |
if (m_totalNeighbors == 0) |
| 588 |
{ |
620 |
{ |
| 589 |
NS_LOG_ERROR ("Nix-vector wasn't in the packet! Rebuild."); |
621 |
m_totalNeighbors = FindTotalNeighbors (); |
|
|
622 |
} |
| 623 |
uint32_t numberOfBits = nixVector->BitCount (m_totalNeighbors); |
| 624 |
uint32_t nodeIndex = nixVector->ExtractNeighborIndex (numberOfBits); |
| 590 |
|
625 |
|
| 591 |
Ptr<NixVector> nixVectorInCache; |
626 |
rtentry = GetIpv4RouteInCache (header.GetDestination ()); |
|
|
627 |
// not in cache |
| 628 |
if (!rtentry) |
| 629 |
{ |
| 630 |
NS_LOG_LOGIC ("Ipv4Route not in cache, build: "); |
| 631 |
Ipv4Address gatewayIp; |
| 632 |
uint32_t index = FindNetDeviceForNixIndex (nodeIndex, gatewayIp); |
| 633 |
uint32_t interfaceIndex = (m_ipv4)->GetInterfaceForDevice(m_node->GetDevice(index)); |
| 634 |
Ipv4InterfaceAddress ifAddr = m_ipv4->GetAddress (interfaceIndex, 0); |
| 592 |
|
635 |
|
| 593 |
NS_LOG_DEBUG ("Dest IP from header: " << header.GetDestination ()); |
636 |
// start filling in the Ipv4Route info |
|
|
637 |
rtentry = Create<Ipv4Route> (); |
| 638 |
rtentry->SetSource (ifAddr.GetLocal ()); |
| 594 |
|
639 |
|
| 595 |
// check if cache |
640 |
rtentry->SetGateway (gatewayIp); |
| 596 |
nixVectorInCache = GetNixVectorInCache(header.GetDestination ()); |
641 |
rtentry->SetDestination (header.GetDestination ()); |
|
|
642 |
rtentry->SetOutputDevice (m_ipv4->GetNetDevice (interfaceIndex)); |
| 597 |
|
643 |
|
| 598 |
// not in cache |
644 |
// add rtentry to cache |
| 599 |
if (!nixVectorInCache) |
645 |
m_ipv4RouteCache.insert(Ipv4RouteMap_t::value_type(header.GetDestination (), rtentry)); |
| 600 |
{ |
646 |
} |
| 601 |
NS_LOG_LOGIC ("RouteInput(): Nix-vector not in cache, build: "); |
|
|
| 602 |
|
647 |
|
| 603 |
// Build the nix-vector, given this node and the |
648 |
NS_LOG_LOGIC ("At Node " << m_node->GetId() << ", Extracting " << numberOfBits << |
| 604 |
// dest IP address |
649 |
" bits from Nix-vector: " << nixVector << " : " << *nixVector); |
| 605 |
nixVectorInCache = GetNixVector (m_node, header.GetDestination ()); |
|
|
| 606 |
} |
| 607 |
|
650 |
|
| 608 |
// path exists |
651 |
// call the unicast callback |
| 609 |
if (nixVectorInCache) |
652 |
// local deliver is handled by Ipv4StaticRoutingImpl |
| 610 |
{ |
653 |
// so this code is never even called if the packet is |
| 611 |
NS_LOG_LOGIC ("Nix-vector contents: " << *nixVectorInCache); |
654 |
// destined for this node. |
|
|
655 |
ucb (rtentry, p, header); |
| 612 |
|
656 |
|
| 613 |
// cache it |
657 |
return true; |
| 614 |
m_nixCache.insert(NixMap_t::value_type(header.GetDestination (), nixVectorInCache)); |
|
|
| 615 |
|
| 616 |
// create a new nix vector to be used, |
| 617 |
// we want to keep the cached version clean |
| 618 |
Ptr<NixVector> nixVectorForPacket; |
| 619 |
nixVectorForPacket = CreateObject<NixVector> (); |
| 620 |
nixVectorForPacket = nixVectorInCache->Copy(); |
| 621 |
|
| 622 |
// Get the interface number that we go out of, by extracting |
| 623 |
// from the nix-vector |
| 624 |
if (m_totalNeighbors == 0) |
| 625 |
{ |
| 626 |
m_totalNeighbors = FindTotalNeighbors (); |
| 627 |
} |
| 628 |
uint32_t numberOfBits = nixVectorForPacket->BitCount (m_totalNeighbors); |
| 629 |
uint32_t nodeIndex = nixVectorForPacket->ExtractNeighborIndex (numberOfBits); |
| 630 |
|
| 631 |
rtentry = GetIpv4RouteInCache (header.GetDestination ()); |
| 632 |
// not in cache |
| 633 |
if (!rtentry) |
| 634 |
{ |
| 635 |
NS_LOG_LOGIC ("Ipv4Route not in cache, build: "); |
| 636 |
Ipv4Address gatewayIp; |
| 637 |
uint32_t index = FindNetDeviceForNixIndex (nodeIndex, gatewayIp); |
| 638 |
|
| 639 |
uint32_t interfaceIndex = (m_ipv4)->GetInterfaceForDevice(m_node->GetDevice(index)); |
| 640 |
Ipv4InterfaceAddress ifAddr = m_ipv4->GetAddress (interfaceIndex, 0); |
| 641 |
|
| 642 |
// start filling in the Ipv4Route info |
| 643 |
rtentry = Create<Ipv4Route> (); |
| 644 |
rtentry->SetSource (ifAddr.GetLocal ()); |
| 645 |
|
| 646 |
rtentry->SetGateway (Ipv4Address(gatewayIp)); |
| 647 |
rtentry->SetDestination (header.GetDestination ()); |
| 648 |
rtentry->SetOutputDevice (m_ipv4->GetNetDevice (interfaceIndex)); |
| 649 |
|
| 650 |
// add rtentry to cache |
| 651 |
m_ipv4RouteCache.insert(Ipv4RouteMap_t::value_type(header.GetDestination (), rtentry)); |
| 652 |
} |
| 653 |
|
| 654 |
NS_LOG_LOGIC ("Nix-vector contents: " << *nixVectorInCache << " : Remaining bits: " << nixVectorForPacket->GetRemainingBits()); |
| 655 |
|
| 656 |
// Add nix-vector in the packet class |
| 657 |
// have to copy the packet first b/c |
| 658 |
// it is const |
| 659 |
Ptr<Packet> newPacket = Create<Packet> (); |
| 660 |
newPacket = p->Copy(); |
| 661 |
|
| 662 |
NS_LOG_LOGIC ("Adding Nix-vector to packet: " << *nixVectorForPacket); |
| 663 |
newPacket->SetNixVector(nixVectorForPacket); |
| 664 |
|
| 665 |
// call the unicast callback |
| 666 |
// local deliver is handled by Ipv4StaticRoutingImpl |
| 667 |
// so this code is never even called if the packet is |
| 668 |
// destined for this node. |
| 669 |
ucb (rtentry, newPacket, header); |
| 670 |
return true; |
| 671 |
} |
| 672 |
else // path doesn't exist |
| 673 |
{ |
| 674 |
NS_LOG_ERROR ("No path to the dest: " << header.GetDestination ()); |
| 675 |
return false; |
| 676 |
} |
| 677 |
} |
| 678 |
else |
| 679 |
{ |
| 680 |
// Get the interface number that we go out of, by extracting |
| 681 |
// from the nix-vector |
| 682 |
if (m_totalNeighbors == 0) |
| 683 |
{ |
| 684 |
m_totalNeighbors = FindTotalNeighbors (); |
| 685 |
} |
| 686 |
uint32_t numberOfBits = nixVector->BitCount (m_totalNeighbors); |
| 687 |
uint32_t nodeIndex = nixVector->ExtractNeighborIndex (numberOfBits); |
| 688 |
|
| 689 |
rtentry = GetIpv4RouteInCache (header.GetDestination ()); |
| 690 |
// not in cache |
| 691 |
if (!rtentry) |
| 692 |
{ |
| 693 |
NS_LOG_LOGIC ("Ipv4Route not in cache, build: "); |
| 694 |
Ipv4Address gatewayIp; |
| 695 |
uint32_t index = FindNetDeviceForNixIndex (nodeIndex, gatewayIp); |
| 696 |
uint32_t interfaceIndex = (m_ipv4)->GetInterfaceForDevice(m_node->GetDevice(index)); |
| 697 |
Ipv4InterfaceAddress ifAddr = m_ipv4->GetAddress (interfaceIndex, 0); |
| 698 |
|
| 699 |
// start filling in the Ipv4Route info |
| 700 |
rtentry = Create<Ipv4Route> (); |
| 701 |
rtentry->SetSource (ifAddr.GetLocal ()); |
| 702 |
|
| 703 |
rtentry->SetGateway (gatewayIp); |
| 704 |
rtentry->SetDestination (header.GetDestination ()); |
| 705 |
rtentry->SetOutputDevice (m_ipv4->GetNetDevice (interfaceIndex)); |
| 706 |
|
| 707 |
// add rtentry to cache |
| 708 |
m_ipv4RouteCache.insert(Ipv4RouteMap_t::value_type(header.GetDestination (), rtentry)); |
| 709 |
} |
| 710 |
|
| 711 |
NS_LOG_LOGIC ("At Node " << m_node->GetId() << ", Extracting " << numberOfBits << |
| 712 |
" bits from Nix-vector: " << nixVector << " : " << *nixVector); |
| 713 |
|
| 714 |
// call the unicast callback |
| 715 |
// local deliver is handled by Ipv4StaticRoutingImpl |
| 716 |
// so this code is never even called if the packet is |
| 717 |
// destined for this node. |
| 718 |
ucb (rtentry, p, header); |
| 719 |
|
| 720 |
return true; |
| 721 |
} |
| 722 |
} |
658 |
} |
| 723 |
|
659 |
|
| 724 |
// virtual functions from Ipv4RoutingProtocol |
660 |
// virtual functions from Ipv4RoutingProtocol |
|
Lines 744-750
Ipv4NixVectorRouting::NotifyRemoveAddres
|
Link Here
|
|---|
|
| 744 |
} |
680 |
} |
| 745 |
|
681 |
|
| 746 |
bool |
682 |
bool |
| 747 |
Ipv4NixVectorRouting::BFS (uint32_t numberOfNodes, Ptr<Node> source, Ptr<Node> dest, std::vector< Ptr<Node> > & parentVector) |
683 |
Ipv4NixVectorRouting::BFS (uint32_t numberOfNodes, Ptr<Node> source, |
|
|
684 |
Ptr<Node> dest, std::vector< Ptr<Node> > & parentVector, |
| 685 |
Ptr<NetDevice> oif) |
| 748 |
{ |
686 |
{ |
| 749 |
NS_LOG_FUNCTION_NOARGS (); |
687 |
NS_LOG_FUNCTION_NOARGS (); |
| 750 |
|
688 |
|
|
Lines 772-812
Ipv4NixVectorRouting::BFS (uint32_t numb
|
Link Here
|
|---|
|
| 772 |
return true; |
710 |
return true; |
| 773 |
} |
711 |
} |
| 774 |
|
712 |
|
| 775 |
|
713 |
// if this is the first iteration of the loop and a |
| 776 |
// Iterate over the current node's adjacent vertices |
714 |
// specific output interface was given, make sure |
| 777 |
// and push them into the queue |
715 |
// we go this way |
| 778 |
for (uint32_t i = 0; i < (currNode->GetNDevices ()); i++) |
716 |
if (currNode == source && oif) |
| 779 |
{ |
717 |
{ |
| 780 |
// Get a net device from the node |
|
|
| 781 |
// as well as the channel, and figure |
| 782 |
// out the adjacent net device |
| 783 |
Ptr<NetDevice> localNetDevice = currNode->GetDevice (i); |
| 784 |
|
| 785 |
// make sure that we can go this way |
718 |
// make sure that we can go this way |
| 786 |
if (ipv4) |
719 |
if (ipv4) |
| 787 |
{ |
720 |
{ |
| 788 |
uint32_t interfaceIndex = (ipv4)->GetInterfaceForDevice(currNode->GetDevice(i)); |
721 |
uint32_t interfaceIndex = (ipv4)->GetInterfaceForDevice(oif); |
| 789 |
if (!(ipv4->IsUp (interfaceIndex))) |
722 |
if (!(ipv4->IsUp (interfaceIndex))) |
| 790 |
{ |
723 |
{ |
| 791 |
NS_LOG_LOGIC ("Ipv4Interface is down"); |
724 |
NS_LOG_LOGIC ("Ipv4Interface is down"); |
| 792 |
continue; |
725 |
return false; |
| 793 |
} |
726 |
} |
| 794 |
} |
727 |
} |
| 795 |
if (!(localNetDevice->IsLinkUp ())) |
728 |
if (!(oif->IsLinkUp ())) |
| 796 |
{ |
729 |
{ |
| 797 |
NS_LOG_LOGIC ("Link is down."); |
730 |
NS_LOG_LOGIC ("Link is down."); |
| 798 |
continue; |
731 |
return false; |
| 799 |
} |
732 |
} |
| 800 |
Ptr<Channel> channel = localNetDevice->GetChannel (); |
733 |
Ptr<Channel> channel = oif->GetChannel (); |
| 801 |
if (channel == 0) |
734 |
if (channel == 0) |
| 802 |
{ |
735 |
{ |
| 803 |
continue; |
736 |
return false; |
| 804 |
} |
737 |
} |
| 805 |
|
738 |
|
| 806 |
// this function takes in the local net dev, and channnel, and |
739 |
// this function takes in the local net dev, and channnel, and |
| 807 |
// writes to the netDeviceContainer the adjacent net devs |
740 |
// writes to the netDeviceContainer the adjacent net devs |
| 808 |
NetDeviceContainer netDeviceContainer; |
741 |
NetDeviceContainer netDeviceContainer; |
| 809 |
GetAdjacentNetDevices (localNetDevice, channel, netDeviceContainer); |
742 |
GetAdjacentNetDevices (oif, channel, netDeviceContainer); |
| 810 |
|
743 |
|
| 811 |
// Finally we can get the adjacent nodes |
744 |
// Finally we can get the adjacent nodes |
| 812 |
// and scan through them. We push them |
745 |
// and scan through them. We push them |
|
Lines 827-832
Ipv4NixVectorRouting::BFS (uint32_t numb
|
Link Here
|
|---|
|
| 827 |
} |
760 |
} |
| 828 |
} |
761 |
} |
| 829 |
} |
762 |
} |
|
|
763 |
else |
| 764 |
{ |
| 765 |
// Iterate over the current node's adjacent vertices |
| 766 |
// and push them into the queue |
| 767 |
for (uint32_t i = 0; i < (currNode->GetNDevices ()); i++) |
| 768 |
{ |
| 769 |
// Get a net device from the node |
| 770 |
// as well as the channel, and figure |
| 771 |
// out the adjacent net device |
| 772 |
Ptr<NetDevice> localNetDevice = currNode->GetDevice (i); |
| 773 |
|
| 774 |
// make sure that we can go this way |
| 775 |
if (ipv4) |
| 776 |
{ |
| 777 |
uint32_t interfaceIndex = (ipv4)->GetInterfaceForDevice(currNode->GetDevice(i)); |
| 778 |
if (!(ipv4->IsUp (interfaceIndex))) |
| 779 |
{ |
| 780 |
NS_LOG_LOGIC ("Ipv4Interface is down"); |
| 781 |
continue; |
| 782 |
} |
| 783 |
} |
| 784 |
if (!(localNetDevice->IsLinkUp ())) |
| 785 |
{ |
| 786 |
NS_LOG_LOGIC ("Link is down."); |
| 787 |
continue; |
| 788 |
} |
| 789 |
Ptr<Channel> channel = localNetDevice->GetChannel (); |
| 790 |
if (channel == 0) |
| 791 |
{ |
| 792 |
continue; |
| 793 |
} |
| 794 |
|
| 795 |
// this function takes in the local net dev, and channnel, and |
| 796 |
// writes to the netDeviceContainer the adjacent net devs |
| 797 |
NetDeviceContainer netDeviceContainer; |
| 798 |
GetAdjacentNetDevices (localNetDevice, channel, netDeviceContainer); |
| 799 |
|
| 800 |
// Finally we can get the adjacent nodes |
| 801 |
// and scan through them. We push them |
| 802 |
// to the greyNode queue, if they aren't |
| 803 |
// already there. |
| 804 |
for (NetDeviceContainer::Iterator iter = netDeviceContainer.Begin (); iter != netDeviceContainer.End (); iter++) |
| 805 |
{ |
| 806 |
Ptr<Node> remoteNode = (*iter)->GetNode (); |
| 807 |
|
| 808 |
// check to see if this node has been pushed before |
| 809 |
// by checking to see if it has a parent |
| 810 |
// if it doesn't (null or 0), then set its parent and |
| 811 |
// push to the queue |
| 812 |
if (parentVector.at (remoteNode->GetId ()) == 0) |
| 813 |
{ |
| 814 |
parentVector.at (remoteNode->GetId ()) = currNode; |
| 815 |
greyNodeList.push (remoteNode); |
| 816 |
} |
| 817 |
} |
| 818 |
} |
| 819 |
} |
| 830 |
|
820 |
|
| 831 |
// Pop off the head grey node. We have all its children. |
821 |
// Pop off the head grey node. We have all its children. |
| 832 |
// It is now black. |
822 |
// It is now black. |