1135 lines
28 KiB
C
1135 lines
28 KiB
C
|
/*++
|
||
|
|
||
|
Copyright (c) 1991 Microsoft Corporation
|
||
|
|
||
|
Module Name:
|
||
|
|
||
|
blrange.c
|
||
|
|
||
|
Abstract:
|
||
|
|
||
|
This module implements ranges and rangelists. These can be used
|
||
|
to keep track of cached ranges of a disk for instance.
|
||
|
|
||
|
Author:
|
||
|
|
||
|
Cenk Ergan (cenke) 11-Jan-2000
|
||
|
|
||
|
Revision History:
|
||
|
|
||
|
--*/
|
||
|
|
||
|
#include "blrange.h"
|
||
|
|
||
|
//
|
||
|
// Range function definitions.
|
||
|
//
|
||
|
|
||
|
VOID
|
||
|
BlRangeListInitialize (
|
||
|
PBLCRANGE_LIST pRangeList,
|
||
|
OPTIONAL PBLCRANGE_MERGE_ROUTINE pMergeRoutine,
|
||
|
OPTIONAL PBLCRANGE_FREE_ROUTINE pFreeRoutine
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This routine initializes the range list whose address is passed in
|
||
|
so it can be used by the other range functions.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
pRangeList - Address of the range list to initialize.
|
||
|
|
||
|
pMergeRoutine - Optional routine to merge Data fields of merged
|
||
|
range entries. See PBLCRANGE_MERGE_ROUTINE description.
|
||
|
|
||
|
pFreeRoutine - Optional routine to free the memory for an entry
|
||
|
that was merged into another. See PBLCRANGE_FREE_ROUTINE desc.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None. [Always successful]
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
InitializeListHead(&pRangeList->Head);
|
||
|
pRangeList->NumEntries = 0;
|
||
|
pRangeList->MergeRoutine = pMergeRoutine;
|
||
|
pRangeList->FreeRoutine = pFreeRoutine;
|
||
|
}
|
||
|
|
||
|
BOOLEAN
|
||
|
BlRangeListAddRange (
|
||
|
PBLCRANGE_LIST pRangeList,
|
||
|
PBLCRANGE_ENTRY pRangeEntry
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This routine adds pRangeEntry to pRangeList only if it does not
|
||
|
have any overlap with other ranges in the list and its size > 0;
|
||
|
If merging becomes possible it is attempted. It does not have to
|
||
|
be successful.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
pRangeList - Address of the range list to add range to.
|
||
|
|
||
|
pRangeEntry - Range to add to pRangeList.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
TRUE if addition is successful [even if merging was possible but failed]
|
||
|
FALSE if not [e.g. overlap/collusion]
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
PBLCRANGE_ENTRY pCurEntry = NULL;
|
||
|
PBLCRANGE_ENTRY pLastEntry = NULL;
|
||
|
LIST_ENTRY *pHead, *pNext;
|
||
|
|
||
|
//
|
||
|
// Handle special empty range case.
|
||
|
//
|
||
|
|
||
|
if (pRangeEntry->Range.Start == pRangeEntry->Range.End)
|
||
|
return TRUE;
|
||
|
|
||
|
//
|
||
|
// Walk through the ranges in the sorted list checking for
|
||
|
// overlaps and looking for the right place for us.
|
||
|
//
|
||
|
|
||
|
pHead = &pRangeList->Head;
|
||
|
pNext = pHead->Flink;
|
||
|
while (pNext != pHead)
|
||
|
{
|
||
|
pCurEntry = CONTAINING_RECORD(pNext, BLCRANGE_ENTRY, Link);
|
||
|
|
||
|
//
|
||
|
// Check if we are completely before this entry.
|
||
|
//
|
||
|
|
||
|
if (pRangeEntry->Range.End <= pCurEntry->Range.Start)
|
||
|
{
|
||
|
//
|
||
|
// Insert the new entry at its place.
|
||
|
//
|
||
|
|
||
|
InsertTailList(pNext, &pRangeEntry->Link);
|
||
|
pRangeList->NumEntries++;
|
||
|
|
||
|
//
|
||
|
// Check if merging is possible.
|
||
|
//
|
||
|
|
||
|
if (pLastEntry && (pRangeEntry->Range.Start == pLastEntry->Range.End))
|
||
|
{
|
||
|
BlRangeListMergeRangeEntries(
|
||
|
pRangeList,
|
||
|
pRangeEntry,
|
||
|
pLastEntry
|
||
|
);
|
||
|
}
|
||
|
|
||
|
if (pRangeEntry->Range.End == pCurEntry->Range.Start)
|
||
|
{
|
||
|
BlRangeListMergeRangeEntries(
|
||
|
pRangeList,
|
||
|
pRangeEntry,
|
||
|
pCurEntry
|
||
|
);
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Check if we are not completely after this entry.
|
||
|
//
|
||
|
|
||
|
if (pRangeEntry->Range.Start < pCurEntry->Range.End)
|
||
|
{
|
||
|
//
|
||
|
// We have an overlapping range.
|
||
|
//
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// We come after this entry.
|
||
|
//
|
||
|
|
||
|
pLastEntry = pCurEntry;
|
||
|
pNext = pNext->Flink;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// We come after the last entry [if there is one], i.e. before the head.
|
||
|
// Insert the new entry and check if merging is possible.
|
||
|
//
|
||
|
|
||
|
InsertTailList(pHead, &pRangeEntry->Link);
|
||
|
pRangeList->NumEntries++;
|
||
|
|
||
|
if (pLastEntry && (pRangeEntry->Range.Start == pLastEntry->Range.End))
|
||
|
{
|
||
|
BlRangeListMergeRangeEntries(
|
||
|
pRangeList,
|
||
|
pRangeEntry,
|
||
|
pLastEntry
|
||
|
);
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
BOOLEAN
|
||
|
BlRangeListFindOverlaps (
|
||
|
PBLCRANGE_LIST pRangeList,
|
||
|
PBLCRANGE pRange,
|
||
|
PBLCRANGE_ENTRY *pOverlapsBuffer,
|
||
|
ULONG OverlapsBufferSize,
|
||
|
OUT ULONG *pNumOverlaps
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This routine will find ranges in pRangeList that overlap with
|
||
|
pRange and put pointers to them into pOverlapsBuffer one after the
|
||
|
other. If all overlapping ranges cannot be copied because
|
||
|
pOverlapsBuffer is NULL or OverlapsBufferSize is 0 or not enough,
|
||
|
the function will return FALSE and but still put the number of
|
||
|
overlapping ranges in pNumOverlaps. You can calculate the required
|
||
|
buffer size from this.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
pRangeList - Address of the range list to search for overlaps.
|
||
|
|
||
|
pRange - We will look for range entries that overlap with pRange.
|
||
|
|
||
|
pOverlapsBuffer - Pointer to buffer we can fill with pointers to
|
||
|
overlapping ranges.
|
||
|
|
||
|
OverlapsBufferSize - Size up to which we can fill pOverlapsBuffer.
|
||
|
|
||
|
pNumOverlaps - Number of overlapping ranges will always be put
|
||
|
here.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
TRUE if successful, FALSE if not.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
PBLCRANGE_ENTRY pCurEntry;
|
||
|
LIST_ENTRY *pHead, *pNext;
|
||
|
ULONG NumOverlaps = 0;
|
||
|
ULONG RequiredOverlapsBufferSize = 0;
|
||
|
|
||
|
//
|
||
|
// Handle special empty range case.
|
||
|
//
|
||
|
|
||
|
if (pRange->Start == pRange->End)
|
||
|
{
|
||
|
*pNumOverlaps = NumOverlaps;
|
||
|
return (pOverlapsBuffer != NULL);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Walk through the ranges in the sorted list and copy over ones
|
||
|
// that overlap into callers buffer if there is enough space.
|
||
|
//
|
||
|
|
||
|
pHead = &pRangeList->Head;
|
||
|
pNext = pHead->Flink;
|
||
|
while (pNext != pHead)
|
||
|
{
|
||
|
pCurEntry = CONTAINING_RECORD(pNext, BLCRANGE_ENTRY, Link);
|
||
|
|
||
|
if ((pRange->End > pCurEntry->Range.Start) &&
|
||
|
(pRange->Start < pCurEntry->Range.End))
|
||
|
{
|
||
|
//
|
||
|
// This entry overlaps.
|
||
|
//
|
||
|
|
||
|
RequiredOverlapsBufferSize += sizeof(PBLCRANGE_ENTRY);
|
||
|
if (pOverlapsBuffer &&
|
||
|
(OverlapsBufferSize >= RequiredOverlapsBufferSize))
|
||
|
{
|
||
|
pOverlapsBuffer[NumOverlaps] = pCurEntry;
|
||
|
}
|
||
|
NumOverlaps++;
|
||
|
}
|
||
|
|
||
|
pNext = pNext->Flink;
|
||
|
}
|
||
|
|
||
|
*pNumOverlaps = NumOverlaps;
|
||
|
|
||
|
return (pOverlapsBuffer &&
|
||
|
(OverlapsBufferSize >= RequiredOverlapsBufferSize));
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOLEAN
|
||
|
BlRangeListFindDistinctRanges (
|
||
|
PBLCRANGE_LIST pRangeList,
|
||
|
PBLCRANGE pRange,
|
||
|
PBLCRANGE pDistinctRanges,
|
||
|
ULONG BufferSize,
|
||
|
OUT ULONG *pNumRanges
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This routine will look at ranges in pRangeList that overlap with
|
||
|
pRange and extract the overlaps from pRange, thus keeping track of
|
||
|
those ranges that are distinct. If all distinct ranges cannot be
|
||
|
put into pDistinctRanges buffer because pDistinctRanges is NULL or
|
||
|
BufferSize is 0 or not enough, the function will return FALSE and
|
||
|
but still put the number of resulting distinct ranges in
|
||
|
pNumRanges. You can calculate the required buffer size from
|
||
|
this.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
pRangeList - Address of the range list.
|
||
|
|
||
|
pRange - We will extract distinct ranges in pRange that do not
|
||
|
overlap with other ranges in pRangeList.
|
||
|
|
||
|
pDistinctRanges - Pointer to buffer we can fill with distinct
|
||
|
ranges.
|
||
|
|
||
|
BufferSize - Size up to which we can fill pDistinctRanges buffer.
|
||
|
|
||
|
pNumRanges - Number of resulting distinct ranges will always be
|
||
|
put here.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
TRUE if successful, FALSE if not.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
PBLCRANGE_ENTRY pCurEntry;
|
||
|
BLCRANGE RemainingRange = *pRange;
|
||
|
ULONGLONG OverlapStart;
|
||
|
ULONGLONG OverlapEnd;
|
||
|
LIST_ENTRY *pHead, *pNext;
|
||
|
ULONG NumRanges = 0;
|
||
|
ULONG RequiredBufferSize = 0;
|
||
|
|
||
|
if (pRange->Start == pRange->End)
|
||
|
{
|
||
|
*pNumRanges = NumRanges;
|
||
|
return (pDistinctRanges != NULL);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Looking at each range in the sorted list, we carve out overlap
|
||
|
// and distinct zones from the start of our range.
|
||
|
//
|
||
|
|
||
|
pHead = &pRangeList->Head;
|
||
|
pNext = pHead->Flink;
|
||
|
while (pNext != pHead)
|
||
|
{
|
||
|
pCurEntry = CONTAINING_RECORD(pNext, BLCRANGE_ENTRY, Link);
|
||
|
|
||
|
//
|
||
|
// Is there still anything remaining from the range that we
|
||
|
// have not carved out as overlap or distinct?
|
||
|
//
|
||
|
|
||
|
if (RemainingRange.Start >= RemainingRange.End)
|
||
|
break;
|
||
|
|
||
|
//
|
||
|
// There are three possibilities:
|
||
|
//
|
||
|
|
||
|
//
|
||
|
// 1. Is the range completely before the current range?
|
||
|
//
|
||
|
|
||
|
if (RemainingRange.End <= pCurEntry->Range.Start)
|
||
|
{
|
||
|
//
|
||
|
// The whole range is distinct.
|
||
|
//
|
||
|
|
||
|
RequiredBufferSize += sizeof(BLCRANGE);
|
||
|
if (pDistinctRanges && (RequiredBufferSize <= BufferSize))
|
||
|
{
|
||
|
pDistinctRanges[NumRanges].Start = RemainingRange.Start;
|
||
|
pDistinctRanges[NumRanges].End = RemainingRange.End;
|
||
|
}
|
||
|
NumRanges++;
|
||
|
|
||
|
RemainingRange.Start = RemainingRange.End;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// 2. Are we completely beyond the current range?
|
||
|
//
|
||
|
|
||
|
if (RemainingRange.Start >= pCurEntry->Range.End)
|
||
|
{
|
||
|
//
|
||
|
// We cannot carve out anything from the remaining range.
|
||
|
// Fall through to processing the next entry.
|
||
|
//
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// 3. Is the remaining range overlaps with the current range.
|
||
|
//
|
||
|
|
||
|
if ((RemainingRange.End > pCurEntry->Range.Start) &&
|
||
|
(RemainingRange.Start < pCurEntry->Range.End))
|
||
|
{
|
||
|
OverlapStart = BLRGMAX(RemainingRange.Start,
|
||
|
pCurEntry->Range.Start);
|
||
|
OverlapEnd = BLRGMIN(RemainingRange.End,
|
||
|
pCurEntry->Range.End);
|
||
|
|
||
|
if (OverlapStart > pRange->Start)
|
||
|
{
|
||
|
//
|
||
|
// There is a distinct region before the overlap
|
||
|
//
|
||
|
RequiredBufferSize += sizeof(BLCRANGE);
|
||
|
if (pDistinctRanges && (RequiredBufferSize <= BufferSize))
|
||
|
{
|
||
|
pDistinctRanges[NumRanges].Start = RemainingRange.Start;
|
||
|
pDistinctRanges[NumRanges].End = OverlapStart;
|
||
|
}
|
||
|
NumRanges++;
|
||
|
}
|
||
|
|
||
|
RemainingRange.Start = OverlapEnd;
|
||
|
}
|
||
|
|
||
|
pNext = pNext->Flink;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// The remaining range (if there is any) is also distinct.
|
||
|
//
|
||
|
|
||
|
if (RemainingRange.Start < RemainingRange.End)
|
||
|
{
|
||
|
RequiredBufferSize += sizeof(BLCRANGE);
|
||
|
if (pDistinctRanges && (RequiredBufferSize <= BufferSize))
|
||
|
{
|
||
|
pDistinctRanges[NumRanges].Start = RemainingRange.Start;
|
||
|
pDistinctRanges[NumRanges].End = RemainingRange.End;
|
||
|
}
|
||
|
NumRanges++;
|
||
|
}
|
||
|
|
||
|
*pNumRanges = NumRanges;
|
||
|
|
||
|
return (pDistinctRanges &&
|
||
|
RequiredBufferSize <= BufferSize);
|
||
|
}
|
||
|
|
||
|
BOOLEAN
|
||
|
BlRangeListMergeRangeEntries (
|
||
|
PBLCRANGE_LIST pRangeList,
|
||
|
PBLCRANGE_ENTRY pDestEntry,
|
||
|
PBLCRANGE_ENTRY pSrcEntry
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Merges SrcEntry and DestEntry range entries into DestEntry by
|
||
|
calling BlRangeEntryMerge. If successful it tries to remove
|
||
|
pSrcEntry from the range list it is in and free its memory by
|
||
|
calling the FreeRoutine specified on the list.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
pRangeList - Range list pDestEntry and pSrcEntry belong to.
|
||
|
|
||
|
pDestEntry - Range entry that we will merge into.
|
||
|
|
||
|
pSrcEntry - Range entry that will be merged into pDestEntry,
|
||
|
removed from its list and free'ed.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
TRUE if successful, FALSE if not. The success is mainly determined
|
||
|
by calls to a MergeRoutine if specified on the list.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
|
||
|
if(BlRangeEntryMerge(pDestEntry,
|
||
|
pSrcEntry,
|
||
|
pRangeList->MergeRoutine))
|
||
|
{
|
||
|
//
|
||
|
// Remove pSrcEntry from the list since it is merged into
|
||
|
// pDestEntry now.
|
||
|
//
|
||
|
|
||
|
pRangeList->NumEntries--;
|
||
|
RemoveEntryList(&pSrcEntry->Link);
|
||
|
|
||
|
//
|
||
|
// Free the removed entry.
|
||
|
//
|
||
|
|
||
|
if (pRangeList->FreeRoutine) pRangeList->FreeRoutine(pSrcEntry);
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
BOOLEAN
|
||
|
BlRangeEntryMerge (
|
||
|
PBLCRANGE_ENTRY pDestEntry,
|
||
|
PBLCRANGE_ENTRY pSrcEntry,
|
||
|
OPTIONAL PBLCRANGE_MERGE_ROUTINE pMergeRoutine
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Merges SrcEntry and DestEntry range entries into DestEntry. It
|
||
|
uses pMergeRoutine if specified to merge the user's Data field of
|
||
|
the range entries.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
pDestEntry - Range entry that we will merge into
|
||
|
|
||
|
pSrcEntry - Range entry that will be merged into pDestEntry
|
||
|
|
||
|
pMergeRoutine - Optional routine to merge Data fields of
|
||
|
merged range entries. See PBLCRANGE_MERGE_ROUTINE description.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
TRUE if successful, FALSE if not. The success is mainly
|
||
|
determined by calls to the pMergeRoutine if specified.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
BLCRANGE_ENTRY TempDest = *pDestEntry;
|
||
|
BOOLEAN RetVal = TRUE;
|
||
|
|
||
|
if (pMergeRoutine)
|
||
|
{
|
||
|
RetVal = pMergeRoutine(&TempDest, pSrcEntry);
|
||
|
}
|
||
|
|
||
|
if (RetVal)
|
||
|
{
|
||
|
TempDest.Range.Start = BLRGMIN(TempDest.Range.Start,
|
||
|
pSrcEntry->Range.Start);
|
||
|
TempDest.Range.End = BLRGMAX(TempDest.Range.End,
|
||
|
pSrcEntry->Range.End);
|
||
|
|
||
|
*pDestEntry = TempDest;
|
||
|
}
|
||
|
|
||
|
return RetVal;
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
BlRangeListRemoveRange (
|
||
|
PBLCRANGE_LIST pRangeList,
|
||
|
PBLCRANGE pRange
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Find the ranges that overlap with pRange, remove them from the
|
||
|
list and free them. It may be possible to reclaim non-overlapping
|
||
|
parts of range entries by allowing the caller to specify a
|
||
|
DivideRoutine in an _Ex version of this function. This function
|
||
|
would be called for invalidating part of the cache, if the range
|
||
|
list is being used for a disk cache.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
pRangeList - Range entry list we are removing range entries that
|
||
|
overlap with pRange from.
|
||
|
|
||
|
pRange - Range to remove from the range entry list.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
PBLCRANGE_ENTRY pCurEntry;
|
||
|
LIST_ENTRY *pHead, *pNext;
|
||
|
|
||
|
//
|
||
|
// Handle special empty range case.
|
||
|
//
|
||
|
|
||
|
if (pRange->Start == pRange->End)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Looking at each range in the list, remove the ones that overlap with
|
||
|
// pRange even slightly.
|
||
|
//
|
||
|
|
||
|
pHead = &pRangeList->Head;
|
||
|
pNext = pHead->Flink;
|
||
|
while (pNext != pHead)
|
||
|
{
|
||
|
pCurEntry = CONTAINING_RECORD(pNext, BLCRANGE_ENTRY, Link);
|
||
|
pNext = pNext->Flink;
|
||
|
|
||
|
if ((pRange->End > pCurEntry->Range.Start) &&
|
||
|
(pRange->Start < pCurEntry->Range.End))
|
||
|
{
|
||
|
pRangeList->NumEntries--;
|
||
|
RemoveEntryList(&pCurEntry->Link);
|
||
|
if (pRangeList->FreeRoutine) pRangeList->FreeRoutine(pCurEntry);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
BlRangeListRemoveAllRanges (
|
||
|
PBLCRANGE_LIST pRangeList
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Remove all ranges from the list and free them.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
pRangeList - Range entry list we are removing range entries that
|
||
|
overlap with pRange from.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
PBLCRANGE_ENTRY pCurEntry;
|
||
|
LIST_ENTRY *pHead, *pNext;
|
||
|
|
||
|
pHead = &pRangeList->Head;
|
||
|
pNext = pHead->Flink;
|
||
|
while (pNext != pHead)
|
||
|
{
|
||
|
pCurEntry = CONTAINING_RECORD(pNext, BLCRANGE_ENTRY, Link);
|
||
|
|
||
|
pRangeList->NumEntries--;
|
||
|
RemoveEntryList(&pCurEntry->Link);
|
||
|
if (pRangeList->FreeRoutine) pRangeList->FreeRoutine(pCurEntry);
|
||
|
|
||
|
pNext = pNext->Flink;
|
||
|
}
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
#ifdef BLRANGE_SELF_TEST
|
||
|
|
||
|
//
|
||
|
// In order to to test blrange implementation, define
|
||
|
// BLRANGE_SELF_TEST and call BlRangeSelfTest from you program passing
|
||
|
// in a function to output debug results.
|
||
|
//
|
||
|
|
||
|
#include <stdlib.h>
|
||
|
#include <stdio.h>
|
||
|
#include <time.h>
|
||
|
|
||
|
//
|
||
|
// Keep MAX_RANDOM above 1000 or you may hit difficulties creating new
|
||
|
// entries.
|
||
|
//
|
||
|
|
||
|
#define MAX_RANDOM 10000
|
||
|
|
||
|
VOID
|
||
|
GetRandom_GetNewSeed(
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
srand((unsigned)time(NULL));
|
||
|
}
|
||
|
|
||
|
ULONG
|
||
|
GetRandom(
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
return (rand() * 10000 / RAND_MAX);
|
||
|
}
|
||
|
|
||
|
typedef
|
||
|
int
|
||
|
(*PBLCRANGE_SELFTEST_FPRINTF_ROUTINE) (
|
||
|
void *stream,
|
||
|
const char *format,
|
||
|
...
|
||
|
);
|
||
|
|
||
|
PBLCRANGE_SELFTEST_FPRINTF_ROUTINE g_fpTestPrintf = NULL;
|
||
|
VOID *g_pTestStream = NULL;
|
||
|
|
||
|
BOOLEAN
|
||
|
BlRangeSelfTest_MergeRoutine (
|
||
|
PBLCRANGE_ENTRY pDestEntry,
|
||
|
PBLCRANGE_ENTRY pSrcEntry
|
||
|
)
|
||
|
{
|
||
|
g_fpTestPrintf(g_pTestStream,
|
||
|
" Merging RangeDest %I64u-%I64u RangeSrc %I64u-%I64u : ",
|
||
|
pDestEntry->Range.Start,
|
||
|
pDestEntry->Range.End,
|
||
|
pSrcEntry->Range.Start,
|
||
|
pSrcEntry->Range.End);
|
||
|
|
||
|
if (GetRandom() < (MAX_RANDOM / 5))
|
||
|
{
|
||
|
g_fpTestPrintf(g_pTestStream,"FAIL\n");
|
||
|
return FALSE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
g_fpTestPrintf(g_pTestStream,"SUCCESS\n");
|
||
|
return TRUE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
BlRangeSelfTest_FreeRoutine (
|
||
|
PBLCRANGE_ENTRY pRangeEntry
|
||
|
)
|
||
|
{
|
||
|
g_fpTestPrintf(g_pTestStream,
|
||
|
" Freeing range %I64u-%I64u \n",
|
||
|
pRangeEntry->Range.Start,
|
||
|
pRangeEntry->Range.End);
|
||
|
|
||
|
free(pRangeEntry);
|
||
|
}
|
||
|
|
||
|
BLCRANGE
|
||
|
BlRangeSelfTest_RandomRange(
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
BLCRANGE RetRange;
|
||
|
ULONG Rand1;
|
||
|
ULONG Rand2;
|
||
|
ULONGLONG Size;
|
||
|
ULONG i;
|
||
|
|
||
|
Rand1 = GetRandom();
|
||
|
Rand2 = GetRandom();
|
||
|
|
||
|
RetRange.Start = BLRGMIN(Rand1, Rand2);
|
||
|
RetRange.End = BLRGMAX(Rand1, Rand2);
|
||
|
|
||
|
//
|
||
|
// Make sure that ranges are small and there are not just a couple
|
||
|
// of big ones.
|
||
|
//
|
||
|
|
||
|
for (i = 0; i < 3; i++)
|
||
|
{
|
||
|
if ((Size = (RetRange.End - RetRange.Start)) > MAX_RANDOM / 20)
|
||
|
{
|
||
|
RetRange.Start += (Size / 2);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return RetRange;
|
||
|
}
|
||
|
|
||
|
PBLCRANGE_ENTRY
|
||
|
BlRangeSelfTest_CreateNewEntry(
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
PBLCRANGE_ENTRY pNewEntry;
|
||
|
|
||
|
pNewEntry = malloc(sizeof(BLCRANGE_ENTRY));
|
||
|
|
||
|
if (pNewEntry)
|
||
|
{
|
||
|
pNewEntry->Range = BlRangeSelfTest_RandomRange();
|
||
|
}
|
||
|
|
||
|
return pNewEntry;
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
BlRangeSelfTest_FreeEntry(
|
||
|
PBLCRANGE_ENTRY pEntry
|
||
|
)
|
||
|
{
|
||
|
free(pEntry);
|
||
|
}
|
||
|
|
||
|
typedef enum _BLRANGE_OP_TYPE
|
||
|
{
|
||
|
BLRANGE_OP_ADD_RANGE,
|
||
|
BLRANGE_OP_ADD_MERGE_RANGE,
|
||
|
BLRANGE_OP_REMOVE_RANGE,
|
||
|
BLRANGE_OP_FIND_OVERLAP,
|
||
|
BLRANGE_OP_FIND_DISTINCT,
|
||
|
BLRANGE_OP_MAX_OP_NO, // Leave this at the end of the enumeration.
|
||
|
} BLRANGE_OP_TYPE;
|
||
|
|
||
|
VOID
|
||
|
BlRangeSelfTest(
|
||
|
PBLCRANGE_SELFTEST_FPRINTF_ROUTINE TestOutFPrintf,
|
||
|
PVOID TestOutStream,
|
||
|
ULONG NumIterations
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Range routines self test routine.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
TestOutFPrintf - Pointer to a routine like fprintf that will be used to
|
||
|
print the output.
|
||
|
|
||
|
TestOutStream - Argument to be passed to fpPrintf as its first argument.
|
||
|
|
||
|
NumIterations - Number of random operations to perform in this self test.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
BLCRANGE_LIST RangeList;
|
||
|
ULONG Rand1;
|
||
|
ULONG Rand2;
|
||
|
BLCRANGE Range1;
|
||
|
PBLCRANGE_ENTRY pEntry1;
|
||
|
PBLCRANGE_ENTRY pEntry2;
|
||
|
BLRANGE_OP_TYPE OpType;
|
||
|
PLIST_ENTRY pHead;
|
||
|
PLIST_ENTRY pNext;
|
||
|
PBLCRANGE_ENTRY *pOverlaps;
|
||
|
PBLCRANGE pDistinctRanges;
|
||
|
ULONG BufSize;
|
||
|
ULONG NumDistincts;
|
||
|
ULONG NumOverlaps;
|
||
|
ULONG RandEntryNo;
|
||
|
|
||
|
//
|
||
|
// Simulation Parameters.
|
||
|
//
|
||
|
|
||
|
ULONG StartNumRanges = 10;
|
||
|
|
||
|
ULONG CurIterIdx;
|
||
|
ULONG CurRangeIdx;
|
||
|
ULONG CurEntryIdx;
|
||
|
|
||
|
//
|
||
|
// Set global output function and stream variable so merge/free etc.
|
||
|
// routines can also output.
|
||
|
//
|
||
|
|
||
|
g_fpTestPrintf = TestOutFPrintf;
|
||
|
g_pTestStream = TestOutStream;
|
||
|
|
||
|
//
|
||
|
// Set semi-random starting point for pseudorandom number generation.
|
||
|
//
|
||
|
|
||
|
GetRandom_GetNewSeed();
|
||
|
|
||
|
|
||
|
//
|
||
|
// Initialize the range list.
|
||
|
//
|
||
|
|
||
|
BlRangeListInitialize(&RangeList,
|
||
|
BlRangeSelfTest_MergeRoutine,
|
||
|
BlRangeSelfTest_FreeRoutine);
|
||
|
|
||
|
//
|
||
|
// Try to add StartNumRanges random entries.
|
||
|
//
|
||
|
|
||
|
for(CurRangeIdx = 0; CurRangeIdx < StartNumRanges; CurRangeIdx++)
|
||
|
{
|
||
|
pEntry1 = BlRangeSelfTest_CreateNewEntry();
|
||
|
|
||
|
if (!pEntry1) continue;
|
||
|
|
||
|
g_fpTestPrintf(g_pTestStream,
|
||
|
"AddStartRange %I64u-%I64u : ",
|
||
|
pEntry1->Range.Start,
|
||
|
pEntry1->Range.End);
|
||
|
|
||
|
if (BlRangeListAddRange(&RangeList, pEntry1))
|
||
|
{
|
||
|
g_fpTestPrintf(g_pTestStream, "SUCCESS\n");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
g_fpTestPrintf(g_pTestStream, "FAILED\n");
|
||
|
BlRangeSelfTest_FreeEntry(pEntry1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for(CurIterIdx = 0; CurIterIdx < NumIterations; CurIterIdx++)
|
||
|
{
|
||
|
//
|
||
|
// Print out the current list.
|
||
|
//
|
||
|
|
||
|
g_fpTestPrintf(g_pTestStream, "List: ");
|
||
|
pHead = &RangeList.Head;
|
||
|
pNext = pHead->Flink;
|
||
|
while (pNext != pHead)
|
||
|
{
|
||
|
pEntry1 = CONTAINING_RECORD(pNext, BLCRANGE_ENTRY, Link);
|
||
|
|
||
|
g_fpTestPrintf(g_pTestStream,
|
||
|
"%I64u-%I64u ",
|
||
|
pEntry1->Range.Start,
|
||
|
pEntry1->Range.End);
|
||
|
|
||
|
pNext = pNext->Flink;
|
||
|
}
|
||
|
g_fpTestPrintf(g_pTestStream, "\n");
|
||
|
|
||
|
get_new_optype:
|
||
|
OpType = GetRandom() % BLRANGE_OP_MAX_OP_NO;
|
||
|
|
||
|
switch (OpType)
|
||
|
{
|
||
|
case BLRANGE_OP_ADD_RANGE:
|
||
|
|
||
|
pEntry1 = BlRangeSelfTest_CreateNewEntry();
|
||
|
g_fpTestPrintf(g_pTestStream,
|
||
|
"AddRange %I64u-%I64u : ",
|
||
|
pEntry1->Range.Start,
|
||
|
pEntry1->Range.End);
|
||
|
|
||
|
if (BlRangeListAddRange(&RangeList, pEntry1))
|
||
|
{
|
||
|
g_fpTestPrintf(g_pTestStream, "SUCCESS\n");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
g_fpTestPrintf(g_pTestStream, "FAILED\n");
|
||
|
BlRangeSelfTest_FreeEntry(pEntry1);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case BLRANGE_OP_ADD_MERGE_RANGE:
|
||
|
|
||
|
RandEntryNo = GetRandom() * RangeList.NumEntries / MAX_RANDOM;
|
||
|
|
||
|
pHead = &RangeList.Head;
|
||
|
pNext = pHead->Flink;
|
||
|
|
||
|
for (CurEntryIdx = 0; CurEntryIdx < RandEntryNo; CurEntryIdx++)
|
||
|
{
|
||
|
pNext = pNext->Flink;
|
||
|
}
|
||
|
|
||
|
if (pNext == pHead) goto get_new_optype;
|
||
|
|
||
|
pEntry1 = CONTAINING_RECORD(pNext, BLCRANGE_ENTRY, Link);
|
||
|
pEntry2 = BlRangeSelfTest_CreateNewEntry();
|
||
|
|
||
|
if (GetRandom() > (MAX_RANDOM / 2))
|
||
|
{
|
||
|
pEntry2->Range.Start = pEntry1->Range.End;
|
||
|
pEntry2->Range.End = pEntry2->Range.Start +
|
||
|
(GetRandom() * (MAX_RANDOM - pEntry2->Range.Start)) / MAX_RANDOM;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pEntry2->Range.End = pEntry1->Range.Start;
|
||
|
pEntry2->Range.Start = pEntry2->Range.End -
|
||
|
(GetRandom() * pEntry2->Range.End) / MAX_RANDOM;
|
||
|
}
|
||
|
|
||
|
g_fpTestPrintf(g_pTestStream,
|
||
|
"MergeAddRange %I64u-%I64u : ",
|
||
|
pEntry2->Range.Start,
|
||
|
pEntry2->Range.End);
|
||
|
|
||
|
if (BlRangeListAddRange(&RangeList, pEntry2))
|
||
|
{
|
||
|
g_fpTestPrintf(g_pTestStream, "SUCCESS\n");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
g_fpTestPrintf(g_pTestStream, "FAILED\n");
|
||
|
BlRangeSelfTest_FreeEntry(pEntry2);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case BLRANGE_OP_REMOVE_RANGE:
|
||
|
|
||
|
Range1 = BlRangeSelfTest_RandomRange();
|
||
|
|
||
|
g_fpTestPrintf(g_pTestStream,
|
||
|
"RemoveRange %I64u-%I64u\n",
|
||
|
Range1.Start,
|
||
|
Range1.End);
|
||
|
|
||
|
BlRangeListRemoveRange(&RangeList, &Range1);
|
||
|
|
||
|
break;
|
||
|
|
||
|
case BLRANGE_OP_FIND_OVERLAP:
|
||
|
|
||
|
Range1 = BlRangeSelfTest_RandomRange();
|
||
|
|
||
|
g_fpTestPrintf(g_pTestStream,
|
||
|
"FindOverlaps %I64u-%I64u : ",
|
||
|
Range1.Start,
|
||
|
Range1.End);
|
||
|
|
||
|
BlRangeListFindOverlaps(&RangeList,
|
||
|
&Range1,
|
||
|
NULL,
|
||
|
0,
|
||
|
&NumOverlaps);
|
||
|
|
||
|
g_fpTestPrintf(g_pTestStream, "%u Overlaps... ", NumOverlaps);
|
||
|
|
||
|
BufSize = NumOverlaps * sizeof(PBLCRANGE_ENTRY);
|
||
|
pOverlaps = malloc(BufSize);
|
||
|
|
||
|
if (!pOverlaps) goto get_new_optype;
|
||
|
|
||
|
if (BlRangeListFindOverlaps(&RangeList,
|
||
|
&Range1,
|
||
|
pOverlaps,
|
||
|
BufSize,
|
||
|
&NumOverlaps) &&
|
||
|
(BufSize == NumOverlaps * sizeof(PBLCRANGE_ENTRY)))
|
||
|
{
|
||
|
g_fpTestPrintf(g_pTestStream, "SUCCESS\n");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
g_fpTestPrintf(g_pTestStream, "FAIL\n");
|
||
|
free(pOverlaps);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
for (CurEntryIdx = 0; CurEntryIdx < NumOverlaps; CurEntryIdx++)
|
||
|
{
|
||
|
g_fpTestPrintf(g_pTestStream,
|
||
|
" %I64u-%I64u\n",
|
||
|
pOverlaps[CurEntryIdx]->Range.Start,
|
||
|
pOverlaps[CurEntryIdx]->Range.End);
|
||
|
}
|
||
|
|
||
|
free(pOverlaps);
|
||
|
|
||
|
break;
|
||
|
|
||
|
case BLRANGE_OP_FIND_DISTINCT:
|
||
|
|
||
|
Range1 = BlRangeSelfTest_RandomRange();
|
||
|
|
||
|
g_fpTestPrintf(g_pTestStream,
|
||
|
"FindDistincts %I64u-%I64u : ",
|
||
|
Range1.Start,
|
||
|
Range1.End);
|
||
|
|
||
|
BlRangeListFindDistinctRanges(&RangeList,
|
||
|
&Range1,
|
||
|
NULL,
|
||
|
0,
|
||
|
&NumDistincts);
|
||
|
|
||
|
g_fpTestPrintf(g_pTestStream, "%u Distincts... ", NumDistincts);
|
||
|
|
||
|
BufSize = NumDistincts * sizeof(BLCRANGE);
|
||
|
pDistinctRanges = malloc(BufSize);
|
||
|
|
||
|
if (!pDistinctRanges) goto get_new_optype;
|
||
|
|
||
|
if (BlRangeListFindDistinctRanges(&RangeList,
|
||
|
&Range1,
|
||
|
pDistinctRanges,
|
||
|
BufSize,
|
||
|
&NumDistincts) &&
|
||
|
(BufSize == NumDistincts * sizeof(BLCRANGE)))
|
||
|
{
|
||
|
g_fpTestPrintf(g_pTestStream, "SUCCESS\n");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
g_fpTestPrintf(g_pTestStream, "FAIL\n");
|
||
|
free(pDistinctRanges);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
for (CurRangeIdx = 0; CurRangeIdx < NumDistincts; CurRangeIdx++)
|
||
|
{
|
||
|
g_fpTestPrintf(g_pTestStream,
|
||
|
" %I64u-%I64u\n",
|
||
|
pDistinctRanges[CurRangeIdx].Start,
|
||
|
pDistinctRanges[CurRangeIdx].End);
|
||
|
}
|
||
|
|
||
|
free(pDistinctRanges);
|
||
|
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
g_fpTestPrintf(g_pTestStream, "ERR: INVALID OP CODE!");
|
||
|
goto get_new_optype;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
#endif // BLRANGE_SELF_TEST
|