2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS system libraries
4 * FILE: lib/rtl/rangelist.c
5 * PURPOSE: Range list implementation
6 * PROGRAMMERS: No programmer listed.
9 /* INCLUDES *****************************************************************/
16 /* TYPES ********************************************************************/
18 typedef struct _RTL_RANGE_ENTRY
22 } RTL_RANGE_ENTRY
, *PRTL_RANGE_ENTRY
;
24 /* FUNCTIONS ***************************************************************/
26 /**********************************************************************
31 * Adds a range to a range list.
34 * RangeList Range list.
46 * - Support shared ranges.
52 RtlAddRange(IN OUT PRTL_RANGE_LIST RangeList
,
57 IN PVOID UserData OPTIONAL
,
58 IN PVOID Owner OPTIONAL
)
60 PRTL_RANGE_ENTRY RangeEntry
;
61 //PRTL_RANGE_ENTRY Previous;
62 PRTL_RANGE_ENTRY Current
;
66 return STATUS_INVALID_PARAMETER
;
68 /* Create new range entry */
69 RangeEntry
= RtlpAllocateMemory(sizeof(RTL_RANGE_ENTRY
), 'elRR');
70 if (RangeEntry
== NULL
)
71 return STATUS_INSUFFICIENT_RESOURCES
;
73 /* Initialize range entry */
74 RangeEntry
->Range
.Start
= Start
;
75 RangeEntry
->Range
.End
= End
;
76 RangeEntry
->Range
.Attributes
= Attributes
;
77 RangeEntry
->Range
.UserData
= UserData
;
78 RangeEntry
->Range
.Owner
= Owner
;
80 RangeEntry
->Range
.Flags
= 0;
81 if (Flags
& RTL_RANGE_LIST_ADD_SHARED
)
82 RangeEntry
->Range
.Flags
|= RTL_RANGE_SHARED
;
84 /* Insert range entry */
85 if (RangeList
->Count
== 0)
87 InsertTailList(&RangeList
->ListHead
,
91 return STATUS_SUCCESS
;
96 Entry
= RangeList
->ListHead
.Flink
;
97 while (Entry
!= &RangeList
->ListHead
)
99 Current
= CONTAINING_RECORD(Entry
, RTL_RANGE_ENTRY
, Entry
);
100 if (Current
->Range
.Start
> RangeEntry
->Range
.End
)
102 /* Insert before current */
103 DPRINT("Insert before current\n");
104 InsertTailList(&Current
->Entry
,
109 return STATUS_SUCCESS
;
112 //Previous = Current;
113 Entry
= Entry
->Flink
;
116 DPRINT("Insert tail\n");
117 InsertTailList(&RangeList
->ListHead
,
121 return STATUS_SUCCESS
;
124 RtlpFreeMemory(RangeEntry
, 0);
126 return STATUS_UNSUCCESSFUL
;
130 /**********************************************************************
138 * CopyRangeList Pointer to the destination range list.
139 * RangeList Pointer to the source range list.
148 RtlCopyRangeList(OUT PRTL_RANGE_LIST CopyRangeList
,
149 IN PRTL_RANGE_LIST RangeList
)
151 PRTL_RANGE_ENTRY Current
;
152 PRTL_RANGE_ENTRY NewEntry
;
155 CopyRangeList
->Flags
= RangeList
->Flags
;
157 Entry
= RangeList
->ListHead
.Flink
;
158 while (Entry
!= &RangeList
->ListHead
)
160 Current
= CONTAINING_RECORD(Entry
, RTL_RANGE_ENTRY
, Entry
);
162 NewEntry
= RtlpAllocateMemory(sizeof(RTL_RANGE_ENTRY
), 'elRR');
163 if (NewEntry
== NULL
)
164 return STATUS_INSUFFICIENT_RESOURCES
;
166 RtlCopyMemory(&NewEntry
->Range
,
170 InsertTailList(&CopyRangeList
->ListHead
,
173 CopyRangeList
->Count
++;
175 Entry
= Entry
->Flink
;
178 CopyRangeList
->Stamp
++;
180 return STATUS_SUCCESS
;
184 /**********************************************************************
186 * RtlDeleteOwnersRanges
189 * Delete all ranges that belong to the given owner.
192 * RangeList Pointer to the range list.
193 * Owner User supplied value that identifies the owner
194 * of the ranges to be deleted.
203 RtlDeleteOwnersRanges(IN OUT PRTL_RANGE_LIST RangeList
,
206 PRTL_RANGE_ENTRY Current
;
209 Entry
= RangeList
->ListHead
.Flink
;
210 while (Entry
!= &RangeList
->ListHead
)
212 Current
= CONTAINING_RECORD(Entry
, RTL_RANGE_ENTRY
, Entry
);
213 if (Current
->Range
.Owner
== Owner
)
215 RemoveEntryList (Entry
);
216 RtlpFreeMemory(Current
, 0);
222 Entry
= Entry
->Flink
;
225 return STATUS_SUCCESS
;
229 /**********************************************************************
234 * Deletes a given range.
237 * RangeList Pointer to the range list.
238 * Start Start of the range to be deleted.
239 * End End of the range to be deleted.
240 * Owner Owner of the ranges to be deleted.
249 RtlDeleteRange(IN OUT PRTL_RANGE_LIST RangeList
,
254 PRTL_RANGE_ENTRY Current
;
257 Entry
= RangeList
->ListHead
.Flink
;
258 while (Entry
!= &RangeList
->ListHead
)
260 Current
= CONTAINING_RECORD(Entry
, RTL_RANGE_ENTRY
, Entry
);
261 if (Current
->Range
.Start
== Start
&&
262 Current
->Range
.End
== End
&&
263 Current
->Range
.Owner
== Owner
)
265 RemoveEntryList(Entry
);
267 RtlpFreeMemory(Current
, 0);
271 return STATUS_SUCCESS
;
274 Entry
= Entry
->Flink
;
277 return STATUS_RANGE_NOT_FOUND
;
281 /**********************************************************************
286 * Searches for an unused range.
289 * RangeList Pointer to the range list.
295 * AttributeAvailableMask
304 * Support shared ranges and callback.
310 RtlFindRange(IN PRTL_RANGE_LIST RangeList
,
311 IN ULONGLONG Minimum
,
312 IN ULONGLONG Maximum
,
316 IN UCHAR AttributeAvailableMask
,
317 IN PVOID Context OPTIONAL
,
318 IN PRTL_CONFLICT_RANGE_CALLBACK Callback OPTIONAL
,
319 OUT PULONGLONG Start
)
321 PRTL_RANGE_ENTRY CurrentEntry
;
322 PRTL_RANGE_ENTRY NextEntry
;
327 if (Alignment
== 0 || Length
== 0)
329 return STATUS_INVALID_PARAMETER
;
332 if (IsListEmpty(&RangeList
->ListHead
))
334 *Start
= ROUND_DOWN(Maximum
- (Length
- 1), Alignment
);
335 return STATUS_SUCCESS
;
339 Entry
= RangeList
->ListHead
.Blink
;
340 while (Entry
!= &RangeList
->ListHead
)
342 CurrentEntry
= CONTAINING_RECORD(Entry
, RTL_RANGE_ENTRY
, Entry
);
344 RangeMax
= NextEntry
? (NextEntry
->Range
.Start
- 1) : Maximum
;
345 if (RangeMax
+ (Length
- 1) < Minimum
)
347 return STATUS_RANGE_NOT_FOUND
;
350 RangeMin
= ROUND_DOWN(RangeMax
- (Length
- 1), Alignment
);
351 if (RangeMin
< Minimum
||
352 (RangeMax
- RangeMin
) < (Length
- 1))
354 return STATUS_RANGE_NOT_FOUND
;
357 DPRINT("RangeMax: %I64x\n", RangeMax
);
358 DPRINT("RangeMin: %I64x\n", RangeMin
);
360 if (RangeMin
> CurrentEntry
->Range
.End
)
363 return STATUS_SUCCESS
;
366 NextEntry
= CurrentEntry
;
367 Entry
= Entry
->Blink
;
370 RangeMax
= NextEntry
? (NextEntry
->Range
.Start
- 1) : Maximum
;
371 if (RangeMax
+ (Length
- 1) < Minimum
)
373 return STATUS_RANGE_NOT_FOUND
;
376 RangeMin
= ROUND_DOWN(RangeMax
- (Length
- 1), Alignment
);
377 if (RangeMin
< Minimum
||
378 (RangeMax
- RangeMin
) < (Length
- 1))
380 return STATUS_RANGE_NOT_FOUND
;
383 DPRINT("RangeMax: %I64x\n", RangeMax
);
384 DPRINT("RangeMin: %I64x\n", RangeMin
);
388 return STATUS_SUCCESS
;
392 /**********************************************************************
397 * Deletes all ranges in a range list.
400 * RangeList Pointer to the range list.
409 RtlFreeRangeList(IN PRTL_RANGE_LIST RangeList
)
412 PRTL_RANGE_ENTRY Current
;
414 while (!IsListEmpty(&RangeList
->ListHead
))
416 Entry
= RemoveHeadList(&RangeList
->ListHead
);
417 Current
= CONTAINING_RECORD(Entry
, RTL_RANGE_ENTRY
, Entry
);
419 DPRINT ("Range start: %I64u\n", Current
->Range
.Start
);
420 DPRINT ("Range end: %I64u\n", Current
->Range
.End
);
422 RtlpFreeMemory(Current
, 0);
425 RangeList
->Flags
= 0;
426 RangeList
->Count
= 0;
430 /**********************************************************************
435 * Retrieves the first range of a range list.
438 * RangeList Pointer to the range list.
439 * Iterator Pointer to a user supplied list state buffer.
440 * Range Pointer to the first range.
449 RtlGetFirstRange(IN PRTL_RANGE_LIST RangeList
,
450 OUT PRTL_RANGE_LIST_ITERATOR Iterator
,
451 OUT PRTL_RANGE
*Range
)
453 Iterator
->RangeListHead
= &RangeList
->ListHead
;
454 Iterator
->MergedHead
= NULL
;
455 Iterator
->Stamp
= RangeList
->Stamp
;
457 if (IsListEmpty(&RangeList
->ListHead
))
459 Iterator
->Current
= NULL
;
461 return STATUS_NO_MORE_ENTRIES
;
464 Iterator
->Current
= RangeList
->ListHead
.Flink
;
465 *Range
= &((PRTL_RANGE_ENTRY
)Iterator
->Current
)->Range
;
467 return STATUS_SUCCESS
;
471 /**********************************************************************
476 * Retrieves the next (or previous) range of a range list.
479 * Iterator Pointer to a user supplied list state buffer.
480 * Range Pointer to the first range.
481 * MoveForwards TRUE, get next range
482 * FALSE, get previous range
491 RtlGetNextRange(IN OUT PRTL_RANGE_LIST_ITERATOR Iterator
,
492 OUT PRTL_RANGE
*Range
,
493 IN BOOLEAN MoveForwards
)
495 PRTL_RANGE_LIST RangeList
;
498 RangeList
= CONTAINING_RECORD(Iterator
->RangeListHead
, RTL_RANGE_LIST
, ListHead
);
499 if (Iterator
->Stamp
!= RangeList
->Stamp
)
500 return STATUS_INVALID_PARAMETER
;
504 Next
= ((PRTL_RANGE_ENTRY
)Iterator
->Current
)->Entry
.Flink
;
508 Next
= ((PRTL_RANGE_ENTRY
)Iterator
->Current
)->Entry
.Blink
;
511 if (Next
== Iterator
->RangeListHead
)
512 return STATUS_NO_MORE_ENTRIES
;
514 Iterator
->Current
= Next
;
515 *Range
= &((PRTL_RANGE_ENTRY
)Next
)->Range
;
517 return STATUS_SUCCESS
;
521 /**********************************************************************
523 * RtlInitializeRangeList
526 * Initializes a range list.
529 * RangeList Pointer to a user supplied range list.
538 RtlInitializeRangeList(IN OUT PRTL_RANGE_LIST RangeList
)
540 InitializeListHead(&RangeList
->ListHead
);
541 RangeList
->Flags
= 0;
542 RangeList
->Count
= 0;
543 RangeList
->Stamp
= 0;
547 /**********************************************************************
552 * Inverts a range list.
555 * InvertedRangeList Inverted range list.
556 * RangeList Range list.
565 RtlInvertRangeList(OUT PRTL_RANGE_LIST InvertedRangeList
,
566 IN PRTL_RANGE_LIST RangeList
)
568 PRTL_RANGE_ENTRY Previous
;
569 PRTL_RANGE_ENTRY Current
;
573 /* Add leading and intermediate ranges */
575 Entry
= RangeList
->ListHead
.Flink
;
576 while (Entry
!= &RangeList
->ListHead
)
578 Current
= CONTAINING_RECORD(Entry
, RTL_RANGE_ENTRY
, Entry
);
580 if (Previous
== NULL
)
582 if (Current
->Range
.Start
!= (ULONGLONG
)0)
584 Status
= RtlAddRange(InvertedRangeList
,
586 Current
->Range
.Start
- 1,
591 if (!NT_SUCCESS(Status
))
597 if (Previous
->Range
.End
+ 1 != Current
->Range
.Start
)
599 Status
= RtlAddRange(InvertedRangeList
,
600 Previous
->Range
.End
+ 1,
601 Current
->Range
.Start
- 1,
606 if (!NT_SUCCESS(Status
))
612 Entry
= Entry
->Flink
;
615 /* Check if the list was empty */
616 if (Previous
== NULL
)
619 return STATUS_SUCCESS
;
622 /* Add trailing range */
623 if (Previous
->Range
.End
+ 1 != (ULONGLONG
)-1)
625 Status
= RtlAddRange(InvertedRangeList
,
626 Previous
->Range
.End
+ 1,
632 if (!NT_SUCCESS(Status
))
636 return STATUS_SUCCESS
;
640 /**********************************************************************
642 * RtlIsRangeAvailable
645 * Checks whether a range is available or not.
648 * RangeList Pointer to the range list.
652 * AttributeAvailableMask
661 * - honor Flags and AttributeAvailableMask.
667 RtlIsRangeAvailable(IN PRTL_RANGE_LIST RangeList
,
671 IN UCHAR AttributeAvailableMask
,
672 IN PVOID Context OPTIONAL
,
673 IN PRTL_CONFLICT_RANGE_CALLBACK Callback OPTIONAL
,
674 OUT PBOOLEAN Available
)
676 PRTL_RANGE_ENTRY Current
;
681 Entry
= RangeList
->ListHead
.Flink
;
682 while (Entry
!= &RangeList
->ListHead
)
684 Current
= CONTAINING_RECORD (Entry
, RTL_RANGE_ENTRY
, Entry
);
686 if (!((Current
->Range
.Start
>= End
&& Current
->Range
.End
> End
) ||
687 (Current
->Range
.Start
<= Start
&& Current
->Range
.End
< Start
&&
688 (!(Flags
& RTL_RANGE_SHARED
) ||
689 !(Current
->Range
.Flags
& RTL_RANGE_SHARED
)))))
691 if (Callback
!= NULL
)
693 *Available
= Callback(Context
,
702 Entry
= Entry
->Flink
;
705 return STATUS_SUCCESS
;
709 /**********************************************************************
714 * Merges two range lists.
717 * MergedRangeList Resulting range list.
718 * RangeList1 First range list.
719 * RangeList2 Second range list
729 RtlMergeRangeLists(OUT PRTL_RANGE_LIST MergedRangeList
,
730 IN PRTL_RANGE_LIST RangeList1
,
731 IN PRTL_RANGE_LIST RangeList2
,
734 RTL_RANGE_LIST_ITERATOR Iterator
;
738 /* Copy range list 1 to the merged range list */
739 Status
= RtlCopyRangeList(MergedRangeList
,
741 if (!NT_SUCCESS(Status
))
744 /* Add range list 2 entries to the merged range list */
745 Status
= RtlGetFirstRange(RangeList2
,
748 if (!NT_SUCCESS(Status
))
749 return (Status
== STATUS_NO_MORE_ENTRIES
) ? STATUS_SUCCESS
: Status
;
753 Status
= RtlAddRange(MergedRangeList
,
757 Range
->Flags
| Flags
,
760 if (!NT_SUCCESS(Status
))
763 Status
= RtlGetNextRange(&Iterator
,
766 if (!NT_SUCCESS(Status
))
770 return (Status
== STATUS_NO_MORE_ENTRIES
) ? STATUS_SUCCESS
: Status
;