1 /**
2  * Generic resizeable array
3  *
4  * Compiler implementation of the
5  * $(LINK2 https://www.dlang.org, D programming language).
6  *
7  * Copyright:   Copyright (C) 2018-2023 by The D Language Foundation, All Rights Reserved
8  * Authors:     $(LINK2 https://www.digitalmars.com, Walter Bright)
9  * License:     $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
10  * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/backend/barrayf.d, backend/barray.d)
11  * Documentation: https://dlang.org/phobos/dmd_backend_barray.html
12  */
13 
14 module dmd.backend.barray;
15 
16 import core.stdc.stdio;
17 import core.stdc.stdlib;
18 import core.stdc.string;
19 
20 nothrow:
21 @safe:
22 
23 extern (C++):
24 
25 import dmd.backend.global : err_nomem;
26 
27 /*************************************
28  * A reusable array that ratchets up in capacity.
29  */
30 struct Barray(T)
31 {
32   @safe:
33 
34     /**********************
35      * Set useable length of array.
36      * Params:
37      *  length = minimum number of elements in array
38      */
39     @trusted
40     void setLength(size_t length)
41     {
42         static void enlarge(ref Barray barray, size_t length)
43         {
44             pragma(inline, false);
45             assert(length < size_t.max / (2 * T.sizeof));       // conservative overflow check
46             auto newcap = (barray.capacity == 0) ? length : length + (length >> 1);
47             barray.capacity = (newcap + 15) & ~15;
48             T* p = cast(T*)realloc(barray.array.ptr, barray.capacity * T.sizeof);
49             if (length && !p)
50             {
51                 version (unittest)
52                     assert(0);
53                 else
54                     err_nomem();
55             }
56             barray.array = p[0 .. length];
57         }
58 
59         if (length <= capacity)
60             array = array.ptr[0 .. length];     // the fast path
61         else
62             enlarge(this, length);              // the slow path
63     }
64 
65     /**********************
66      * Resets length of array to 0 without
67      * free'ing the array memory. This
68      * sets it up for re-using the memory.
69      */
70     void reset()
71     {
72         setLength(0);
73     }
74 
75     /*******************
76      * Append element t to array.
77      * Params:
78      *  t = element to append
79      */
80     void push(T t)
81     {
82         const i = length;
83         setLength(i + 1);
84         array[i] = t;
85     }
86 
87     /*******************
88      * Append a 0-initialized element of T to array
89      * Returns:
90      *  pointer to appended element
91      */
92     @trusted
93     T* push()
94     {
95         const i = length;
96         setLength(i + 1);
97         auto p = &array[i];
98         memset(p, 0, T.sizeof);
99         return p;
100     }
101 
102     /**********************
103      * Move the last element from the array into [i].
104      * Reduce the array length by one.
105      * Params:
106      *  i = index of element to remove
107      */
108     void remove(size_t i)
109     {
110         const len = length - 1;
111         if (i != len)
112         {
113             array[i] = array[len];
114         }
115         setLength(len);
116     }
117 
118     /******************
119      * Release all memory used.
120      */
121     @trusted
122     void dtor()
123     {
124         free(array.ptr);
125         array = null;
126         capacity = 0;
127     }
128 
129     alias array this;
130     T[] array;
131 
132   private:
133     size_t capacity;
134 }
135 
136 unittest
137 {
138     Barray!int a;
139     a.setLength(10);
140     assert(a.length == 10);
141     a.setLength(4);
142     assert(a.length == 4);
143     foreach (i, ref v; a[])
144         v = cast(int) i * 2;
145     foreach (i, ref const v; a[])
146         assert(v == i * 2);
147     a.remove(3);
148     assert(a.length == 3);
149     a.push(50);
150     a.remove(1);
151     assert(a[1] == 50);
152     a.dtor();
153     assert(a.length == 0);
154 }
155 
156 /**************************************
157  * While Barray is good for reusing a Barray's previous allocation,
158  * it doesn't work if an element of the Barray is itself a Barray.
159  * Rarray aims to fix that.
160  */
161 
162 struct Rarray(T)
163 {
164   @safe:
165 
166     /*******************
167      * Append an uninitialized element of T to array.
168      * This leaves allocations used by T intact.
169      * Returns:
170      *  pointer to appended element
171      */
172     T* push()
173     {
174         const i = length;
175         ++length;
176         if (i < barray.length)
177         {
178              return &barray.array[i];
179         }
180         return barray.push();
181     }
182 
183     ref inout(T) opIndex(size_t i) inout nothrow pure @nogc
184     {
185         assert(i < length);
186         return barray[i];
187     }
188 
189     extern (D) inout(T)[] opSlice() inout nothrow pure @nogc
190     {
191         return barray[0 .. length];
192     }
193 
194     extern (D) inout(T)[] opSlice(size_t a, size_t b) inout nothrow pure @nogc
195     {
196         assert(a <= b && b <= length);
197         return barray[a .. b];
198     }
199 
200     /**********************
201      * Resets length of array to 0 without
202      * free'ing the array memory. This
203      * sets it up for re-using the memory.
204      */
205     void reset()
206     {
207         length = 0;
208     }
209 
210     /******************
211      * Release all memory used.
212      */
213     void dtor()
214     {
215         reset();
216         barray.dtor();
217     }
218 
219     alias opDollar = length;
220 
221     /* barray[0 .. barray.capacity] allocated length
222      * barray[0 .. barray.length] initialized length
223      * barray[0 .. length] in use length
224      * 0 <= length <= barray.length <= barray.capacity
225      */
226     Barray!T barray;
227 
228     size_t length;      // <= barray.length
229 }