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