1 /*
2  *  This file is part of the MR utility library.
3  *
4  *  This code is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This code is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this code; if not, write to the Free Software
16  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17  */
18 
19 /** \file ducc0/infra/aligned_array.h
20  *
21  * \copyright Copyright (C) 2019-2021 Max-Planck-Society
22  * \author Martin Reinecke
23  */
24 
25 #ifndef DUCC0_ALIGNED_ARRAY_H
26 #define DUCC0_ALIGNED_ARRAY_H
27 
28 #include <cstdlib>
29 #include <new>
30 
31 namespace ducc0 {
32 
33 namespace detail_aligned_array {
34 
35 using namespace std;
36 
37 /// Bare bones array class.
38 /** Mostly useful for uninitialized temporary buffers.
39  *  \note Since this class operates on raw memory, it should only be used with
40  *        POD types, and even then only with caution! */
41 template<typename T, size_t alignment=alignof(T)> class array_base
42   {
43   private:
44     T *p;
45     size_t sz;
46 
ralloc(size_t num)47     static T *ralloc(size_t num)
48       {
49       if constexpr(alignment<=alignof(max_align_t))
50         {
51         void *res = malloc(num*sizeof(T));
52         if (!res) throw bad_alloc();
53         return reinterpret_cast<T *>(res);
54         }
55       else
56         {
57         if (num==0) return nullptr;
58 // FIXME: let's not use aligned_alloc on Apple for the moment,
59 // it's only supported from 10.15 on...
60 #if ((__cplusplus >= 201703L) && (!defined(__APPLE__)))
61         // aligned_alloc requires the allocated size to be a multiple of the
62         // requested alignment, so increase size if necessary
63         void *res = aligned_alloc(alignment,((num*sizeof(T)+alignment-1)/alignment)*alignment);
64         if (!res) throw bad_alloc();
65 #else // portable emulation
66         void *ptr = malloc(num*sizeof(T)+alignment);
67         if (!ptr) throw bad_alloc();
68         void *res = reinterpret_cast<void *>((reinterpret_cast<size_t>(ptr) & ~(size_t(alignment-1))) + alignment);
69         (reinterpret_cast<void**>(res))[-1] = ptr;
70 #endif
71         return reinterpret_cast<T *>(res);
72         }
73       }
dealloc(T * ptr)74     static void dealloc(T *ptr)
75       {
76       if constexpr(alignment<=alignof(max_align_t))
77         free(ptr);
78       else
79 #if ((__cplusplus >= 201703L) && (!defined(__APPLE__)))
80         free(ptr);
81 #else
82         if (ptr) free((reinterpret_cast<void**>(ptr))[-1]);
83 #endif
84       }
85 
86   public:
87     /// Creates a zero-sized array with no associated memory.
array_base()88     array_base() : p(nullptr), sz(0) {}
89     /// Creates an array with \a n entries.
90     /** \note Memory is not initialized! */
array_base(size_t n)91     array_base(size_t n) : p(ralloc(n)), sz(n) {}
array_base(array_base && other)92     array_base(array_base &&other)
93       : p(other.p), sz(other.sz)
94       { other.p=nullptr; other.sz=0; }
~array_base()95     ~array_base() { dealloc(p); }
96 
97     /// If \a n is different from the currnt size, resizes the array to hold
98     /// \a n elements.
99     /** \note No data content is copied, the new array is uninitialized! */
resize(size_t n)100     void resize(size_t n)
101       {
102       if (n==sz) return;
103       dealloc(p);
104       p = ralloc(n);
105       sz = n;
106       }
107 
108     /// Returns a writeable reference to the element at index \a idx.
109     T &operator[](size_t idx) { return p[idx]; }
110     /// Returns a read-only reference to the element at index \a idx.
111     const T &operator[](size_t idx) const { return p[idx]; }
112 
113     /// Returns a writeable pointer to the array data.
data()114     T *data() { return p; }
115     /// Returns a read-only pointer to the array data.
data()116     const T *data() const { return p; }
117 
118     /// Returns the size of the array.
size()119     size_t size() const { return sz; }
120   };
121 
122 template<typename T> using quick_array = array_base<T>;
123 template<typename T> using aligned_array = array_base<T,64>;
124 
125 }
126 
127 using detail_aligned_array::aligned_array;
128 using detail_aligned_array::quick_array;
129 
130 }
131 
132 #endif
133 
134