1 /**
2  * Region storage allocator implementation.
3  *
4  * Copyright:   Copyright (C) 2019-2021 by The D Language Foundation, All Rights Reserved
5  * Authors:     $(LINK2 http://www.digitalmars.com, Walter Bright)
6  * License:     $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
7  * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/root/region.d, root/_region.d)
8  * Documentation:  https://dlang.org/phobos/dmd_root_region.html
9  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/region.d
10  */
11 
12 module dmd.root.region;
13 
14 import core.stdc.stdio;
15 import core.stdc.string;
16 import core.stdc.stdlib;
17 
18 import dmd.root.rmem;
19 import dmd.root.array;
20 
21 /*****
22  * Simple region storage allocator.
23  */
24 struct Region
25 {
26   nothrow:
27   private:
28 
29     Array!(void*) array; // array of chunks
30     int used;            // number of chunks used in array[]
31     void[] available;    // slice of chunk that's available to allocate
32 
33     enum ChunkSize = 4096 * 1024;
34     enum MaxAllocSize = ChunkSize;
35 
36     struct RegionPos
37     {
38         int used;
39         void[] available;
40     }
41 
42 public:
43 
44     /******
45      * Allocate nbytes. Aborts on failure.
46      * Params:
47      *  nbytes = number of bytes to allocate, can be 0, must be <= than MaxAllocSize
48      * Returns:
49      *  allocated data, null for nbytes==0
50      */
51     void* malloc(size_t nbytes)
52     {
53         if (!nbytes)
54             return null;
55 
56         nbytes = (nbytes + 15) & ~15;
57         if (nbytes > available.length)
58         {
59             assert(nbytes <= MaxAllocSize);
60             if (used == array.length)
61             {
62                 auto h = Mem.check(.malloc(ChunkSize));
63                 array.push(h);
64             }
65 
66             available = array[used][0 .. MaxAllocSize];
67             ++used;
68         }
69 
70         auto p = available.ptr;
71         available = (p + nbytes)[0 .. available.length - nbytes];
72         return p;
73     }
74 
75     /****************************
76      * Return stack position for allocations in this region.
77      * Returns:
78      *  an opaque struct to be passed to `release()`
79      */
80     RegionPos savePos()
81     {
82         return RegionPos(used, available);
83     }
84 
85     /********************
86      * Release the memory that was allocated after the respective call to `savePos()`.
87      * Params:
88      *  pos = position returned by `savePos()`
89      */
90     void release(RegionPos pos)
91     {
92         version (all)
93         {
94             /* Recycle the memory. There better not be
95              * any live pointers to it.
96              */
97             used = pos.used;
98             available = pos.available;
99         }
100         else
101         {
102             /* Instead of recycling the memory, stomp on it
103              * to flush out any remaining live pointers to it.
104              */
105             (cast(ubyte[])pos.available)[] = 0xFF;
106             foreach (h; array[pos.used .. used])
107                 (cast(ubyte*)h)[0 .. ChunkSize] = 0xFF;
108         }
109     }
110 
111     /****************************
112      * If pointer points into Region.
113      * Params:
114      *  p = pointer to check
115      * Returns:
116      *  true if it points into the region
117      */
118     bool contains(void* p)
119     {
120         foreach (h; array[0 .. used])
121         {
122             if (h <= p && p < h + ChunkSize)
123                 return true;
124         }
125         return false;
126     }
127 
128     /*********************
129      * Returns: size of Region
130      */
131     size_t size()
132     {
133         return used * MaxAllocSize - available.length;
134     }
135 }
136 
137 
138 unittest
139 {
140     Region reg;
141     auto rgnpos = reg.savePos();
142 
143     void* p = reg.malloc(0);
144     assert(p == null);
145     assert(!reg.contains(p));
146 
147     p = reg.malloc(100);
148     assert(p !is null);
149     assert(reg.contains(p));
150     memset(p, 0, 100);
151 
152     p = reg.malloc(100);
153     assert(p !is null);
154     assert(reg.contains(p));
155     memset(p, 0, 100);
156 
157     assert(reg.size() > 0);
158     assert(!reg.contains(&reg));
159 
160     reg.release(rgnpos);
161 }