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