3 * Copyright (C) 2004 ReactOS Team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 /* $Id: rangelist.c,v 1.2 2004/08/15 16:39:11 chorns Exp $
21 * COPYRIGHT: See COPYING in the top level directory
22 * PROJECT: ReactOS system libraries
23 * PURPOSE: Range list implementation
24 * FILE: ntoskrnl/rtl/rangelist.c
27 /* INCLUDES ****************************************************************/
31 #include <internal/debug.h>
34 #define ROUND_DOWN(N, S) ((N) - ((N) % (S)))
36 typedef struct _RTL_RANGE_ENTRY
40 } RTL_RANGE_ENTRY
, *PRTL_RANGE_ENTRY
;
43 /* FUNCTIONS ***************************************************************/
45 /**********************************************************************
50 * Adds a range to a range list.
53 * RangeList Range list.
65 * - Support shared ranges.
70 RtlAddRange (IN OUT PRTL_RANGE_LIST RangeList
,
75 IN PVOID UserData OPTIONAL
,
76 IN PVOID Owner OPTIONAL
)
78 PRTL_RANGE_ENTRY RangeEntry
;
79 PRTL_RANGE_ENTRY Previous
;
80 PRTL_RANGE_ENTRY Current
;
84 return STATUS_INVALID_PARAMETER
;
86 /* Create new range entry */
87 RangeEntry
= ExAllocatePool (PagedPool
,
88 sizeof(RTL_RANGE_ENTRY
));
89 if (RangeEntry
== NULL
)
90 return STATUS_INSUFFICIENT_RESOURCES
;
92 /* Initialize range entry */
93 RangeEntry
->Range
.Start
= Start
;
94 RangeEntry
->Range
.End
= End
;
95 RangeEntry
->Range
.Attributes
= Attributes
;
96 RangeEntry
->Range
.UserData
= UserData
;
97 RangeEntry
->Range
.Owner
= Owner
;
99 RangeEntry
->Range
.Flags
= 0;
100 if (Flags
& RTL_RANGE_LIST_ADD_SHARED
)
101 RangeEntry
->Range
.Flags
|= RTL_RANGE_SHARED
;
103 /* Insert range entry */
104 if (RangeList
->Count
== 0)
106 InsertTailList (&RangeList
->ListHead
,
110 return STATUS_SUCCESS
;
115 Entry
= RangeList
->ListHead
.Flink
;
116 while (Entry
!= &RangeList
->ListHead
)
118 Current
= CONTAINING_RECORD (Entry
, RTL_RANGE_ENTRY
, Entry
);
119 if (Current
->Range
.Start
> RangeEntry
->Range
.End
)
121 /* Insert before current */
122 DPRINT ("Insert before current\n");
123 InsertTailList (&Current
->Entry
,
128 return STATUS_SUCCESS
;
132 Entry
= Entry
->Flink
;
135 DPRINT ("Insert tail\n");
136 InsertTailList (&RangeList
->ListHead
,
140 return STATUS_SUCCESS
;
143 ExFreePool (RangeEntry
);
145 return STATUS_UNSUCCESSFUL
;
149 /**********************************************************************
157 * CopyRangeList Pointer to the destination range list.
158 * RangeList Pointer to the source range list.
166 RtlCopyRangeList (OUT PRTL_RANGE_LIST CopyRangeList
,
167 IN PRTL_RANGE_LIST RangeList
)
169 PRTL_RANGE_ENTRY Current
;
170 PRTL_RANGE_ENTRY NewEntry
;
173 CopyRangeList
->Flags
= RangeList
->Flags
;
175 Entry
= RangeList
->ListHead
.Flink
;
176 while (Entry
!= &RangeList
->ListHead
)
178 Current
= CONTAINING_RECORD (Entry
, RTL_RANGE_ENTRY
, Entry
);
180 NewEntry
= ExAllocatePool (PagedPool
,
181 sizeof(RTL_RANGE_ENTRY
));
182 if (NewEntry
== NULL
)
183 return STATUS_INSUFFICIENT_RESOURCES
;
185 RtlCopyMemory (&NewEntry
->Range
,
187 sizeof(RTL_RANGE_ENTRY
));
189 InsertTailList (&CopyRangeList
->ListHead
,
192 CopyRangeList
->Count
++;
194 Entry
= Entry
->Flink
;
197 CopyRangeList
->Stamp
++;
199 return STATUS_SUCCESS
;
203 /**********************************************************************
205 * RtlDeleteOwnersRanges
208 * Delete all ranges that belong to the given owner.
211 * RangeList Pointer to the range list.
212 * Owner User supplied value that identifies the owner
213 * of the ranges to be deleted.
221 RtlDeleteOwnersRanges (IN OUT PRTL_RANGE_LIST RangeList
,
224 PRTL_RANGE_ENTRY Current
;
227 Entry
= RangeList
->ListHead
.Flink
;
228 while (Entry
!= &RangeList
->ListHead
)
230 Current
= CONTAINING_RECORD (Entry
, RTL_RANGE_ENTRY
, Entry
);
231 if (Current
->Range
.Owner
== Owner
)
233 RemoveEntryList (Entry
);
234 ExFreePool (Current
);
240 Entry
= Entry
->Flink
;
243 return STATUS_SUCCESS
;
247 /**********************************************************************
252 * Deletes a given range.
255 * RangeList Pointer to the range list.
256 * Start Start of the range to be deleted.
257 * End End of the range to be deleted.
258 * Owner Owner of the ranges to be deleted.
266 RtlDeleteRange (IN OUT PRTL_RANGE_LIST RangeList
,
271 PRTL_RANGE_ENTRY Current
;
274 Entry
= RangeList
->ListHead
.Flink
;
275 while (Entry
!= &RangeList
->ListHead
)
277 Current
= CONTAINING_RECORD (Entry
, RTL_RANGE_ENTRY
, Entry
);
278 if (Current
->Range
.Start
== Start
&&
279 Current
->Range
.End
== End
&&
280 Current
->Range
.Owner
== Owner
)
282 RemoveEntryList (Entry
);
284 ExFreePool (Current
);
288 return STATUS_SUCCESS
;
291 Entry
= Entry
->Flink
;
294 return STATUS_RANGE_NOT_FOUND
;
298 /**********************************************************************
303 * Searches for an unused range.
306 * RangeList Pointer to the range list.
312 * AttributeAvailableMask
321 * Support shared ranges and callback.
326 RtlFindRange (IN PRTL_RANGE_LIST RangeList
,
327 IN ULONGLONG Minimum
,
328 IN ULONGLONG Maximum
,
332 IN UCHAR AttributeAvailableMask
,
333 IN PVOID Context OPTIONAL
,
334 IN PRTL_CONFLICT_RANGE_CALLBACK Callback OPTIONAL
,
335 OUT PULONGLONG Start
)
337 PRTL_RANGE_ENTRY CurrentEntry
;
338 PRTL_RANGE_ENTRY NextEntry
;
343 if (Alignment
== 0 || Length
== 0)
345 return STATUS_INVALID_PARAMETER
;
348 if (IsListEmpty(&RangeList
->ListHead
))
350 *Start
= ROUND_DOWN (Maximum
- (Length
- 1), Alignment
);
351 return STATUS_SUCCESS
;
355 Entry
= RangeList
->ListHead
.Blink
;
356 while (Entry
!= &RangeList
->ListHead
)
358 CurrentEntry
= CONTAINING_RECORD (Entry
, RTL_RANGE_ENTRY
, Entry
);
360 RangeMax
= NextEntry
? (NextEntry
->Range
.Start
- 1) : Maximum
;
361 if (RangeMax
+ (Length
- 1) < Minimum
)
363 return STATUS_RANGE_NOT_FOUND
;
366 RangeMin
= ROUND_DOWN (RangeMax
- (Length
- 1), Alignment
);
367 if (RangeMin
< Minimum
||
368 (RangeMax
- RangeMin
) < (Length
- 1))
370 return STATUS_RANGE_NOT_FOUND
;
373 DPRINT("RangeMax: %I64x\n", RangeMax
);
374 DPRINT("RangeMin: %I64x\n", RangeMin
);
376 if (RangeMin
> CurrentEntry
->Range
.End
)
379 return STATUS_SUCCESS
;
382 NextEntry
= CurrentEntry
;
383 Entry
= Entry
->Blink
;
386 RangeMax
= NextEntry
? (NextEntry
->Range
.Start
- 1) : Maximum
;
387 if (RangeMax
+ (Length
- 1) < Minimum
)
389 return STATUS_RANGE_NOT_FOUND
;
392 RangeMin
= ROUND_DOWN (RangeMax
- (Length
- 1), Alignment
);
393 if (RangeMin
< Minimum
||
394 (RangeMax
- RangeMin
) < (Length
- 1))
396 return STATUS_RANGE_NOT_FOUND
;
399 DPRINT("RangeMax: %I64x\n", RangeMax
);
400 DPRINT("RangeMin: %I64x\n", RangeMin
);
404 return STATUS_SUCCESS
;
408 /**********************************************************************
413 * Deletes all ranges in a range list.
416 * RangeList Pointer to the range list.
424 RtlFreeRangeList (IN PRTL_RANGE_LIST RangeList
)
427 PRTL_RANGE_ENTRY Current
;
429 while (!IsListEmpty(&RangeList
->ListHead
))
431 Entry
= RemoveHeadList (&RangeList
->ListHead
);
432 Current
= CONTAINING_RECORD (Entry
, RTL_RANGE_ENTRY
, Entry
);
434 DPRINT ("Range start: %I64u\n", Current
->Range
.Start
);
435 DPRINT ("Range end: %I64u\n", Current
->Range
.End
);
437 ExFreePool (Current
);
440 RangeList
->Flags
= 0;
441 RangeList
->Count
= 0;
445 /**********************************************************************
450 * Retrieves the first range of a range list.
453 * RangeList Pointer to the range list.
454 * Iterator Pointer to a user supplied list state buffer.
455 * Range Pointer to the first range.
463 RtlGetFirstRange (IN PRTL_RANGE_LIST RangeList
,
464 OUT PRTL_RANGE_LIST_ITERATOR Iterator
,
465 OUT PRTL_RANGE
*Range
)
467 Iterator
->RangeListHead
= &RangeList
->ListHead
;
468 Iterator
->MergedHead
= NULL
;
469 Iterator
->Stamp
= RangeList
->Stamp
;
470 if (IsListEmpty(&RangeList
->ListHead
))
472 Iterator
->Current
= NULL
;
474 return STATUS_NO_MORE_ENTRIES
;
477 Iterator
->Current
= RangeList
->ListHead
.Flink
;
478 *Range
= &((PRTL_RANGE_ENTRY
)Iterator
->Current
)->Range
;
480 return STATUS_SUCCESS
;
484 /**********************************************************************
489 * Retrieves the next (or previous) range of a range list.
492 * Iterator Pointer to a user supplied list state buffer.
493 * Range Pointer to the first range.
494 * MoveForwards TRUE, get next range
495 * FALSE, get previous range
503 RtlGetNextRange (IN OUT PRTL_RANGE_LIST_ITERATOR Iterator
,
504 OUT PRTL_RANGE
*Range
,
505 IN BOOLEAN MoveForwards
)
507 PRTL_RANGE_LIST RangeList
;
510 RangeList
= CONTAINING_RECORD(Iterator
->RangeListHead
, RTL_RANGE_LIST
, ListHead
);
511 if (Iterator
->Stamp
!= RangeList
->Stamp
)
512 return STATUS_INVALID_PARAMETER
;
516 Next
= ((PRTL_RANGE_ENTRY
)Iterator
->Current
)->Entry
.Flink
;
520 Next
= ((PRTL_RANGE_ENTRY
)Iterator
->Current
)->Entry
.Blink
;
523 if (Next
== Iterator
->RangeListHead
)
524 return STATUS_NO_MORE_ENTRIES
;
526 Iterator
->Current
= Next
;
527 *Range
= &((PRTL_RANGE_ENTRY
)Next
)->Range
;
529 return STATUS_SUCCESS
;
533 /**********************************************************************
535 * RtlInitializeRangeList
538 * Initializes a range list.
541 * RangeList Pointer to a user supplied range list.
549 RtlInitializeRangeList (IN OUT PRTL_RANGE_LIST RangeList
)
551 InitializeListHead (&RangeList
->ListHead
);
552 RangeList
->Flags
= 0;
553 RangeList
->Count
= 0;
554 RangeList
->Stamp
= 0;
558 /**********************************************************************
563 * Inverts a range list.
566 * InvertedRangeList Inverted range list.
567 * RangeList Range list.
575 RtlInvertRangeList (OUT PRTL_RANGE_LIST InvertedRangeList
,
576 IN PRTL_RANGE_LIST RangeList
)
578 PRTL_RANGE_ENTRY Previous
;
579 PRTL_RANGE_ENTRY Current
;
583 /* Don't invert an empty range list */
584 if (IsListEmpty(&RangeList
->ListHead
))
586 return STATUS_SUCCESS
;
589 /* Add leading and intermediate ranges */
591 Entry
= RangeList
->ListHead
.Flink
;
592 while (Entry
!= &RangeList
->ListHead
)
594 Current
= CONTAINING_RECORD (Entry
, RTL_RANGE_ENTRY
, Entry
);
596 if (Previous
== NULL
)
598 if (Current
->Range
.Start
!= (ULONGLONG
)0)
600 Status
= RtlAddRange (InvertedRangeList
,
602 Current
->Range
.Start
- 1,
607 if (!NT_SUCCESS(Status
))
613 if (Previous
->Range
.End
+ 1 != Current
->Range
.Start
)
615 Status
= RtlAddRange (InvertedRangeList
,
616 Previous
->Range
.End
+ 1,
617 Current
->Range
.Start
- 1,
622 if (!NT_SUCCESS(Status
))
628 Entry
= Entry
->Flink
;
631 /* Add trailing range */
632 if (Previous
->Range
.End
+ 1 != (ULONGLONG
)-1)
634 Status
= RtlAddRange (InvertedRangeList
,
635 Previous
->Range
.End
+ 1,
641 if (!NT_SUCCESS(Status
))
645 return STATUS_SUCCESS
;
649 /**********************************************************************
651 * RtlIsRangeAvailable
654 * Checks whether a range is available or not.
657 * RangeList Pointer to the range list.
661 * AttributeAvailableMask
670 * - honor Flags and AttributeAvailableMask.
675 RtlIsRangeAvailable (IN PRTL_RANGE_LIST RangeList
,
679 IN UCHAR AttributeAvailableMask
,
680 IN PVOID Context OPTIONAL
,
681 IN PRTL_CONFLICT_RANGE_CALLBACK Callback OPTIONAL
,
682 OUT PBOOLEAN Available
)
684 PRTL_RANGE_ENTRY Current
;
689 Entry
= RangeList
->ListHead
.Flink
;
690 while (Entry
!= &RangeList
->ListHead
)
692 Current
= CONTAINING_RECORD (Entry
, RTL_RANGE_ENTRY
, Entry
);
693 if (!((Current
->Range
.Start
> End
&& Current
->Range
.End
> End
) ||
694 (Current
->Range
.Start
< Start
&& Current
->Range
.End
< Start
)))
696 if (Callback
!= NULL
)
698 *Available
= Callback (Context
,
707 Entry
= Entry
->Flink
;
710 return STATUS_SUCCESS
;
714 /**********************************************************************
719 * Merges two range lists.
722 * MergedRangeList Resulting range list.
723 * RangeList1 First range list.
724 * RangeList2 Second range list
733 RtlMergeRangeLists (OUT PRTL_RANGE_LIST MergedRangeList
,
734 IN PRTL_RANGE_LIST RangeList1
,
735 IN PRTL_RANGE_LIST RangeList2
,
738 RTL_RANGE_LIST_ITERATOR Iterator
;
742 /* Copy range list 1 to the merged range list */
743 Status
= RtlCopyRangeList (MergedRangeList
,
745 if (!NT_SUCCESS(Status
))
748 /* Add range list 2 entries to the merged range list */
749 Status
= RtlGetFirstRange (RangeList2
,
752 if (!NT_SUCCESS(Status
))
753 return (Status
== STATUS_NO_MORE_ENTRIES
) ? STATUS_SUCCESS
: Status
;
757 Status
= RtlAddRange (MergedRangeList
,
761 Range
->Flags
| Flags
,
764 if (!NT_SUCCESS(Status
))
767 Status
= RtlGetNextRange (&Iterator
,
770 if (!NT_SUCCESS(Status
))
774 return (Status
== STATUS_NO_MORE_ENTRIES
) ? STATUS_SUCCESS
: Status
;