c@313
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
c@313
|
2
|
c@313
|
3 /*
|
c@313
|
4 QM DSP Library
|
c@313
|
5
|
c@313
|
6 Centre for Digital Music, Queen Mary, University of London.
|
c@313
|
7
|
c@313
|
8 This file is derived from the FSB Allocator by Juha Nieminen. The
|
c@313
|
9 underlying method is unchanged, but the class has been refactored
|
c@313
|
10 to permit multiple separate allocators (e.g. one per thread)
|
c@313
|
11 rather than use a single global one (and to fit house style).
|
c@313
|
12
|
c@313
|
13 Copyright (c) 2008 Juha Nieminen
|
c@313
|
14
|
c@313
|
15 Permission is hereby granted, free of charge, to any person obtaining a copy
|
c@313
|
16 of this software and associated documentation files (the "Software"), to deal
|
c@313
|
17 in the Software without restriction, including without limitation the rights
|
c@313
|
18 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
c@313
|
19 copies of the Software, and to permit persons to whom the Software is
|
c@313
|
20 furnished to do so, subject to the following conditions:
|
c@313
|
21
|
c@313
|
22 The above copyright notice and this permission notice shall be included in
|
c@313
|
23 all copies or substantial portions of the Software.
|
c@313
|
24
|
c@313
|
25 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
c@313
|
26 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
c@313
|
27 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
c@313
|
28 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
c@313
|
29 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
c@313
|
30 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
c@313
|
31 THE SOFTWARE.
|
c@313
|
32 */
|
c@313
|
33
|
c@313
|
34 #ifndef _BLOCK_ALLOCATOR_H_
|
c@313
|
35 #define _BLOCK_ALLOCATOR_H_
|
c@313
|
36
|
c@313
|
37 #include <cstdlib>
|
c@313
|
38
|
c@313
|
39 /**
|
c@313
|
40 * BlockAllocator is a simple allocator for fixed-size (usually small)
|
c@313
|
41 * chunks of memory. The size of an element is specified in the
|
c@313
|
42 * BlockAllocator constructor, and the functions allocate() and
|
c@313
|
43 * deallocate() are used to obtain and release a single element at a
|
c@313
|
44 * time.
|
c@313
|
45 *
|
c@313
|
46 * BlockAllocator may be an appropriate class to use in situations
|
c@313
|
47 * involving a very large number of allocations and deallocations of
|
c@313
|
48 * simple, identical objects across multiple threads (a hard situation
|
c@313
|
49 * for a generic system malloc implementation to handle well). Retain
|
c@313
|
50 * one BlockAllocator per thread (the class itself is not
|
c@313
|
51 * thread-safe), and ensure that each thread uses its own allocator
|
c@313
|
52 * exclusively.
|
c@313
|
53 *
|
c@313
|
54 * BlockAllocator is based on Juha Nieminen's more general
|
c@313
|
55 * FSBAllocator.
|
c@313
|
56 */
|
c@313
|
57 class BlockAllocator
|
c@313
|
58 {
|
c@313
|
59 public:
|
c@313
|
60 typedef std::size_t data_t;
|
c@313
|
61
|
c@313
|
62 BlockAllocator(int elementSize) : m_sz(elementSize) { }
|
c@313
|
63
|
c@313
|
64 void *
|
c@313
|
65 allocate()
|
c@313
|
66 {
|
c@313
|
67 if (m_freelist.empty()) {
|
c@313
|
68 m_freelist.push_back(m_blocks.data.size());
|
c@313
|
69 m_blocks.data.push_back(Block(this));
|
c@313
|
70 }
|
c@313
|
71
|
c@313
|
72 const data_t index = m_freelist.back();
|
c@313
|
73 Block &block = m_blocks.data[index];
|
c@313
|
74 void *retval = block.allocate(index);
|
c@313
|
75 if (block.isFull()) m_freelist.pop_back();
|
c@313
|
76
|
c@313
|
77 return retval;
|
c@313
|
78 }
|
c@313
|
79
|
c@313
|
80 void
|
c@313
|
81 deallocate(void *ptr)
|
c@313
|
82 {
|
c@313
|
83 if (!ptr) return;
|
c@313
|
84
|
c@313
|
85 data_t *unitPtr = (data_t *)ptr;
|
c@313
|
86 const data_t blockIndex = unitPtr[elementSizeInDataUnits()];
|
c@313
|
87 Block& block = m_blocks.data[blockIndex];
|
c@313
|
88
|
c@313
|
89 if (block.isFull()) m_freelist.push_back(blockIndex);
|
c@313
|
90 block.deallocate(unitPtr);
|
c@313
|
91 }
|
c@313
|
92
|
c@313
|
93 private:
|
c@313
|
94 inline data_t elementsPerBlock() const {
|
c@313
|
95 return 512;
|
c@313
|
96 }
|
c@313
|
97 inline data_t dataSize() const {
|
c@313
|
98 return sizeof(data_t);
|
c@313
|
99 }
|
c@313
|
100 inline data_t elementSizeInDataUnits() const {
|
c@313
|
101 return (m_sz + (dataSize() - 1)) / dataSize();
|
c@313
|
102 }
|
c@313
|
103 inline data_t unitSizeInDataUnits() const {
|
c@313
|
104 return elementSizeInDataUnits() + 1;
|
c@313
|
105 }
|
c@313
|
106 inline data_t blockSizeInDataUnits() const {
|
c@313
|
107 return elementsPerBlock() * unitSizeInDataUnits();
|
c@313
|
108 }
|
c@313
|
109
|
c@313
|
110 class Block
|
c@313
|
111 {
|
c@313
|
112 public:
|
c@313
|
113 Block(BlockAllocator *a) :
|
c@313
|
114 m_a(a),
|
c@313
|
115 m_block(0),
|
c@313
|
116 m_firstFreeUnit(data_t(-1)),
|
c@313
|
117 m_allocated(0),
|
c@313
|
118 m_end(0)
|
c@313
|
119 {}
|
c@313
|
120
|
c@313
|
121 ~Block() {
|
c@313
|
122 delete[] m_block;
|
c@313
|
123 }
|
c@313
|
124
|
c@313
|
125 bool isFull() const {
|
c@313
|
126 return m_allocated == m_a->elementsPerBlock();
|
c@313
|
127 }
|
c@313
|
128
|
c@313
|
129 void clear() {
|
c@313
|
130 delete[] m_block;
|
c@313
|
131 m_block = 0;
|
c@313
|
132 m_firstFreeUnit = data_t(-1);
|
c@313
|
133 }
|
c@313
|
134
|
c@313
|
135 void *allocate(data_t index) {
|
c@313
|
136
|
c@313
|
137 if (m_firstFreeUnit == data_t(-1)) {
|
c@313
|
138
|
c@313
|
139 if (!m_block) {
|
c@313
|
140 m_block = new data_t[m_a->blockSizeInDataUnits()];
|
c@313
|
141 m_end = 0;
|
c@313
|
142 }
|
c@313
|
143
|
c@313
|
144 data_t *retval = m_block + m_end;
|
c@313
|
145 m_end += m_a->unitSizeInDataUnits();
|
c@313
|
146 retval[m_a->elementSizeInDataUnits()] = index;
|
c@313
|
147 ++m_allocated;
|
c@313
|
148 return retval;
|
c@313
|
149
|
c@313
|
150 } else {
|
c@313
|
151
|
c@313
|
152 data_t *retval = m_block + m_firstFreeUnit;
|
c@313
|
153 m_firstFreeUnit = *retval;
|
c@313
|
154 ++m_allocated;
|
c@313
|
155 return retval;
|
c@313
|
156 }
|
c@313
|
157 }
|
c@313
|
158
|
c@313
|
159 void deallocate(data_t *ptr) {
|
c@313
|
160
|
c@313
|
161 *ptr = m_firstFreeUnit;
|
c@313
|
162 m_firstFreeUnit = ptr - m_block;
|
c@313
|
163
|
c@313
|
164 if (--m_allocated == 0) clear();
|
c@313
|
165 }
|
c@313
|
166
|
c@313
|
167 private:
|
c@313
|
168 const BlockAllocator *m_a;
|
c@313
|
169 data_t *m_block;
|
c@313
|
170 data_t m_firstFreeUnit;
|
c@313
|
171 data_t m_allocated;
|
c@313
|
172 data_t m_end;
|
c@313
|
173 };
|
c@313
|
174
|
c@313
|
175 struct Blocks
|
c@313
|
176 {
|
c@313
|
177 std::vector<Block> data;
|
c@313
|
178
|
c@313
|
179 Blocks() {
|
c@313
|
180 data.reserve(1024);
|
c@313
|
181 }
|
c@313
|
182 };
|
c@313
|
183
|
c@313
|
184 const int m_sz;
|
c@313
|
185 Blocks m_blocks;
|
c@313
|
186 std::vector<data_t> m_freelist;
|
c@313
|
187 };
|
c@313
|
188
|
c@313
|
189 #endif
|