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: 9 окт. 2017 г.
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 #ifndef DATA_CASTORAGE_H_
23 #define DATA_CASTORAGE_H_
24 
25 #include <core/types.h>
26 #include <core/alloc.h>
27 #include <string.h>
28 
29 namespace lsp
30 {
31     class basic_aligned_storage
32     {
33         protected:
34             uint8_t    *vItems;
35             size_t      nCapacity;
36             size_t      nItems;
37             size_t      nSizeOf;
38             size_t      nAlign;
39             uint8_t    *vData;
40 
41         protected:
realloc_capacity(size_t capacity)42             bool realloc_capacity(size_t capacity)
43             {
44                 if (capacity < 32)
45                     capacity        = 32;
46 
47                 // Do aligned (re)allocation
48                 size_t alloc    = nSizeOf * capacity + nAlign;
49 
50                 uint8_t *ptr    = lsp_trealloc(uint8_t, vData, alloc);
51                 if (ptr == NULL)
52                     return false;
53                 else if (ptr == vData)
54                 {
55                     nCapacity   = capacity;
56                     return true;
57                 }
58 
59                 uint8_t *p      = ALIGN_PTR(ptr, nAlign);
60                 size_t s_diff   = vItems - vData;
61                 size_t d_diff   = p - ptr;
62 
63                 if (s_diff != d_diff)
64                     ::memmove(&ptr[d_diff], &ptr[s_diff], nSizeOf * nCapacity);
65 
66                 // Update internal data
67                 vData           = ptr;
68                 vItems          = p;
69                 nCapacity   = capacity;
70                 return true;
71             }
72 
alloc_items(size_t n)73             uint8_t *alloc_items(size_t n)
74             {
75                 if ((nItems + n) > nCapacity)
76                 {
77                     size_t dn = nCapacity + n;
78                     if (!realloc_capacity(dn + (dn >> 1)))
79                         return NULL;
80                 }
81 
82                 uint8_t    *ptr = &vItems[nItems * nSizeOf];
83                 nItems         += n;
84                 return ptr;
85             }
86 
alloc_item()87             uint8_t *alloc_item()
88             {
89                 if (nItems >= nCapacity)
90                 {
91                     if (!realloc_capacity(nCapacity + (nCapacity >> 1)))
92                         return NULL;
93                 }
94 
95                 return &vItems[(nItems++)*nSizeOf];
96             }
97 
insert_items(size_t index,size_t n)98             uint8_t *insert_items(size_t index, size_t n)
99             {
100                 if ((index < 0) || (index > nItems))
101                     return NULL;
102                 if ((nItems + n) > nCapacity)
103                 {
104                     size_t dn = nCapacity + n;
105                     if (!realloc_capacity(dn + (dn >> 1)))
106                         return NULL;
107                 }
108                 uint8_t *res = &vItems[index * nSizeOf];
109                 if (index < nItems)
110                     ::memmove(&vItems[(index+n) * nSizeOf], res, (nItems - index)*nSizeOf);
111                 nItems += n;
112                 return res;
113             }
114 
at(size_t index)115             uint8_t *at(size_t index)
116             {
117                 return &vItems[index * nSizeOf];
118             }
119 
c_at(size_t index)120             uint8_t *c_at(size_t index)
121             {
122                 return &vItems[(index % nItems) * nSizeOf];
123             }
124 
get_item(size_t index)125             uint8_t *get_item(size_t index)
126             {
127                 return (index < nItems) ? &vItems[index * nSizeOf] : NULL;
128             }
129 
c_get_item(size_t index)130             uint8_t *c_get_item(size_t index)
131             {
132                 if (nItems == 0)
133                     return NULL;
134 
135                 return &vItems[(index % nItems) * nSizeOf];
136             }
137 
first()138             uint8_t *first()
139             {
140                 return (nItems > 0) ? vItems : NULL;
141             }
142 
last()143             uint8_t *last()
144             {
145                 return (nItems > 0) ? &vItems[(nItems-1)*nSizeOf] : NULL;
146             }
147 
pop_last()148             uint8_t *pop_last()
149             {
150                 return (nItems > 0) ? &vItems[(--nItems)*nSizeOf] : NULL;
151             }
152 
do_swap_data(basic_aligned_storage * src)153             inline void do_swap_data(basic_aligned_storage *src)
154             {
155                 uint8_t *_vItems    = src->vItems;
156                 size_t  _nCapacity  = src->nCapacity;
157                 size_t  _nItems     = src->nItems;
158                 size_t  _nSizeOf    = src->nSizeOf;
159                 size_t  _nAlign     = src->nAlign;
160                 uint8_t *_vData     = src->vData;
161 
162                 src->vItems     = vItems;
163                 src->nCapacity  = nCapacity;
164                 src->nItems     = nItems;
165                 src->nSizeOf    = nSizeOf;
166                 src->nAlign     = nAlign;
167                 src->vData      = vData;
168 
169                 vItems          = _vItems;
170                 nCapacity       = _nCapacity;
171                 nItems          = _nItems;
172                 nSizeOf         = _nSizeOf;
173                 nAlign          = _nAlign;
174                 vData           = _vData;
175             }
176 
177         public:
basic_aligned_storage(size_t sz,size_t align)178             inline basic_aligned_storage(size_t sz, size_t align)
179             {
180                 vItems      = NULL;
181                 nCapacity   = 0;
182                 nItems      = 0;
183                 nSizeOf     = sz;
184                 nAlign      = align;
185                 vData       = NULL;
186             }
187 
~basic_aligned_storage()188             inline ~basic_aligned_storage()
189             {
190                 flush();
191             }
192 
flush()193             void flush()
194             {
195                 if (vData != NULL)
196                 {
197                     lsp_free(vData);
198                     vData   = NULL;
199                 }
200                 vItems      = NULL;
201                 nCapacity   = 0;
202                 nItems      = 0;
203             }
204 
remove(size_t idx)205             inline bool remove(size_t idx)
206             {
207                 if (idx >= nItems)
208                     return false;
209                 if (idx != (--nItems))
210                     ::memmove(&vItems[idx * nSizeOf], &vItems[(idx+1)*nSizeOf], (nItems-idx) * nSizeOf);
211                 return true;
212             }
213 
remove_n(size_t idx,size_t n)214             inline bool remove_n(size_t idx, size_t n)
215             {
216                 size_t last = idx + n;
217                 if ((last + n) > nItems)
218                     return false;
219                 if ((last + n) != nItems)
220                     ::memmove(&vItems[idx * nSizeOf], &vItems[last * nSizeOf], (nItems - last) * nSizeOf);
221                 nItems     -= n;
222                 return true;
223             }
224 
size()225             inline size_t size() const  { return nItems; }
226 
capacity()227             inline size_t capacity() const { return nCapacity; }
228 
clear()229             inline void clear() { nItems = 0; }
230 
231     };
232 
233     template <class T, size_t A=DEFAULT_ALIGN>
234         class castorage: public basic_aligned_storage
235         {
236             private:
237                 castorage(const castorage<T, A> &src);                          // Disable copying
238                 castorage<T, A> & operator = (const castorage<T, A> & src);     // Disable copying
239 
240             public:
castorage()241                 castorage() : basic_aligned_storage(sizeof(T), A) {};
~castorage()242                 ~castorage() {};
243 
244             public:
append()245                 inline T *append() { return reinterpret_cast<T *>(basic_aligned_storage::alloc_item()); }
add()246                 inline T *add() { return reinterpret_cast<T *>(basic_aligned_storage::alloc_item()); }
247 
append(const T * v)248                 inline T *append(const T *v)
249                 {
250                     T *dst = reinterpret_cast<T *>(basic_aligned_storage::alloc_item());
251                     if (dst != NULL)
252                         *dst = *v;
253                     return dst;
254                 }
255 
append(const T * v,size_t n)256                 inline T *append(const T *v, size_t n)
257                 {
258                     T *dst = reinterpret_cast<T *>(basic_aligned_storage::alloc_item());
259                     if (dst != NULL)
260                         ::memcpy(dst, v, n*sizeof(T));
261                     return dst;
262                 }
263 
append(T v)264                 inline T *append(T v)
265                 {
266                     T *dst = reinterpret_cast<T *>(basic_aligned_storage::alloc_item());
267                     if (dst != NULL)
268                         *dst = v;
269                     return dst;
270                 }
271 
add(const T * v)272                 inline T *add(const T *v)
273                 {
274                     T *dst = reinterpret_cast<T *>(basic_aligned_storage::alloc_item());
275                     if (dst != NULL)
276                         *dst = *v;
277                     return dst;
278                 }
279 
add(T v)280                 inline T *add(T v)
281                 {
282                     T *dst = reinterpret_cast<T *>(basic_aligned_storage::alloc_item());
283                     if (dst != NULL)
284                         *dst = v;
285                     return dst;
286                 }
287 
get(size_t idx)288                 inline T *get(size_t idx) { return reinterpret_cast<T *>(basic_aligned_storage::get_item(idx)); }
289 
c_get(size_t idx)290                 inline T *c_get(size_t idx) { return reinterpret_cast<T *>(basic_aligned_storage::c_get_item(idx)); }
291 
292                 inline T *operator[](size_t index) { return reinterpret_cast<T *>(basic_aligned_storage::get_item(index)); }
293 
append_n(size_t n)294                 inline T *append_n(size_t n) { return (n == 0) ? NULL : reinterpret_cast<T *>(basic_aligned_storage::alloc_items(n)); }
295 
at(size_t index)296                 inline T *at(size_t index) { return reinterpret_cast<T *>(basic_aligned_storage::at(index)); }
297 
c_at(size_t index)298                 inline T *c_at(size_t index) { return reinterpret_cast<T *>(basic_aligned_storage::c_at(index)); }
299 
first()300                 inline T *first() { return reinterpret_cast<T *>(basic_aligned_storage::first()); }
301 
get_array()302                 inline T *get_array() { return reinterpret_cast<T *>(basic_aligned_storage::first()); }
303 
last()304                 inline T *last() { return reinterpret_cast<T *>(basic_aligned_storage::last()); }
305 
insert(size_t idx)306                 inline T *insert(size_t idx) { return reinterpret_cast<T *>(basic_aligned_storage::insert_items(idx, 1)); }
307 
insert(size_t idx,const T * v)308                 inline T *insert(size_t idx, const T *v)
309                 {
310                     T *dst = reinterpret_cast<T *>(basic_aligned_storage::insert_items(idx, 1));
311                     if (dst != NULL)
312                         *dst = *v;
313                     return dst;
314                 }
315 
insert_n(size_t idx,size_t n)316                 inline T *insert_n(size_t idx, size_t n) { return reinterpret_cast<T *>(basic_aligned_storage::insert_items(idx, n)); }
317 
insert_n(size_t idx,const T * v,size_t n)318                 inline T *insert_n(size_t idx, const T *v, size_t n)
319                 {
320                     T *dst = reinterpret_cast<T *>(basic_aligned_storage::insert_items(idx, n));
321                     if (dst != NULL)
322                         ::memcpy(dst, v, n*sizeof(T));
323                     return dst;
324                 }
325 
remove(size_t idx)326                 inline bool remove(size_t idx) { return basic_aligned_storage::remove(idx); }
327 
remove(size_t idx,T * dst)328                 inline bool remove(size_t idx, T *dst)
329                 {
330                     void *p = basic_aligned_storage::get_item(idx);
331                     if (p == NULL)
332                         return false;
333                     *dst = *(reinterpret_cast<T *>(p));
334                     return basic_aligned_storage::remove(idx);
335                 }
336 
remove_n(size_t idx,size_t n)337                 inline bool remove_n(size_t idx, size_t n) { return basic_aligned_storage::remove_n(idx, n); }
338 
pop(T * dst)339                 inline bool pop(T *dst)
340                 {
341                     T *src = reinterpret_cast<T *>(basic_aligned_storage::pop_last());
342                     if (src == NULL)
343                         return false;
344                     *dst = *src;
345                     return true;
346                 }
347 
remove_last()348                 inline bool remove_last()
349                 {
350                     void *ptr = basic_aligned_storage::pop_last();
351                     return ptr != NULL;
352                 }
353 
pop()354                 inline bool pop()
355                 {
356                     void *ptr = basic_aligned_storage::pop_last();
357                     return ptr != NULL;
358                 }
359 
indexof(const T * ptr)360                 inline ssize_t indexof(const T *ptr)
361                 {
362                     T *p        = reinterpret_cast<T *>(vItems);
363                     if (p == NULL)
364                         return -1;
365                     ssize_t idx = ptr - p;
366                     return ((idx < 0) || (idx >= nItems)) ? -1 : idx;
367                 }
368 
swap(castorage<T,A> * src)369                 inline void swap(castorage<T, A> *src) { do_swap_data(src); }
370         };
371 }
372 
373 #endif /* DATA_CASTORAGE_H_ */
374