Split lines with many copyright years
[alexxy/gromacs.git] / src / gromacs / selection / mempool.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2010,2011,2012,2014,2017 by the GROMACS development team.
5  * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
6  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
7  * and including many others, as listed in the AUTHORS file in the
8  * top-level source directory and at http://www.gromacs.org.
9  *
10  * GROMACS is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public License
12  * as published by the Free Software Foundation; either version 2.1
13  * of the License, or (at your option) any later version.
14  *
15  * GROMACS is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with GROMACS; if not, see
22  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
23  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
24  *
25  * If you want to redistribute modifications to GROMACS, please
26  * consider that scientific software is very special. Version
27  * control is crucial - bugs must be traceable. We will be happy to
28  * consider code for inclusion in the official distribution, but
29  * derived work must not be called official GROMACS. Details are found
30  * in the README & COPYING files - if they are missing, get the
31  * official version at http://www.gromacs.org.
32  *
33  * To help us fund GROMACS development, we humbly ask that you cite
34  * the research papers on the package. Check out http://www.gromacs.org.
35  */
36 /*! \internal \file
37  * \brief
38  * Implements functions in mempool.h.
39  *
40  * \author Teemu Murtola <teemu.murtola@gmail.com>
41  * \ingroup module_selection
42  */
43 #include "gmxpre.h"
44
45 #include "mempool.h"
46
47 #include <cstdlib>
48
49 #include <new>
50
51 #include "gromacs/selection/indexutil.h"
52 #include "gromacs/utility/exceptions.h"
53 #include "gromacs/utility/gmxassert.h"
54 #include "gromacs/utility/smalloc.h"
55
56 //! Alignment in bytes for all returned blocks.
57 #define ALIGN_STEP 8
58
59 /*! \internal \brief
60  * Describes a single block allocated from the memory pool.
61  */
62 typedef struct gmx_sel_mempool_block_t
63 {
64     //! Pointer to the start of the block (as returned to the user).
65     void* ptr;
66     //! Size of the block, including padding required to align next block.
67     size_t size;
68 } gmx_sel_mempool_block_t;
69
70 /*! \internal \brief
71  * Describes a memory pool.
72  */
73 struct gmx_sel_mempool_t
74 {
75     //! Number of bytes currently allocated from the pool.
76     size_t currsize;
77     //! Number of bytes free in the pool, or 0 if \a buffer is NULL.
78     size_t freesize;
79     //! Memory area allocated for the pool, or NULL if not yet reserved.
80     char* buffer;
81     //! Pointer to the first free byte (aligned at ::ALIGN_STEP) in \a buffer.
82     char* freeptr;
83     //! Number of blocks allocated from the pool.
84     int nblocks;
85     //! Array describing the allocated blocks.
86     gmx_sel_mempool_block_t* blockstack;
87     //! Number of elements allocated for the \a blockstack array.
88     int blockstack_nalloc;
89     /*! \brief
90      * Maximum number of bytes that have been reserved from the pool
91      * simultaneously.
92      */
93     size_t maxsize;
94 };
95
96 gmx_sel_mempool_t* _gmx_sel_mempool_create()
97 {
98     gmx_sel_mempool_t* mp;
99
100     snew(mp, 1);
101     mp->currsize          = 0;
102     mp->freesize          = 0;
103     mp->buffer            = nullptr;
104     mp->freeptr           = nullptr;
105     mp->nblocks           = 0;
106     mp->blockstack        = nullptr;
107     mp->blockstack_nalloc = 0;
108     mp->maxsize           = 0;
109     return mp;
110 }
111
112 void _gmx_sel_mempool_destroy(gmx_sel_mempool_t* mp)
113 {
114     if (!mp->buffer)
115     {
116         int i;
117
118         for (i = 0; i < mp->nblocks; ++i)
119         {
120             sfree(mp->blockstack[i].ptr);
121         }
122     }
123     sfree(mp->buffer);
124     sfree(mp->blockstack);
125     sfree(mp);
126 }
127
128 void* _gmx_sel_mempool_alloc(gmx_sel_mempool_t* mp, size_t size)
129 {
130     void*  ptr = nullptr;
131     size_t size_walign;
132
133     size_walign = ((size + ALIGN_STEP - 1) / ALIGN_STEP) * ALIGN_STEP;
134     if (mp->buffer)
135     {
136         if (mp->freesize < size)
137         {
138             GMX_THROW(gmx::InternalError("Out of memory pool memory"));
139         }
140         ptr = mp->freeptr;
141         mp->freeptr += size_walign;
142         mp->freesize -= size_walign;
143         mp->currsize += size_walign;
144     }
145     else
146     {
147         ptr = malloc(size);
148         if (!ptr)
149         {
150             throw std::bad_alloc();
151         }
152         mp->currsize += size_walign;
153         if (mp->currsize > mp->maxsize)
154         {
155             mp->maxsize = mp->currsize;
156         }
157     }
158
159     if (mp->nblocks >= mp->blockstack_nalloc)
160     {
161         mp->blockstack_nalloc = mp->nblocks + 10;
162         srenew(mp->blockstack, mp->blockstack_nalloc);
163     }
164     mp->blockstack[mp->nblocks].ptr  = ptr;
165     mp->blockstack[mp->nblocks].size = size_walign;
166     mp->nblocks++;
167
168     return ptr;
169 }
170
171 void _gmx_sel_mempool_free(gmx_sel_mempool_t* mp, void* ptr)
172 {
173     int size;
174
175     if (ptr == nullptr)
176     {
177         return;
178     }
179     GMX_RELEASE_ASSERT(mp->nblocks > 0 && mp->blockstack[mp->nblocks - 1].ptr == ptr,
180                        "Invalid order of memory pool free calls");
181     mp->nblocks--;
182     size = mp->blockstack[mp->nblocks].size;
183     mp->currsize -= size;
184     if (mp->buffer)
185     {
186         mp->freeptr = static_cast<char*>(ptr);
187         mp->freesize += size;
188     }
189     else
190     {
191         sfree(ptr);
192     }
193 }
194
195 void _gmx_sel_mempool_reserve(gmx_sel_mempool_t* mp, size_t size)
196 {
197     GMX_RELEASE_ASSERT(mp->nblocks == 0,
198                        "Cannot reserve memory pool when there is something allocated");
199     GMX_RELEASE_ASSERT(!mp->buffer, "Cannot reserve memory pool twice");
200     if (size == 0)
201     {
202         size = mp->maxsize;
203     }
204     mp->buffer = static_cast<char*>(malloc(size));
205     if (!mp->buffer)
206     {
207         throw std::bad_alloc();
208     }
209     mp->freesize = size;
210     mp->freeptr  = mp->buffer;
211 }
212
213 void _gmx_sel_mempool_alloc_group(gmx_sel_mempool_t* mp, gmx_ana_index_t* g, int isize)
214 {
215     void* ptr = _gmx_sel_mempool_alloc(mp, sizeof(*g->index) * isize);
216     g->index  = static_cast<int*>(ptr);
217 }
218
219 void _gmx_sel_mempool_free_group(gmx_sel_mempool_t* mp, gmx_ana_index_t* g)
220 {
221     _gmx_sel_mempool_free(mp, g->index);
222     g->index = nullptr;
223 }