xref: /reactos/sdk/lib/rtl/rangelist.c (revision 2fb2f1e4)
1 /*
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.
7  */
8 
9 /* INCLUDES *****************************************************************/
10 
11 #include <rtl.h>
12 
13 #define NDEBUG
14 #include <debug.h>
15 
16 /* TYPES ********************************************************************/
17 
18 typedef struct _RTL_RANGE_ENTRY
19 {
20     LIST_ENTRY Entry;
21     RTL_RANGE Range;
22 } RTL_RANGE_ENTRY, *PRTL_RANGE_ENTRY;
23 
24 /* FUNCTIONS ***************************************************************/
25 
26 /**********************************************************************
27  * NAME							EXPORTED
28  * 	RtlAddRange
29  *
30  * DESCRIPTION
31  *	Adds a range to a range list.
32  *
33  * ARGUMENTS
34  *	RangeList		Range list.
35  *	Start
36  *	End
37  *	Attributes
38  *	Flags
39  *	UserData
40  *	Owner
41  *
42  * RETURN VALUE
43  *	Status
44  *
45  * TODO:
46  *   - Support shared ranges.
47  *
48  * @implemented
49  */
50 NTSTATUS
51 NTAPI
RtlAddRange(IN OUT PRTL_RANGE_LIST RangeList,IN ULONGLONG Start,IN ULONGLONG End,IN UCHAR Attributes,IN ULONG Flags,IN PVOID UserData OPTIONAL,IN PVOID Owner OPTIONAL)52 RtlAddRange(IN OUT PRTL_RANGE_LIST RangeList,
53             IN ULONGLONG Start,
54             IN ULONGLONG End,
55             IN UCHAR Attributes,
56             IN ULONG Flags,
57             IN PVOID UserData OPTIONAL,
58             IN PVOID Owner OPTIONAL)
59 {
60     PRTL_RANGE_ENTRY RangeEntry;
61     //PRTL_RANGE_ENTRY Previous;
62     PRTL_RANGE_ENTRY Current;
63     PLIST_ENTRY Entry;
64 
65     if (Start > End)
66         return STATUS_INVALID_PARAMETER;
67 
68     /* Create new range entry */
69     RangeEntry = RtlpAllocateMemory(sizeof(RTL_RANGE_ENTRY), 'elRR');
70     if (RangeEntry == NULL)
71         return STATUS_INSUFFICIENT_RESOURCES;
72 
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;
79 
80     RangeEntry->Range.Flags = 0;
81     if (Flags & RTL_RANGE_LIST_ADD_SHARED)
82         RangeEntry->Range.Flags |= RTL_RANGE_SHARED;
83 
84     /* Insert range entry */
85     if (RangeList->Count == 0)
86     {
87         InsertTailList(&RangeList->ListHead,
88                        &RangeEntry->Entry);
89         RangeList->Count++;
90         RangeList->Stamp++;
91         return STATUS_SUCCESS;
92     }
93     else
94     {
95          //Previous = NULL;
96         Entry = RangeList->ListHead.Flink;
97         while (Entry != &RangeList->ListHead)
98         {
99             Current = CONTAINING_RECORD(Entry, RTL_RANGE_ENTRY, Entry);
100             if (Current->Range.Start > RangeEntry->Range.End)
101             {
102                 /* Insert before current */
103                 DPRINT("Insert before current\n");
104                 InsertTailList(&Current->Entry,
105                                &RangeEntry->Entry);
106 
107                 RangeList->Count++;
108                 RangeList->Stamp++;
109                 return STATUS_SUCCESS;
110             }
111 
112             //Previous = Current;
113             Entry = Entry->Flink;
114         }
115 
116         DPRINT("Insert tail\n");
117         InsertTailList(&RangeList->ListHead,
118                        &RangeEntry->Entry);
119         RangeList->Count++;
120         RangeList->Stamp++;
121         return STATUS_SUCCESS;
122     }
123 
124     RtlpFreeMemory(RangeEntry, 0);
125 
126     return STATUS_UNSUCCESSFUL;
127 }
128 
129 
130 /**********************************************************************
131  * NAME							EXPORTED
132  * 	RtlCopyRangeList
133  *
134  * DESCRIPTION
135  *	Copy a range list.
136  *
137  * ARGUMENTS
138  *	CopyRangeList	Pointer to the destination range list.
139  *	RangeList	Pointer to the source range list.
140  *
141  * RETURN VALUE
142  *	Status
143  *
144  * @implemented
145  */
146 NTSTATUS
147 NTAPI
RtlCopyRangeList(OUT PRTL_RANGE_LIST CopyRangeList,IN PRTL_RANGE_LIST RangeList)148 RtlCopyRangeList(OUT PRTL_RANGE_LIST CopyRangeList,
149                  IN PRTL_RANGE_LIST RangeList)
150 {
151     PRTL_RANGE_ENTRY Current;
152     PRTL_RANGE_ENTRY NewEntry;
153     PLIST_ENTRY Entry;
154 
155     CopyRangeList->Flags = RangeList->Flags;
156 
157     Entry = RangeList->ListHead.Flink;
158     while (Entry != &RangeList->ListHead)
159     {
160         Current = CONTAINING_RECORD(Entry, RTL_RANGE_ENTRY, Entry);
161 
162         NewEntry = RtlpAllocateMemory(sizeof(RTL_RANGE_ENTRY), 'elRR');
163         if (NewEntry == NULL)
164             return STATUS_INSUFFICIENT_RESOURCES;
165 
166         RtlCopyMemory(&NewEntry->Range,
167                       &Current->Range,
168                       sizeof(RTL_RANGE));
169 
170         InsertTailList(&CopyRangeList->ListHead,
171                        &NewEntry->Entry);
172 
173         CopyRangeList->Count++;
174 
175         Entry = Entry->Flink;
176     }
177 
178     CopyRangeList->Stamp++;
179 
180     return STATUS_SUCCESS;
181 }
182 
183 
184 /**********************************************************************
185  * NAME							EXPORTED
186  * 	RtlDeleteOwnersRanges
187  *
188  * DESCRIPTION
189  *	Delete all ranges that belong to the given owner.
190  *
191  * ARGUMENTS
192  *	RangeList	Pointer to the range list.
193  *	Owner		User supplied value that identifies the owner
194  *			of the ranges to be deleted.
195  *
196  * RETURN VALUE
197  *	Status
198  *
199  * @implemented
200  */
201 NTSTATUS
202 NTAPI
RtlDeleteOwnersRanges(IN OUT PRTL_RANGE_LIST RangeList,IN PVOID Owner)203 RtlDeleteOwnersRanges(IN OUT PRTL_RANGE_LIST RangeList,
204                       IN PVOID Owner)
205 {
206     PRTL_RANGE_ENTRY Current;
207     PLIST_ENTRY Entry;
208 
209     Entry = RangeList->ListHead.Flink;
210     while (Entry != &RangeList->ListHead)
211     {
212         Current = CONTAINING_RECORD(Entry, RTL_RANGE_ENTRY, Entry);
213         if (Current->Range.Owner == Owner)
214         {
215             RemoveEntryList (Entry);
216             RtlpFreeMemory(Current, 0);
217 
218             RangeList->Count--;
219             RangeList->Stamp++;
220         }
221 
222         Entry = Entry->Flink;
223     }
224 
225     return STATUS_SUCCESS;
226 }
227 
228 
229 /**********************************************************************
230  * NAME							EXPORTED
231  * 	RtlDeleteRange
232  *
233  * DESCRIPTION
234  *	Deletes a given range.
235  *
236  * ARGUMENTS
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.
241  *
242  * RETURN VALUE
243  *	Status
244  *
245  * @implemented
246  */
247 NTSTATUS
248 NTAPI
RtlDeleteRange(IN OUT PRTL_RANGE_LIST RangeList,IN ULONGLONG Start,IN ULONGLONG End,IN PVOID Owner)249 RtlDeleteRange(IN OUT PRTL_RANGE_LIST RangeList,
250                IN ULONGLONG Start,
251                IN ULONGLONG End,
252                IN PVOID Owner)
253 {
254     PRTL_RANGE_ENTRY Current;
255     PLIST_ENTRY Entry;
256 
257     Entry = RangeList->ListHead.Flink;
258     while (Entry != &RangeList->ListHead)
259     {
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)
264         {
265             RemoveEntryList(Entry);
266 
267             RtlpFreeMemory(Current, 0);
268 
269             RangeList->Count--;
270             RangeList->Stamp++;
271             return STATUS_SUCCESS;
272         }
273 
274         Entry = Entry->Flink;
275     }
276 
277     return STATUS_RANGE_NOT_FOUND;
278 }
279 
280 
281 /**********************************************************************
282  * NAME							EXPORTED
283  * 	RtlFindRange
284  *
285  * DESCRIPTION
286  *	Searches for an unused range.
287  *
288  * ARGUMENTS
289  *	RangeList		Pointer to the range list.
290  *	Minimum
291  *	Maximum
292  *	Length
293  *	Alignment
294  *	Flags
295  *	AttributeAvailableMask
296  *	Context
297  *	Callback
298  *	Start
299  *
300  * RETURN VALUE
301  *	Status
302  *
303  * TODO
304  *	Support shared ranges and callback.
305  *
306  * @implemented
307  */
308 NTSTATUS
309 NTAPI
RtlFindRange(IN PRTL_RANGE_LIST RangeList,IN ULONGLONG Minimum,IN ULONGLONG Maximum,IN ULONG Length,IN ULONG Alignment,IN ULONG Flags,IN UCHAR AttributeAvailableMask,IN PVOID Context OPTIONAL,IN PRTL_CONFLICT_RANGE_CALLBACK Callback OPTIONAL,OUT PULONGLONG Start)310 RtlFindRange(IN PRTL_RANGE_LIST RangeList,
311              IN ULONGLONG Minimum,
312              IN ULONGLONG Maximum,
313              IN ULONG Length,
314              IN ULONG Alignment,
315              IN ULONG Flags,
316              IN UCHAR AttributeAvailableMask,
317              IN PVOID Context OPTIONAL,
318              IN PRTL_CONFLICT_RANGE_CALLBACK Callback OPTIONAL,
319              OUT PULONGLONG Start)
320 {
321     PRTL_RANGE_ENTRY CurrentEntry;
322     PRTL_RANGE_ENTRY NextEntry;
323     PLIST_ENTRY Entry;
324     ULONGLONG RangeMin;
325     ULONGLONG RangeMax;
326 
327     if (Alignment == 0 || Length == 0)
328     {
329         return STATUS_INVALID_PARAMETER;
330     }
331 
332     if (IsListEmpty(&RangeList->ListHead))
333     {
334         *Start = ROUND_DOWN(Maximum - (Length - 1), Alignment);
335         return STATUS_SUCCESS;
336     }
337 
338     NextEntry = NULL;
339     Entry = RangeList->ListHead.Blink;
340     while (Entry != &RangeList->ListHead)
341     {
342         CurrentEntry = CONTAINING_RECORD(Entry, RTL_RANGE_ENTRY, Entry);
343 
344         RangeMax = NextEntry ? (NextEntry->Range.Start - 1) : Maximum;
345         if (RangeMax + (Length - 1) < Minimum)
346         {
347             return STATUS_RANGE_NOT_FOUND;
348         }
349 
350         RangeMin = ROUND_DOWN(RangeMax - (Length - 1), Alignment);
351         if (RangeMin < Minimum ||
352             (RangeMax - RangeMin) < (Length - 1))
353         {
354             return STATUS_RANGE_NOT_FOUND;
355         }
356 
357         DPRINT("RangeMax: %I64x\n", RangeMax);
358         DPRINT("RangeMin: %I64x\n", RangeMin);
359 
360         if (RangeMin > CurrentEntry->Range.End)
361         {
362             *Start = RangeMin;
363             return STATUS_SUCCESS;
364         }
365 
366         NextEntry = CurrentEntry;
367         Entry = Entry->Blink;
368     }
369 
370     RangeMax = NextEntry ? (NextEntry->Range.Start - 1) : Maximum;
371     if (RangeMax + (Length - 1) < Minimum)
372     {
373         return STATUS_RANGE_NOT_FOUND;
374     }
375 
376     RangeMin = ROUND_DOWN(RangeMax - (Length - 1), Alignment);
377     if (RangeMin < Minimum ||
378         (RangeMax - RangeMin) < (Length - 1))
379     {
380         return STATUS_RANGE_NOT_FOUND;
381     }
382 
383     DPRINT("RangeMax: %I64x\n", RangeMax);
384     DPRINT("RangeMin: %I64x\n", RangeMin);
385 
386     *Start = RangeMin;
387 
388     return STATUS_SUCCESS;
389 }
390 
391 
392 /**********************************************************************
393  * NAME							EXPORTED
394  * 	RtlFreeRangeList
395  *
396  * DESCRIPTION
397  *	Deletes all ranges in a range list.
398  *
399  * ARGUMENTS
400  *	RangeList	Pointer to the range list.
401  *
402  * RETURN VALUE
403  *	None
404  *
405  * @implemented
406  */
407 VOID
408 NTAPI
RtlFreeRangeList(IN PRTL_RANGE_LIST RangeList)409 RtlFreeRangeList(IN PRTL_RANGE_LIST RangeList)
410 {
411     PLIST_ENTRY Entry;
412     PRTL_RANGE_ENTRY Current;
413 
414     while (!IsListEmpty(&RangeList->ListHead))
415     {
416         Entry = RemoveHeadList(&RangeList->ListHead);
417         Current = CONTAINING_RECORD(Entry, RTL_RANGE_ENTRY, Entry);
418 
419         DPRINT ("Range start: %I64u\n", Current->Range.Start);
420         DPRINT ("Range end:   %I64u\n", Current->Range.End);
421 
422         RtlpFreeMemory(Current, 0);
423     }
424 
425     RangeList->Flags = 0;
426     RangeList->Count = 0;
427 }
428 
429 
430 /**********************************************************************
431  * NAME							EXPORTED
432  * 	RtlGetFirstRange
433  *
434  * DESCRIPTION
435  *	Retrieves the first range of a range list.
436  *
437  * ARGUMENTS
438  *	RangeList	Pointer to the range list.
439  *	Iterator	Pointer to a user supplied list state buffer.
440  *	Range		Pointer to the first range.
441  *
442  * RETURN VALUE
443  *	Status
444  *
445  * @implemented
446  */
447 NTSTATUS
448 NTAPI
RtlGetFirstRange(IN PRTL_RANGE_LIST RangeList,OUT PRTL_RANGE_LIST_ITERATOR Iterator,OUT PRTL_RANGE * Range)449 RtlGetFirstRange(IN PRTL_RANGE_LIST RangeList,
450                  OUT PRTL_RANGE_LIST_ITERATOR Iterator,
451                  OUT PRTL_RANGE *Range)
452 {
453     Iterator->RangeListHead = &RangeList->ListHead;
454     Iterator->MergedHead = NULL;
455     Iterator->Stamp = RangeList->Stamp;
456 
457     if (IsListEmpty(&RangeList->ListHead))
458     {
459         Iterator->Current = NULL;
460         *Range = NULL;
461         return STATUS_NO_MORE_ENTRIES;
462     }
463 
464     Iterator->Current = RangeList->ListHead.Flink;
465     *Range = &((PRTL_RANGE_ENTRY)Iterator->Current)->Range;
466 
467     return STATUS_SUCCESS;
468 }
469 
470 
471 /**********************************************************************
472  * NAME							EXPORTED
473  * 	RtlGetNextRange
474  *
475  * DESCRIPTION
476  *	Retrieves the next (or previous) range of a range list.
477  *
478  * ARGUMENTS
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
483  *
484  * RETURN VALUE
485  *	Status
486  *
487  * @implemented
488  */
489 NTSTATUS
490 NTAPI
RtlGetNextRange(IN OUT PRTL_RANGE_LIST_ITERATOR Iterator,OUT PRTL_RANGE * Range,IN BOOLEAN MoveForwards)491 RtlGetNextRange(IN OUT PRTL_RANGE_LIST_ITERATOR Iterator,
492                 OUT PRTL_RANGE *Range,
493                 IN BOOLEAN MoveForwards)
494 {
495     PRTL_RANGE_LIST RangeList;
496     PLIST_ENTRY Next;
497 
498     RangeList = CONTAINING_RECORD(Iterator->RangeListHead, RTL_RANGE_LIST, ListHead);
499     if (Iterator->Stamp != RangeList->Stamp)
500         return STATUS_INVALID_PARAMETER;
501 
502     if (Iterator->Current == NULL)
503     {
504         *Range = NULL;
505         return STATUS_NO_MORE_ENTRIES;
506     }
507 
508     if (MoveForwards)
509     {
510         Next = ((PRTL_RANGE_ENTRY)Iterator->Current)->Entry.Flink;
511     }
512     else
513     {
514         Next = ((PRTL_RANGE_ENTRY)Iterator->Current)->Entry.Blink;
515     }
516 
517     if (Next == Iterator->RangeListHead)
518     {
519         Iterator->Current = NULL;
520         *Range = NULL;
521         return STATUS_NO_MORE_ENTRIES;
522     }
523 
524     Iterator->Current = Next;
525     *Range = &((PRTL_RANGE_ENTRY)Next)->Range;
526 
527     return STATUS_SUCCESS;
528 }
529 
530 
531 /**********************************************************************
532  * NAME							EXPORTED
533  * 	RtlInitializeRangeList
534  *
535  * DESCRIPTION
536  *	Initializes a range list.
537  *
538  * ARGUMENTS
539  *	RangeList	Pointer to a user supplied range list.
540  *
541  * RETURN VALUE
542  *	None
543  *
544  * @implemented
545  */
546 VOID
547 NTAPI
RtlInitializeRangeList(IN OUT PRTL_RANGE_LIST RangeList)548 RtlInitializeRangeList(IN OUT PRTL_RANGE_LIST RangeList)
549 {
550     InitializeListHead(&RangeList->ListHead);
551     RangeList->Flags = 0;
552     RangeList->Count = 0;
553     RangeList->Stamp = 0;
554 }
555 
556 
557 /**********************************************************************
558  * NAME							EXPORTED
559  * 	RtlInvertRangeList
560  *
561  * DESCRIPTION
562  *	Inverts a range list.
563  *
564  * ARGUMENTS
565  *	InvertedRangeList	Inverted range list.
566  *	RangeList		Range list.
567  *
568  * RETURN VALUE
569  *	Status
570  *
571  * @implemented
572  */
573 NTSTATUS
574 NTAPI
RtlInvertRangeList(OUT PRTL_RANGE_LIST InvertedRangeList,IN PRTL_RANGE_LIST RangeList)575 RtlInvertRangeList(OUT PRTL_RANGE_LIST InvertedRangeList,
576                    IN PRTL_RANGE_LIST RangeList)
577 {
578     PRTL_RANGE_ENTRY Previous;
579     PRTL_RANGE_ENTRY Current;
580     PLIST_ENTRY Entry;
581     NTSTATUS Status;
582 
583     /* Add leading and intermediate ranges */
584     Previous = NULL;
585     Entry = RangeList->ListHead.Flink;
586     while (Entry != &RangeList->ListHead)
587     {
588         Current = CONTAINING_RECORD(Entry, RTL_RANGE_ENTRY, Entry);
589 
590         if (Previous == NULL)
591         {
592             if (Current->Range.Start != (ULONGLONG)0)
593             {
594                 Status = RtlAddRange(InvertedRangeList,
595                                      (ULONGLONG)0,
596                                      Current->Range.Start - 1,
597                                      0,
598                                      0,
599                                      NULL,
600                                      NULL);
601                 if (!NT_SUCCESS(Status))
602                     return Status;
603             }
604         }
605         else
606         {
607             if (Previous->Range.End + 1 != Current->Range.Start)
608             {
609                 Status = RtlAddRange(InvertedRangeList,
610                                      Previous->Range.End + 1,
611                                      Current->Range.Start - 1,
612                                      0,
613                                      0,
614                                      NULL,
615                                      NULL);
616                 if (!NT_SUCCESS(Status))
617                     return Status;
618             }
619         }
620 
621         Previous = Current;
622         Entry = Entry->Flink;
623     }
624 
625     /* Check if the list was empty */
626     if (Previous == NULL)
627     {
628         /* We're done */
629         return STATUS_SUCCESS;
630     }
631 
632     /* Add trailing range */
633     if (Previous->Range.End + 1 != (ULONGLONG)-1)
634     {
635         Status = RtlAddRange(InvertedRangeList,
636                              Previous->Range.End + 1,
637                              (ULONGLONG)-1,
638                              0,
639                              0,
640                              NULL,
641                              NULL);
642         if (!NT_SUCCESS(Status))
643             return Status;
644     }
645 
646     return STATUS_SUCCESS;
647 }
648 
649 
650 /**********************************************************************
651  * NAME							EXPORTED
652  * 	RtlIsRangeAvailable
653  *
654  * DESCRIPTION
655  *	Checks whether a range is available or not.
656  *
657  * ARGUMENTS
658  *	RangeList		Pointer to the range list.
659  *	Start
660  *	End
661  *	Flags
662  *	AttributeAvailableMask
663  *	Context
664  *	Callback
665  *	Available
666  *
667  * RETURN VALUE
668  *	Status
669  *
670  * TODO:
671  *   - honor Flags and AttributeAvailableMask.
672  *
673  * @implemented
674  */
675 NTSTATUS
676 NTAPI
RtlIsRangeAvailable(IN PRTL_RANGE_LIST RangeList,IN ULONGLONG Start,IN ULONGLONG End,IN ULONG Flags,IN UCHAR AttributeAvailableMask,IN PVOID Context OPTIONAL,IN PRTL_CONFLICT_RANGE_CALLBACK Callback OPTIONAL,OUT PBOOLEAN Available)677 RtlIsRangeAvailable(IN PRTL_RANGE_LIST RangeList,
678                     IN ULONGLONG Start,
679                     IN ULONGLONG End,
680                     IN ULONG Flags,
681                     IN UCHAR AttributeAvailableMask,
682                     IN PVOID Context OPTIONAL,
683                     IN PRTL_CONFLICT_RANGE_CALLBACK Callback OPTIONAL,
684                     OUT PBOOLEAN Available)
685 {
686     PRTL_RANGE_ENTRY Current;
687     PLIST_ENTRY Entry;
688 
689     *Available = TRUE;
690 
691     Entry = RangeList->ListHead.Flink;
692     while (Entry != &RangeList->ListHead)
693     {
694         Current = CONTAINING_RECORD (Entry, RTL_RANGE_ENTRY, Entry);
695 
696         if (!((Current->Range.Start >= End && Current->Range.End > End) ||
697               (Current->Range.Start <= Start && Current->Range.End < Start &&
698                (!(Flags & RTL_RANGE_SHARED) ||
699                 !(Current->Range.Flags & RTL_RANGE_SHARED)))))
700         {
701             if (Callback != NULL)
702             {
703                 *Available = Callback(Context,
704                                       &Current->Range);
705             }
706             else
707             {
708                 *Available = FALSE;
709             }
710         }
711 
712         Entry = Entry->Flink;
713     }
714 
715     return STATUS_SUCCESS;
716 }
717 
718 
719 /**********************************************************************
720  * NAME							EXPORTED
721  * 	RtlMergeRangeList
722  *
723  * DESCRIPTION
724  *	Merges two range lists.
725  *
726  * ARGUMENTS
727  *	MergedRangeList	Resulting range list.
728  *	RangeList1	First range list.
729  *	RangeList2	Second range list
730  *	Flags
731  *
732  * RETURN VALUE
733  *	Status
734  *
735  * @implemented
736  */
737 NTSTATUS
738 NTAPI
RtlMergeRangeLists(OUT PRTL_RANGE_LIST MergedRangeList,IN PRTL_RANGE_LIST RangeList1,IN PRTL_RANGE_LIST RangeList2,IN ULONG Flags)739 RtlMergeRangeLists(OUT PRTL_RANGE_LIST MergedRangeList,
740                    IN PRTL_RANGE_LIST RangeList1,
741                    IN PRTL_RANGE_LIST RangeList2,
742                    IN ULONG Flags)
743 {
744     RTL_RANGE_LIST_ITERATOR Iterator;
745     PRTL_RANGE Range;
746     NTSTATUS Status;
747 
748     /* Copy range list 1 to the merged range list */
749     Status = RtlCopyRangeList(MergedRangeList,
750                               RangeList1);
751     if (!NT_SUCCESS(Status))
752         return Status;
753 
754     /* Add range list 2 entries to the merged range list */
755     Status = RtlGetFirstRange(RangeList2,
756                               &Iterator,
757                               &Range);
758     if (!NT_SUCCESS(Status))
759         return (Status == STATUS_NO_MORE_ENTRIES) ? STATUS_SUCCESS : Status;
760 
761     while (TRUE)
762     {
763         Status = RtlAddRange(MergedRangeList,
764                              Range->Start,
765                              Range->End,
766                              Range->Attributes,
767                              Range->Flags | Flags,
768                              Range->UserData,
769                              Range->Owner);
770         if (!NT_SUCCESS(Status))
771             break;
772 
773         Status = RtlGetNextRange(&Iterator,
774                                  &Range,
775                                  TRUE);
776         if (!NT_SUCCESS(Status))
777             break;
778     }
779 
780     return (Status == STATUS_NO_MORE_ENTRIES) ? STATUS_SUCCESS : Status;
781 }
782 
783 /* EOF */
784