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.1 2004/05/24 12:05:54 ekohl 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 ****************************************************************/
29 #include <ddk/ntddk.h>
32 #include <internal/debug.h>
35 #define ROUND_DOWN(N, S) ((N) - ((N) % (S)))
37 typedef struct _RTL_RANGE_ENTRY
41 } RTL_RANGE_ENTRY
, *PRTL_RANGE_ENTRY
;
44 /* FUNCTIONS ***************************************************************/
46 /**********************************************************************
51 * Adds a range to a range list.
54 * RangeList Range list.
66 * - Support shared ranges.
71 RtlAddRange (IN OUT PRTL_RANGE_LIST RangeList
,
76 IN PVOID UserData OPTIONAL
,
77 IN PVOID Owner OPTIONAL
)
79 PRTL_RANGE_ENTRY RangeEntry
;
80 PRTL_RANGE_ENTRY Previous
;
81 PRTL_RANGE_ENTRY Current
;
85 return STATUS_INVALID_PARAMETER
;
87 /* Create new range entry */
88 RangeEntry
= ExAllocatePool (PagedPool
,
89 sizeof(RTL_RANGE_ENTRY
));
90 if (RangeEntry
== NULL
)
91 return STATUS_INSUFFICIENT_RESOURCES
;
93 /* Initialize range entry */
94 RangeEntry
->Range
.Start
= Start
;
95 RangeEntry
->Range
.End
= End
;
96 RangeEntry
->Range
.Attributes
= Attributes
;
97 RangeEntry
->Range
.UserData
= UserData
;
98 RangeEntry
->Range
.Owner
= Owner
;
100 RangeEntry
->Range
.Flags
= 0;
101 if (Flags
& RTL_RANGE_LIST_ADD_SHARED
)
102 RangeEntry
->Range
.Flags
|= RTL_RANGE_SHARED
;
104 /* Insert range entry */
105 if (RangeList
->Count
== 0)
107 InsertTailList (&RangeList
->ListHead
,
111 return STATUS_SUCCESS
;
116 Entry
= RangeList
->ListHead
.Flink
;
117 while (Entry
!= &RangeList
->ListHead
)
119 Current
= CONTAINING_RECORD (Entry
, RTL_RANGE_ENTRY
, Entry
);
120 if (Current
->Range
.Start
> RangeEntry
->Range
.End
)
122 /* Insert before current */
123 DPRINT ("Insert before current\n");
124 InsertTailList (&Current
->Entry
,
129 return STATUS_SUCCESS
;
133 Entry
= Entry
->Flink
;
136 DPRINT ("Insert tail\n");
137 InsertTailList (&RangeList
->ListHead
,
141 return STATUS_SUCCESS
;
144 ExFreePool (RangeEntry
);
146 return STATUS_UNSUCCESSFUL
;
150 /**********************************************************************
158 * CopyRangeList Pointer to the destination range list.
159 * RangeList Pointer to the source range list.
167 RtlCopyRangeList (OUT PRTL_RANGE_LIST CopyRangeList
,
168 IN PRTL_RANGE_LIST RangeList
)
170 PRTL_RANGE_ENTRY Current
;
171 PRTL_RANGE_ENTRY NewEntry
;
174 CopyRangeList
->Flags
= RangeList
->Flags
;
176 Entry
= RangeList
->ListHead
.Flink
;
177 while (Entry
!= &RangeList
->ListHead
)
179 Current
= CONTAINING_RECORD (Entry
, RTL_RANGE_ENTRY
, Entry
);
181 NewEntry
= ExAllocatePool (PagedPool
,
182 sizeof(RTL_RANGE_ENTRY
));
183 if (NewEntry
== NULL
)
184 return STATUS_INSUFFICIENT_RESOURCES
;
186 RtlCopyMemory (&NewEntry
->Range
,
188 sizeof(RTL_RANGE_ENTRY
));
190 InsertTailList (&CopyRangeList
->ListHead
,
193 CopyRangeList
->Count
++;
195 Entry
= Entry
->Flink
;
198 CopyRangeList
->Stamp
++;
200 return STATUS_SUCCESS
;
204 /**********************************************************************
206 * RtlDeleteOwnersRanges
209 * Delete all ranges that belong to the given owner.
212 * RangeList Pointer to the range list.
213 * Owner User supplied value that identifies the owner
214 * of the ranges to be deleted.
222 RtlDeleteOwnersRanges (IN OUT PRTL_RANGE_LIST RangeList
,
225 PRTL_RANGE_ENTRY Current
;
228 Entry
= RangeList
->ListHead
.Flink
;
229 while (Entry
!= &RangeList
->ListHead
)
231 Current
= CONTAINING_RECORD (Entry
, RTL_RANGE_ENTRY
, Entry
);
232 if (Current
->Range
.Owner
== Owner
)
234 RemoveEntryList (Entry
);
235 ExFreePool (Current
);
241 Entry
= Entry
->Flink
;
244 return STATUS_SUCCESS
;
248 /**********************************************************************
253 * Deletes a given range.
256 * RangeList Pointer to the range list.
257 * Start Start of the range to be deleted.
258 * End End of the range to be deleted.
259 * Owner Owner of the ranges to be deleted.
267 RtlDeleteRange (IN OUT PRTL_RANGE_LIST RangeList
,
272 PRTL_RANGE_ENTRY Current
;
275 Entry
= RangeList
->ListHead
.Flink
;
276 while (Entry
!= &RangeList
->ListHead
)
278 Current
= CONTAINING_RECORD (Entry
, RTL_RANGE_ENTRY
, Entry
);
279 if (Current
->Range
.Start
== Start
&&
280 Current
->Range
.End
== End
&&
281 Current
->Range
.Owner
== Owner
)
283 RemoveEntryList (Entry
);
285 ExFreePool (Current
);
289 return STATUS_SUCCESS
;
292 Entry
= Entry
->Flink
;
295 return STATUS_RANGE_NOT_FOUND
;
299 /**********************************************************************
304 * Searches for an unused range.
307 * RangeList Pointer to the range list.
313 * AttributeAvailableMask
322 * Support shared ranges and callback.
327 RtlFindRange (IN PRTL_RANGE_LIST RangeList
,
328 IN ULONGLONG Minimum
,
329 IN ULONGLONG Maximum
,
333 IN UCHAR AttributeAvailableMask
,
334 IN PVOID Context OPTIONAL
,
335 IN PRTL_CONFLICT_RANGE_CALLBACK Callback OPTIONAL
,
336 OUT PULONGLONG Start
)
338 PRTL_RANGE_ENTRY CurrentEntry
;
339 PRTL_RANGE_ENTRY NextEntry
;
344 if (Alignment
== 0 || Length
== 0)
346 return STATUS_INVALID_PARAMETER
;
349 if (IsListEmpty(&RangeList
->ListHead
))
351 *Start
= ROUND_DOWN (Maximum
- (Length
- 1), Alignment
);
352 return STATUS_SUCCESS
;
356 Entry
= RangeList
->ListHead
.Blink
;
357 while (Entry
!= &RangeList
->ListHead
)
359 CurrentEntry
= CONTAINING_RECORD (Entry
, RTL_RANGE_ENTRY
, Entry
);
361 RangeMax
= NextEntry
? (NextEntry
->Range
.Start
- 1) : Maximum
;
362 if (RangeMax
+ (Length
- 1) < Minimum
)
364 return STATUS_RANGE_NOT_FOUND
;
367 RangeMin
= ROUND_DOWN (RangeMax
- (Length
- 1), Alignment
);
368 if (RangeMin
< Minimum
||
369 (RangeMax
- RangeMin
) < (Length
- 1))
371 return STATUS_RANGE_NOT_FOUND
;
374 DPRINT("RangeMax: %I64x\n", RangeMax
);
375 DPRINT("RangeMin: %I64x\n", RangeMin
);
377 if (RangeMin
> CurrentEntry
->Range
.End
)
380 return STATUS_SUCCESS
;
383 NextEntry
= CurrentEntry
;
384 Entry
= Entry
->Blink
;
387 RangeMax
= NextEntry
? (NextEntry
->Range
.Start
- 1) : Maximum
;
388 if (RangeMax
+ (Length
- 1) < Minimum
)
390 return STATUS_RANGE_NOT_FOUND
;
393 RangeMin
= ROUND_DOWN (RangeMax
- (Length
- 1), Alignment
);
394 if (RangeMin
< Minimum
||
395 (RangeMax
- RangeMin
) < (Length
- 1))
397 return STATUS_RANGE_NOT_FOUND
;
400 DPRINT("RangeMax: %I64x\n", RangeMax
);
401 DPRINT("RangeMin: %I64x\n", RangeMin
);
405 return STATUS_SUCCESS
;
409 /**********************************************************************
414 * Deletes all ranges in a range list.
417 * RangeList Pointer to the range list.
425 RtlFreeRangeList (IN PRTL_RANGE_LIST RangeList
)
428 PRTL_RANGE_ENTRY Current
;
430 while (!IsListEmpty(&RangeList
->ListHead
))
432 Entry
= RemoveHeadList (&RangeList
->ListHead
);
433 Current
= CONTAINING_RECORD (Entry
, RTL_RANGE_ENTRY
, Entry
);
435 DPRINT ("Range start: %I64u\n", Current
->Range
.Start
);
436 DPRINT ("Range end: %I64u\n", Current
->Range
.End
);
438 ExFreePool (Current
);
441 RangeList
->Flags
= 0;
442 RangeList
->Count
= 0;
446 /**********************************************************************
451 * Retrieves the first range of a range list.
454 * RangeList Pointer to the range list.
455 * Iterator Pointer to a user supplied list state buffer.
456 * Range Pointer to the first range.
464 RtlGetFirstRange (IN PRTL_RANGE_LIST RangeList
,
465 OUT PRTL_RANGE_LIST_ITERATOR Iterator
,
466 OUT PRTL_RANGE
*Range
)
468 Iterator
->RangeListHead
= &RangeList
->ListHead
;
469 Iterator
->MergedHead
= NULL
;
470 Iterator
->Stamp
= RangeList
->Stamp
;
471 if (IsListEmpty(&RangeList
->ListHead
))
473 Iterator
->Current
= NULL
;
475 return STATUS_NO_MORE_ENTRIES
;
478 Iterator
->Current
= RangeList
->ListHead
.Flink
;
479 *Range
= &((PRTL_RANGE_ENTRY
)Iterator
->Current
)->Range
;
481 return STATUS_SUCCESS
;
485 /**********************************************************************
490 * Retrieves the next (or previous) range of a range list.
493 * Iterator Pointer to a user supplied list state buffer.
494 * Range Pointer to the first range.
495 * MoveForwards TRUE, get next range
496 * FALSE, get previous range
504 RtlGetNextRange (IN OUT PRTL_RANGE_LIST_ITERATOR Iterator
,
505 OUT PRTL_RANGE
*Range
,
506 IN BOOLEAN MoveForwards
)
508 PRTL_RANGE_LIST RangeList
;
511 RangeList
= CONTAINING_RECORD(Iterator
->RangeListHead
, RTL_RANGE_LIST
, ListHead
);
512 if (Iterator
->Stamp
!= RangeList
->Stamp
)
513 return STATUS_INVALID_PARAMETER
;
517 Next
= ((PRTL_RANGE_ENTRY
)Iterator
->Current
)->Entry
.Flink
;
521 Next
= ((PRTL_RANGE_ENTRY
)Iterator
->Current
)->Entry
.Blink
;
524 if (Next
== Iterator
->RangeListHead
)
525 return STATUS_NO_MORE_ENTRIES
;
527 Iterator
->Current
= Next
;
528 *Range
= &((PRTL_RANGE_ENTRY
)Next
)->Range
;
530 return STATUS_SUCCESS
;
534 /**********************************************************************
536 * RtlInitializeRangeList
539 * Initializes a range list.
542 * RangeList Pointer to a user supplied range list.
550 RtlInitializeRangeList (IN OUT PRTL_RANGE_LIST RangeList
)
552 InitializeListHead (&RangeList
->ListHead
);
553 RangeList
->Flags
= 0;
554 RangeList
->Count
= 0;
555 RangeList
->Stamp
= 0;
559 /**********************************************************************
564 * Inverts a range list.
567 * InvertedRangeList Inverted range list.
568 * RangeList Range list.
576 RtlInvertRangeList (OUT PRTL_RANGE_LIST InvertedRangeList
,
577 IN PRTL_RANGE_LIST RangeList
)
579 PRTL_RANGE_ENTRY Previous
;
580 PRTL_RANGE_ENTRY Current
;
584 /* Don't invert an empty range list */
585 if (IsListEmpty(&RangeList
->ListHead
))
587 return STATUS_SUCCESS
;
590 /* Add leading and intermediate ranges */
592 Entry
= RangeList
->ListHead
.Flink
;
593 while (Entry
!= &RangeList
->ListHead
)
595 Current
= CONTAINING_RECORD (Entry
, RTL_RANGE_ENTRY
, Entry
);
597 if (Previous
== NULL
)
599 if (Current
->Range
.Start
!= (ULONGLONG
)0)
601 Status
= RtlAddRange (InvertedRangeList
,
603 Current
->Range
.Start
- 1,
608 if (!NT_SUCCESS(Status
))
614 if (Previous
->Range
.End
+ 1 != Current
->Range
.Start
)
616 Status
= RtlAddRange (InvertedRangeList
,
617 Previous
->Range
.End
+ 1,
618 Current
->Range
.Start
- 1,
623 if (!NT_SUCCESS(Status
))
629 Entry
= Entry
->Flink
;
632 /* Add trailing range */
633 if (Previous
->Range
.End
+ 1 != (ULONGLONG
)-1)
635 Status
= RtlAddRange (InvertedRangeList
,
636 Previous
->Range
.End
+ 1,
642 if (!NT_SUCCESS(Status
))
646 return STATUS_SUCCESS
;
650 /**********************************************************************
652 * RtlIsRangeAvailable
655 * Checks whether a range is available or not.
658 * RangeList Pointer to the range list.
662 * AttributeAvailableMask
671 * - honor Flags and AttributeAvailableMask.
676 RtlIsRangeAvailable (IN PRTL_RANGE_LIST RangeList
,
680 IN UCHAR AttributeAvailableMask
,
681 IN PVOID Context OPTIONAL
,
682 IN PRTL_CONFLICT_RANGE_CALLBACK Callback OPTIONAL
,
683 OUT PBOOLEAN Available
)
685 PRTL_RANGE_ENTRY Current
;
690 Entry
= RangeList
->ListHead
.Flink
;
691 while (Entry
!= &RangeList
->ListHead
)
693 Current
= CONTAINING_RECORD (Entry
, RTL_RANGE_ENTRY
, Entry
);
694 if (!((Current
->Range
.Start
> End
&& Current
->Range
.End
> End
) ||
695 (Current
->Range
.Start
< Start
&& Current
->Range
.End
< Start
)))
697 if (Callback
!= NULL
)
699 *Available
= Callback (Context
,
708 Entry
= Entry
->Flink
;
711 return STATUS_SUCCESS
;
715 /**********************************************************************
720 * Merges two range lists.
723 * MergedRangeList Resulting range list.
724 * RangeList1 First range list.
725 * RangeList2 Second range list
734 RtlMergeRangeLists (OUT PRTL_RANGE_LIST MergedRangeList
,
735 IN PRTL_RANGE_LIST RangeList1
,
736 IN PRTL_RANGE_LIST RangeList2
,
739 RTL_RANGE_LIST_ITERATOR Iterator
;
743 /* Copy range list 1 to the merged range list */
744 Status
= RtlCopyRangeList (MergedRangeList
,
746 if (!NT_SUCCESS(Status
))
749 /* Add range list 2 entries to the merged range list */
750 Status
= RtlGetFirstRange (RangeList2
,
753 if (!NT_SUCCESS(Status
))
754 return (Status
== STATUS_NO_MORE_ENTRIES
) ? STATUS_SUCCESS
: Status
;
758 Status
= RtlAddRange (MergedRangeList
,
762 Range
->Flags
| Flags
,
765 if (!NT_SUCCESS(Status
))
768 Status
= RtlGetNextRange (&Iterator
,
771 if (!NT_SUCCESS(Status
))
775 return (Status
== STATUS_NO_MORE_ENTRIES
) ? STATUS_SUCCESS
: Status
;