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