1 /*
2  * Copyright (C) 2020 Linux Studio Plugins Project <https://lsp-plug.in/>
3  *           (C) 2020 Vladimir Sadovnikov <sadko4u@gmail.com>
4  *
5  * This file is part of lsp-plugins
6  * Created on: 14 янв. 2019 г.
7  *
8  * lsp-plugins is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * any later version.
12  *
13  * lsp-plugins is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with lsp-plugins. If not, see <https://www.gnu.org/licenses/>.
20  */
21 
22 #include <dsp/bits.h>
23 #include <core/3d/Allocator3D.h>
24 #include <core/sugar.h>
25 #include <core/status.h>
26 
27 namespace lsp
28 {
BasicAllocator3D(size_t sz_of,size_t c_size)29     BasicAllocator3D::BasicAllocator3D(size_t sz_of, size_t c_size)
30     {
31         nChunks         = 0;
32         nShift          = int_log2(c_size);
33         nMask           = (1 << nShift) - 1;
34 
35         nSizeOf         = sz_of;
36         nAllocated      = 0;
37         vChunks         = NULL;
38         pCurr           = NULL;
39         nLeft           = 0;
40     }
41 
~BasicAllocator3D()42     BasicAllocator3D::~BasicAllocator3D()
43     {
44         do_destroy();
45     }
46 
get_chunk(size_t id)47     uint8_t *BasicAllocator3D::get_chunk(size_t id)
48     {
49         // Reallocate chunk index if too small
50         if (id >= nChunks)
51         {
52             size_t cap      = (id + 0x10) & (~0x0f);
53             uint8_t **nc    = reinterpret_cast<uint8_t **>(::realloc(vChunks, sizeof(uint8_t *) * cap));
54             if (nc == NULL)
55                 return NULL;
56             // Initialize pointers
57             for (size_t i=nChunks; i<cap; ++i)
58                 nc[nChunks++] = NULL;
59             vChunks         = nc;
60         }
61 
62         // Fetch chunks
63         uint8_t *chunk = vChunks[id];
64         if (chunk != NULL)
65             return chunk;
66 
67         // Try to allocate
68         chunk = reinterpret_cast<uint8_t *>(::malloc(nSizeOf << nShift));
69         if (chunk == NULL)
70             return NULL;
71 
72         vChunks[id]         = chunk;
73         return chunk;
74     }
75 
do_get(size_t idx)76     void *BasicAllocator3D::do_get(size_t idx)
77     {
78         if (idx >= nAllocated)
79             return NULL;
80         uint8_t *chunk      = vChunks[idx >> nShift];
81         return &chunk[nSizeOf * (idx & nMask)];
82     }
83 
do_alloc()84     void *BasicAllocator3D::do_alloc()
85     {
86         // Try to allocate from current chunk
87         if (nLeft <= 0)
88         {
89             pCurr           = get_chunk(nAllocated >> nShift);
90             if (pCurr == NULL)
91                 return NULL;
92             nLeft           = nMask; // (1 << nShift) - 1
93         }
94         else
95             --nLeft;
96 
97         uint8_t *p      = pCurr;
98         pCurr          += nSizeOf;
99         ++nAllocated;
100         return p;
101     }
102 
do_alloc_n(void ** ptr,size_t n)103     size_t BasicAllocator3D::do_alloc_n(void **ptr, size_t n)
104     {
105         size_t left = n;
106 
107         while (left > 0)
108         {
109             // Try to allocate from current chunk
110             if (nLeft <= 0)
111             {
112                 pCurr           = get_chunk(nAllocated >> nShift);
113                 if (pCurr == NULL)
114                     break;
115                 nLeft           = (1 << nShift);
116             }
117 
118             // Allocate N items
119             size_t to_alloc = (nLeft > n) ? n : nLeft;
120             nLeft      -= to_alloc;
121             nAllocated += to_alloc;
122 
123             uint8_t *p      = pCurr;
124             while (to_alloc--)
125             {
126                 *(ptr++)        = p;
127                 p              += nSizeOf;
128             }
129 
130             pCurr       = p;
131         }
132 
133         return n - left;
134     }
135 
do_ialloc(void ** p)136     ssize_t BasicAllocator3D::do_ialloc(void **p)
137     {
138         // Try to allocate from current chunk
139         if (nLeft <= 0)
140         {
141             pCurr           = get_chunk(nAllocated >> nShift);
142             if (pCurr == NULL)
143                 return -STATUS_NO_MEM;
144             nLeft           = nMask; // (1 << nShift) - 1
145         }
146         else
147             --nLeft;
148 
149         *p              = pCurr;
150         pCurr          += nSizeOf;
151         return nAllocated++;
152     }
153 
do_destroy()154     void BasicAllocator3D::do_destroy()
155     {
156         if (vChunks != NULL)
157         {
158             for (size_t i=0; i<nChunks; ++i)
159             {
160                 uint8_t *c = vChunks[i];
161                 if (c != NULL)
162                 {
163                     ::free(c);
164                     vChunks[i] = NULL;
165                 }
166             }
167 
168             ::free(vChunks);
169             vChunks = NULL;
170         }
171 
172         nAllocated      = 0;
173         nChunks         = 0;
174         pCurr           = NULL;
175         nLeft           = 0;
176     }
177 
do_clear()178     void BasicAllocator3D::do_clear()
179     {
180         nAllocated      = 0;
181         pCurr           = NULL;
182         nLeft           = 0;
183     }
184 
do_swap(BasicAllocator3D * src)185     void BasicAllocator3D::do_swap(BasicAllocator3D *src)
186     {
187         swap(nChunks, src->nChunks);
188         swap(nShift, src->nShift);
189         swap(nMask, src->nMask);
190         swap(nSizeOf, src->nSizeOf);
191         swap(nAllocated, src->nAllocated);
192         swap(vChunks, src->vChunks);
193         swap(pCurr, src->pCurr);
194         swap(nLeft, src->nLeft);
195     }
196 
do_validate(const void * ptr) const197     bool BasicAllocator3D::do_validate(const void *ptr) const
198     {
199         if (ptr == NULL)
200             return true;
201 
202         const uint8_t *uptr     = reinterpret_cast<const uint8_t *>(ptr);
203         ssize_t csize           = nSizeOf << nShift;
204 
205         for (size_t i=0; i<nChunks; ++i)
206         {
207             if (vChunks[i] == NULL)
208                 continue;
209             ssize_t delta           = uptr - vChunks[i];
210             if ((delta < 0) || (delta >= csize))
211                 continue;
212             if ((delta % nSizeOf) != 0)
213                 return false;
214             delta /= nSizeOf;
215 
216             return ((i << nShift) + delta) < nAllocated;
217         }
218 
219         return false;
220     }
221 
calc_index_of(const void * ptr) const222     ssize_t BasicAllocator3D::calc_index_of(const void *ptr) const
223     {
224         if (ptr == NULL)
225             return -1;
226 
227         const uint8_t *uptr     = reinterpret_cast<const uint8_t *>(ptr);
228         ssize_t csize           = nSizeOf << nShift;
229         ssize_t offset          = 0;
230         ssize_t chunk_cap       = 1 << nShift;
231 
232         for (size_t i=0; i<nChunks; ++i, offset += chunk_cap)
233         {
234             if (vChunks[i] == NULL)
235                 continue;
236             ssize_t delta           = uptr - vChunks[i];
237             if ((delta < 0) || (delta >= csize))
238                 continue;
239             if ((delta % nSizeOf) != 0)
240                 return -1;
241             return offset + delta / nSizeOf;
242         }
243 
244         return -1;
245     }
246 }
247 
248