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