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.
21 * COPYRIGHT: See COPYING in the top level directory
22 * PROJECT: ReactOS system libraries
23 * PURPOSE: Range list implementation
24 * FILE: lib/ntdll/rtl/rangelist.c
27 /* INCLUDES ****************************************************************/
29 #include <ddk/ntddk.h>
32 #include <ntdll/ntdll.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
= RtlAllocateHeap (RtlGetProcessHeap(),
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 RtlFreeHeap (RtlGetProcessHeap(),
148 return STATUS_UNSUCCESSFUL
;
152 /**********************************************************************
160 * CopyRangeList Pointer to the destination range list.
161 * RangeList Pointer to the source range list.
169 RtlCopyRangeList (OUT PRTL_RANGE_LIST CopyRangeList
,
170 IN PRTL_RANGE_LIST RangeList
)
172 PRTL_RANGE_ENTRY Current
;
173 PRTL_RANGE_ENTRY NewEntry
;
176 CopyRangeList
->Flags
= RangeList
->Flags
;
178 Entry
= RangeList
->ListHead
.Flink
;
179 while (Entry
!= &RangeList
->ListHead
)
181 Current
= CONTAINING_RECORD (Entry
, RTL_RANGE_ENTRY
, Entry
);
183 NewEntry
= RtlAllocateHeap (RtlGetProcessHeap(),
185 sizeof(RTL_RANGE_ENTRY
));
186 if (NewEntry
== NULL
)
187 return STATUS_INSUFFICIENT_RESOURCES
;
189 RtlCopyMemory (&NewEntry
->Range
,
191 sizeof(RTL_RANGE_ENTRY
));
193 InsertTailList (&CopyRangeList
->ListHead
,
196 CopyRangeList
->Count
++;
198 Entry
= Entry
->Flink
;
201 CopyRangeList
->Stamp
++;
203 return STATUS_SUCCESS
;
207 /**********************************************************************
209 * RtlDeleteOwnersRanges
212 * Delete all ranges that belong to the given owner.
215 * RangeList Pointer to the range list.
216 * Owner User supplied value that identifies the owner
217 * of the ranges to be deleted.
225 RtlDeleteOwnersRanges (IN OUT PRTL_RANGE_LIST RangeList
,
228 PRTL_RANGE_ENTRY Current
;
231 Entry
= RangeList
->ListHead
.Flink
;
232 while (Entry
!= &RangeList
->ListHead
)
234 Current
= CONTAINING_RECORD (Entry
, RTL_RANGE_ENTRY
, Entry
);
235 if (Current
->Range
.Owner
== Owner
)
237 RemoveEntryList (Entry
);
238 RtlFreeHeap (RtlGetProcessHeap(),
246 Entry
= Entry
->Flink
;
249 return STATUS_SUCCESS
;
253 /**********************************************************************
258 * Deletes a given range.
261 * RangeList Pointer to the range list.
262 * Start Start of the range to be deleted.
263 * End End of the range to be deleted.
264 * Owner Owner of the ranges to be deleted.
272 RtlDeleteRange (IN OUT PRTL_RANGE_LIST RangeList
,
277 PRTL_RANGE_ENTRY Current
;
280 Entry
= RangeList
->ListHead
.Flink
;
281 while (Entry
!= &RangeList
->ListHead
)
283 Current
= CONTAINING_RECORD (Entry
, RTL_RANGE_ENTRY
, Entry
);
284 if (Current
->Range
.Start
== Start
&&
285 Current
->Range
.End
== End
&&
286 Current
->Range
.Owner
== Owner
)
288 RemoveEntryList (Entry
);
290 RtlFreeHeap (RtlGetProcessHeap(),
296 return STATUS_SUCCESS
;
299 Entry
= Entry
->Flink
;
302 return STATUS_RANGE_NOT_FOUND
;
306 /**********************************************************************
311 * Searches for an unused range.
314 * RangeList Pointer to the range list.
320 * AttributeAvailableMask
329 * Support shared ranges and callback.
334 RtlFindRange (IN PRTL_RANGE_LIST RangeList
,
335 IN ULONGLONG Minimum
,
336 IN ULONGLONG Maximum
,
340 IN UCHAR AttributeAvailableMask
,
341 IN PVOID Context OPTIONAL
,
342 IN PRTL_CONFLICT_RANGE_CALLBACK Callback OPTIONAL
,
343 OUT PULONGLONG Start
)
345 PRTL_RANGE_ENTRY CurrentEntry
;
346 PRTL_RANGE_ENTRY NextEntry
;
351 if (Alignment
== 0 || Length
== 0)
353 return STATUS_INVALID_PARAMETER
;
356 if (IsListEmpty(&RangeList
->ListHead
))
358 *Start
= ROUND_DOWN (Maximum
- (Length
- 1), Alignment
);
359 return STATUS_SUCCESS
;
363 Entry
= RangeList
->ListHead
.Blink
;
364 while (Entry
!= &RangeList
->ListHead
)
366 CurrentEntry
= CONTAINING_RECORD (Entry
, RTL_RANGE_ENTRY
, Entry
);
368 RangeMax
= NextEntry
? (NextEntry
->Range
.Start
- 1) : Maximum
;
369 if (RangeMax
+ (Length
- 1) < Minimum
)
371 return STATUS_RANGE_NOT_FOUND
;
374 RangeMin
= ROUND_DOWN (RangeMax
- (Length
- 1), Alignment
);
375 if (RangeMin
< Minimum
||
376 (RangeMax
- RangeMin
) < (Length
- 1))
378 return STATUS_RANGE_NOT_FOUND
;
381 DPRINT("RangeMax: %I64x\n", RangeMax
);
382 DPRINT("RangeMin: %I64x\n", RangeMin
);
384 if (RangeMin
> CurrentEntry
->Range
.End
)
387 return STATUS_SUCCESS
;
390 NextEntry
= CurrentEntry
;
391 Entry
= Entry
->Blink
;
394 RangeMax
= NextEntry
? (NextEntry
->Range
.Start
- 1) : Maximum
;
395 if (RangeMax
+ (Length
- 1) < Minimum
)
397 return STATUS_RANGE_NOT_FOUND
;
400 RangeMin
= ROUND_DOWN (RangeMax
- (Length
- 1), Alignment
);
401 if (RangeMin
< Minimum
||
402 (RangeMax
- RangeMin
) < (Length
- 1))
404 return STATUS_RANGE_NOT_FOUND
;
407 DPRINT("RangeMax: %I64x\n", RangeMax
);
408 DPRINT("RangeMin: %I64x\n", RangeMin
);
412 return STATUS_SUCCESS
;
416 /**********************************************************************
421 * Deletes all ranges in a range list.
424 * RangeList Pointer to the range list.
432 RtlFreeRangeList (IN PRTL_RANGE_LIST RangeList
)
435 PRTL_RANGE_ENTRY Current
;
437 while (!IsListEmpty(&RangeList
->ListHead
))
439 Entry
= RemoveHeadList (&RangeList
->ListHead
);
440 Current
= CONTAINING_RECORD (Entry
, RTL_RANGE_ENTRY
, Entry
);
442 DPRINT ("Range start: %I64u\n", Current
->Range
.Start
);
443 DPRINT ("Range end: %I64u\n", Current
->Range
.End
);
445 RtlFreeHeap (RtlGetProcessHeap(),
450 RangeList
->Flags
= 0;
451 RangeList
->Count
= 0;
455 /**********************************************************************
460 * Retrieves the first range of a range list.
463 * RangeList Pointer to the range list.
464 * Iterator Pointer to a user supplied list state buffer.
465 * Range Pointer to the first range.
473 RtlGetFirstRange (IN PRTL_RANGE_LIST RangeList
,
474 OUT PRTL_RANGE_LIST_ITERATOR Iterator
,
475 OUT PRTL_RANGE
*Range
)
477 Iterator
->RangeListHead
= &RangeList
->ListHead
;
478 Iterator
->MergedHead
= NULL
;
479 Iterator
->Stamp
= RangeList
->Stamp
;
480 if (IsListEmpty(&RangeList
->ListHead
))
482 Iterator
->Current
= NULL
;
484 return STATUS_NO_MORE_ENTRIES
;
487 Iterator
->Current
= RangeList
->ListHead
.Flink
;
488 *Range
= &((PRTL_RANGE_ENTRY
)Iterator
->Current
)->Range
;
490 return STATUS_SUCCESS
;
494 /**********************************************************************
499 * Retrieves the next (or previous) range of a range list.
502 * Iterator Pointer to a user supplied list state buffer.
503 * Range Pointer to the first range.
504 * MoveForwards TRUE, get next range
505 * FALSE, get previous range
513 RtlGetNextRange (IN OUT PRTL_RANGE_LIST_ITERATOR Iterator
,
514 OUT PRTL_RANGE
*Range
,
515 IN BOOLEAN MoveForwards
)
517 PRTL_RANGE_LIST RangeList
;
520 RangeList
= CONTAINING_RECORD(Iterator
->RangeListHead
, RTL_RANGE_LIST
, ListHead
);
521 if (Iterator
->Stamp
!= RangeList
->Stamp
)
522 return STATUS_INVALID_PARAMETER
;
526 Next
= ((PRTL_RANGE_ENTRY
)Iterator
->Current
)->Entry
.Flink
;
530 Next
= ((PRTL_RANGE_ENTRY
)Iterator
->Current
)->Entry
.Blink
;
533 if (Next
== Iterator
->RangeListHead
)
534 return STATUS_NO_MORE_ENTRIES
;
536 Iterator
->Current
= Next
;
537 *Range
= &((PRTL_RANGE_ENTRY
)Next
)->Range
;
539 return STATUS_SUCCESS
;
543 /**********************************************************************
545 * RtlInitializeRangeList
548 * Initializes a range list.
551 * RangeList Pointer to a user supplied range list.
559 RtlInitializeRangeList (IN OUT PRTL_RANGE_LIST RangeList
)
561 InitializeListHead (&RangeList
->ListHead
);
562 RangeList
->Flags
= 0;
563 RangeList
->Count
= 0;
564 RangeList
->Stamp
= 0;
568 /**********************************************************************
573 * Inverts a range list.
576 * InvertedRangeList Inverted range list.
577 * RangeList Range list.
585 RtlInvertRangeList (OUT PRTL_RANGE_LIST InvertedRangeList
,
586 IN PRTL_RANGE_LIST RangeList
)
588 PRTL_RANGE_ENTRY Previous
;
589 PRTL_RANGE_ENTRY Current
;
593 /* Don't invert an empty range list */
594 if (IsListEmpty(&RangeList
->ListHead
))
596 return STATUS_SUCCESS
;
599 /* Add leading and intermediate ranges */
601 Entry
= RangeList
->ListHead
.Flink
;
602 while (Entry
!= &RangeList
->ListHead
)
604 Current
= CONTAINING_RECORD (Entry
, RTL_RANGE_ENTRY
, Entry
);
606 if (Previous
== NULL
)
608 if (Current
->Range
.Start
!= (ULONGLONG
)0)
610 Status
= RtlAddRange (InvertedRangeList
,
612 Current
->Range
.Start
- 1,
617 if (!NT_SUCCESS(Status
))
623 if (Previous
->Range
.End
+ 1 != Current
->Range
.Start
)
625 Status
= RtlAddRange (InvertedRangeList
,
626 Previous
->Range
.End
+ 1,
627 Current
->Range
.Start
- 1,
632 if (!NT_SUCCESS(Status
))
638 Entry
= Entry
->Flink
;
641 /* Add trailing range */
642 if (Previous
->Range
.End
+ 1 != (ULONGLONG
)-1)
644 Status
= RtlAddRange (InvertedRangeList
,
645 Previous
->Range
.End
+ 1,
651 if (!NT_SUCCESS(Status
))
655 return STATUS_SUCCESS
;
659 /**********************************************************************
661 * RtlIsRangeAvailable
664 * Checks whether a range is available or not.
667 * RangeList Pointer to the range list.
671 * AttributeAvailableMask
680 * - honor Flags and AttributeAvailableMask.
685 RtlIsRangeAvailable (IN PRTL_RANGE_LIST RangeList
,
689 IN UCHAR AttributeAvailableMask
,
690 IN PVOID Context OPTIONAL
,
691 IN PRTL_CONFLICT_RANGE_CALLBACK Callback OPTIONAL
,
692 OUT PBOOLEAN Available
)
694 PRTL_RANGE_ENTRY Current
;
699 Entry
= RangeList
->ListHead
.Flink
;
700 while (Entry
!= &RangeList
->ListHead
)
702 Current
= CONTAINING_RECORD (Entry
, RTL_RANGE_ENTRY
, Entry
);
703 if (!((Current
->Range
.Start
> End
&& Current
->Range
.End
> End
) ||
704 (Current
->Range
.Start
< Start
&& Current
->Range
.End
< Start
)))
706 if (Callback
!= NULL
)
708 *Available
= Callback (Context
,
717 Entry
= Entry
->Flink
;
720 return STATUS_SUCCESS
;
724 /**********************************************************************
729 * Merges two range lists.
732 * MergedRangeList Resulting range list.
733 * RangeList1 First range list.
734 * RangeList2 Second range list
743 RtlMergeRangeLists (OUT PRTL_RANGE_LIST MergedRangeList
,
744 IN PRTL_RANGE_LIST RangeList1
,
745 IN PRTL_RANGE_LIST RangeList2
,
748 RTL_RANGE_LIST_ITERATOR Iterator
;
752 /* Copy range list 1 to the merged range list */
753 Status
= RtlCopyRangeList (MergedRangeList
,
755 if (!NT_SUCCESS(Status
))
758 /* Add range list 2 entries to the merged range list */
759 Status
= RtlGetFirstRange (RangeList2
,
762 if (!NT_SUCCESS(Status
))
763 return (Status
== STATUS_NO_MORE_ENTRIES
) ? STATUS_SUCCESS
: Status
;
767 Status
= RtlAddRange (MergedRangeList
,
771 Range
->Flags
| Flags
,
774 if (!NT_SUCCESS(Status
))
777 Status
= RtlGetNextRange (&Iterator
,
780 if (!NT_SUCCESS(Status
))
784 return (Status
== STATUS_NO_MORE_ENTRIES
) ? STATUS_SUCCESS
: Status
;