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