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