/*++

Copyright (c) 1998, Microsoft Corporation

Module Name:

    range.c

Abstract:

    This module implements an efficient mapping from an arbitrary range of
    IP addresses to a minimal set of IP address-mask pairs covering the range.

    The key to the approach is to regard the set of all possible IP addresses
    as a full 32-bit deep binary tree. Then a single IP address is a path
    through that tree, and a range of addresses is the area between two paths
    through the tree. We then describe such a path-delineated area by pruning
    full subtrees of the area recursively from left to right.

Author:

    Abolade Gbadegesin (aboladeg)   20-Mar-1998

Revision History:

--*/

#include "precomp.h"
#pragma hdrstop


VOID
DecomposeRange(
    ULONG StartAddress,
    ULONG EndAddress,
    ULONG Mask,
    PDECOMPOSE_RANGE_CALLBACK Callback,
    PVOID CallbackContext
    )

/*++

Routine Description:

    This routine decomposes the range StartAddress-EndAddress into
    a minimal set of address-mask pairs, passing the generated
    pairs to the given callback routine.

Arguments:

    StartAddress - the start of the range

    EndAddress - the end of the range

    Mask - the most general mask covering the range

    Callback - routine invoked for each generated address-mask pair

    CallbackContext - context passed to 'Callback'.

Return Value:

    none.

--*/

{
    ULONG temp;

    //
    // Step 1:
    // Check for the first base case: the root of a full tree.
    //

    if ((StartAddress & ~Mask) == 0 && (EndAddress & ~Mask) == ~Mask) {

        if (Callback) { Callback(StartAddress, Mask, CallbackContext); }

        return;
    }

    //
    // Step 2.
    // Extend the mask by one bit to cover the first different position
    // between the start and end address, essentially moving down in the tree
    // to the node where the paths branch.
    //
    //      . <- Most general mask
    //      |
    //      * <- branching point
    //     / \
    //

    Mask = ntohl(Mask);
    Mask >>= 1; Mask |= (1<<31);
    Mask = htonl(Mask);

    //
    // Step 3.
    // Split the range, with the new right edge being a fully-rightward path
    // (no left turns) starting below and to the left of the branching point.
    //
    //      . <- branching point
    //     / \
    //    *
    //     \ <- new right edge
    //

    temp = StartAddress | ~Mask;

    //
    // Step 4.
    // Check for the second base case:
    // the left edge is a fully-leftward path (all-zeroes).
    //

    if ((StartAddress & ~Mask) == 0) {

        if (Callback) { Callback(StartAddress, Mask, CallbackContext); }
    }
    else {

        //
        // Not a base case, so take the left branch.
        //
    
        DecomposeRange(
            StartAddress,
            temp,
            Mask,
            Callback,
            CallbackContext
            );
    }

    //
    // we may be done, if the right edge is also fully rightward
    //

    if ((StartAddress | ~Mask) == EndAddress) { return; }

    //
    // Step 5.
    // Decompose the remaining portion of the range,
    // with the new left edge being the fully-leftward path which starts
    // below and to the right of the original branching point.
    //
    //      . <- branching point
    //     / \
    //        *
    //       / <- new left edge
    //

    temp = EndAddress & Mask;

    //
    // Step 6.
    // Check for the third base case:
    // the right edge is fully-rightward (all-ones).
    //

    if (EndAddress == (temp | ~Mask)) {

        if (Callback) { Callback(EndAddress, Mask, CallbackContext); }
    }
    else {

        //
        // Not a base case; take the right branch.
        //

        DecomposeRange(
            temp,
            EndAddress,
            MostGeneralMask(temp, EndAddress),
            Callback,
            CallbackContext
            );
    }
}


ULONG
MostGeneralMask(
    ULONG StartAddress,
    ULONG EndAddress
    )

/*++

Routine Description:

    This routine generates the most general mask covering the range
    'StartAddress' - 'EndAddress'.

Arguments:

    StartAddress - beginning of range, in network order

    EndAddress - end of range, in network order

Return Value:

    ULONG - the most general mask

--*/

{
    ULONG CommonBits, Mask;
    StartAddress = ntohl(StartAddress);
    EndAddress = ntohl(EndAddress);

    //
    // find the bits common to the start address and end address
    //

    CommonBits = ~(StartAddress ^ EndAddress);

    //
    // CommonBits now has a 1 in each position where StartAddress and
    // EndAddress are the same.
    // We want to reduce this to only include the longest contiguous
    // most significant bits
    // e.g. 11101110 becomes 11100000 and 11111101 becomes 11111100
    //

    for (Mask = 0xffffffff; Mask && ((CommonBits & Mask) != Mask); Mask<<=1) { }
    
    return htonl(Mask);
}