1 /* Copyright (c) 2015  Gerald Knizia
2  *
3  * This file is part of the IboView program (see: http://www.iboview.org)
4  *
5  * IboView is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, version 3.
8  *
9  * IboView 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 details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with bfint (LICENSE). If not, see http://www.gnu.org/licenses/
16  *
17  * Please see IboView documentation in README.txt for:
18  * -- A list of included external software and their licenses. The included
19  *    external software's copyright is not touched by this agreement.
20  * -- Notes on re-distribution and contributions to/further development of
21  *    the IboView software
22  */
23 
24 #ifndef _CX_MEMORYSTACK_H
25 #define _CX_MEMORYSTACK_H
26 
27 #include <stddef.h> // for size_t
28 #include <string.h> // for memset
29 
30 typedef unsigned int
31    uint;
32 
33 namespace ct {
34 
35 /// An object managing a continuous block of memory. Used for allocation-free
36 /// temporary work space storage.
37 /// Notes:
38 ///    - Deallocations must occur in reverse allocation order. A deallocation
39 ///      FREES ALL MEMORY ALLOCATED AFTER THE ALLOCATION POINT.
40 ///    - Both alloc and free are effectively non-ops in terms of computational
41 ///      cost. Any work space, no matter how large or small, can be allocated
42 ///      at will.
43 ///    - There is no memory fragmentation.
44 struct FMemoryStack
45 {
46     /// allocate nSizeInBytes bytes off the stack. Return pointer to start.
47     virtual void* Alloc(size_t nSizeInBytes) = 0;
48     /// Free pointer p on stack. Should be an address returned by Alloc.
49     virtual void Free(void *p) = 0;
50     /// return amount of memory left (in bytes).
51     virtual size_t MemoryLeft() = 0;
52 
53     /// allocate nObjects objects of size sizeof(T). Store pointer in p.
54     /// Usage:
55     ///     MyType *p;
56     ///     Mem.Alloc(p, 10);  // allocate array of 10 objects of type MyType.
57     template<class T>
58     inline void Alloc(T *&p, size_t nObjects = 1){
59         p = reinterpret_cast<T*>( this->Alloc(sizeof(T) * nObjects) );
60         // note: objects are not constructed. Use only for PODs!
61     }
62 
63     /// as Alloc(), but set allocated memory to zero.
64     template<class T>
65     inline void ClearAlloc(T *&p, size_t nObjects = 1){
66         Alloc(p, nObjects);
67         ::memset(p, 0, sizeof(T) * nObjects);
68     }
69 
70     template<class T>
AllocNFMemoryStack71     inline T* AllocN(size_t nObjects, T const &){
72         return reinterpret_cast<T*>( this->Alloc(sizeof(T) * nObjects) );
73     }
74 
75     template<class T>
ClearAllocNFMemoryStack76     inline T* ClearAllocN(size_t nObjects, T const &){
77         T *p;
78         this->Alloc(p, nObjects);
79         ::memset(p, 0, sizeof(T) * nObjects);
80         return p;
81     }
82 
83     /// align stack to next 'Boundary' (must be power of two)-boundary .
84     virtual void Align(uint Boundary);
85 
86     virtual ~FMemoryStack();
87 };
88 
89 /// A memory stack defined by a base pointer and a size. Can own
90 /// the memory managed, but can also be put on memory allocated from
91 /// elsewhere.
92 struct FMemoryStack2 : public FMemoryStack {
93    /// creates a stack of 'nInitialSize' bytes on the global heap.
94    /// If nInitialSize is 0, the stack is created empty and a storage
95    /// area can be later assigned by AssignMemory() or Create().
96    explicit FMemoryStack2(size_t nInitialSize = 0) : m_pDataStart(0), m_pDataAligned(0) { Create(nInitialSize); }
97    /// if pBase != 0 && nActualSize >= nNeededSize:
98    ///    creates a stack of 'nActualSize' bytes at memory pBase (i.e., at a memory location given from the outside)
99    /// otherwise
100    ///    construction equivalent to FMemoryStack2((nNeededSize != 0)? nNeededSize : nActualSize).
101    FMemoryStack2(char *pBase, size_t nActualSize, size_t nNeededSize = 0);
102    inline void PushTopMark();
103    inline void PopTopMark();
104 
105    void* Alloc(size_t nSizeInBytes); // override
106    using FMemoryStack::Alloc;
107    void Free(void *p); // override
108    size_t MemoryLeft(); // override
109    ~FMemoryStack2(); // override
110 
111    /// create a new stack of size 'nSize' bytes on the global heap.
112    void Create( size_t nSize );
113    /// create a new stack in the storage area marked by 'pBase',
114    /// of 'nSize' bytes. This assumes that pBase is already allocated
115    /// from elsewhere.
116    void AssignMemory(char *pBase_, size_t nSize);
117    void Destroy();
118    void Align(uint Boundary); // override
119 
120 private:
121    size_t
122       m_Pos, m_Size;
123    char
124       *m_pDataStart,
125       *m_pDataAligned; // next default-aligned address beyond m_pDataStart.
126    void DefaultAlign();
127    bool m_bOwnMemory; // if true, we allocated m_pData and need to free it on Destroy().
128 
129 private:
130     FMemoryStack2(FMemoryStack2 const &); // not implemented
131     void operator = (FMemoryStack2 const &); // not implemented
132 };
133 
134 // allocate a block of memory from pMem, and free it when the object goes out of scope.
135 template<class FType>
136 struct TMemoryLock
137 {
138    FType
139       *p; // pointer to allocated data
140 
141    // allocate nCount objects from pMem, and store the pointer in this->p
142    inline TMemoryLock(size_t nCount, FMemoryStack *pMem);
143    // allocate nCount objects from pMem, and store the pointer in this->p and pOutsidePtr.
144    inline TMemoryLock(FType *&pOutsidePtr, size_t nCount, FMemoryStack *pMem);
145    inline ~TMemoryLock();
146 
147    operator FType* () { return p; }
148    operator FType const* () const { return p; }
149 
150    FMemoryStack
151       *m_pMemoryStack;
152 };
153 
154 template<class FType>
TMemoryLock(size_t nCount,FMemoryStack * pMem)155 TMemoryLock<FType>::TMemoryLock(size_t nCount, FMemoryStack *pMem)
156 {
157    m_pMemoryStack = pMem;
158    m_pMemoryStack->Alloc(p, nCount);
159 }
160 
161 template<class FType>
TMemoryLock(FType * & pOutsidePtr,size_t nCount,FMemoryStack * pMem)162 TMemoryLock<FType>::TMemoryLock(FType *&pOutsidePtr, size_t nCount, FMemoryStack *pMem)
163 {
164    m_pMemoryStack = pMem;
165    m_pMemoryStack->Alloc(p, nCount);
166    pOutsidePtr = p;
167 }
168 
169 template<class FType>
~TMemoryLock()170 TMemoryLock<FType>::~TMemoryLock()
171 {
172    m_pMemoryStack->Free(p);
173    p = 0;
174 }
175 
176 
177 /// split a base memory stack into sub-stacks, one for each openmp thread.
178 struct FMemoryStackArray
179 {
180     explicit FMemoryStackArray(FMemoryStack &BaseStack);
181     ~FMemoryStackArray();
182 
183     FMemoryStack2
184         *pSubStacks;
185     FMemoryStack
186         *pBaseStack;
187     uint
188         nThreads;
189 
190     // return substack for the calling openmp thread.
191     FMemoryStack2 &GetStackOfThread();
192 
193     // destroy this object (explicitly) already now
194     void Release();
195 
196     char
197         *pStackBase;
198 private:
199     FMemoryStackArray(FMemoryStackArray const &); // not implemented
200     void operator = (FMemoryStackArray const &); // not implemented
201 };
202 
203 
204 
205 } // namespace ct
206 
207 
208 #endif // _CX_MEMORYSTACK_H
209