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 }