1/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
3#ident "$Id$"
4/*======
5This file is part of PerconaFT.
6
7
8Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
9
10 PerconaFT is free software: you can redistribute it and/or modify
11 it under the terms of the GNU General Public License, version 2,
12 as published by the Free Software Foundation.
13
14 PerconaFT is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
21
22----------------------------------------
23
24 PerconaFT is free software: you can redistribute it and/or modify
25 it under the terms of the GNU Affero General Public License, version 3,
26 as published by the Free Software Foundation.
27
28 PerconaFT is distributed in the hope that it will be useful,
29 but WITHOUT ANY WARRANTY; without even the implied warranty of
30 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31 GNU Affero General Public License for more details.
32
33 You should have received a copy of the GNU Affero General Public License
34 along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
35======= */
36
37#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
38
39#include <string.h>
40#include <memory.h>
41#include <toku_assert.h>
42#include "mempool.h"
43
44/* Contract:
45 * Caller allocates mempool struct as convenient for caller, but memory used for data storage
46 * must be dynamically allocated via toku_malloc().
47 * Caller dynamically allocates memory for mempool and initializes mempool by calling toku_mempool_init().
48 * Once a buffer is assigned to a mempool (via toku_mempool_init()), the mempool owns it and
49 * is responsible for destroying it when the mempool is destroyed.
50 * Caller destroys mempool by calling toku_mempool_destroy().
51 *
52 * Note, toku_mempool_init() does not allocate the memory because sometimes the caller will already have
53 * the memory allocated and will assign the pre-allocated memory to the mempool.
54 */
55
56/* This is a constructor to be used when the memory for the mempool struct has been
57 * allocated by the caller, but no memory has yet been allocatd for the data.
58 */
59void toku_mempool_zero(struct mempool *mp) {
60 // printf("mempool_zero %p\n", mp);
61 memset(mp, 0, sizeof(*mp));
62}
63
64// TODO 4050 this is dirty, try to replace all uses of this
65void toku_mempool_init(struct mempool *mp, void *base, size_t free_offset, size_t size) {
66 // printf("mempool_init %p %p %lu\n", mp, base, size);
67 paranoid_invariant(base != 0);
68 paranoid_invariant(size < (1U<<31)); // used to be assert(size >= 0), but changed to size_t so now let's make sure it's not more than 2GB...
69 paranoid_invariant(free_offset <= size);
70 mp->base = base;
71 mp->size = size;
72 mp->free_offset = free_offset; // address of first available memory
73 mp->frag_size = 0; // byte count of wasted space (formerly used, no longer used or available)
74}
75
76/* allocate memory and construct mempool
77 */
78void toku_mempool_construct(struct mempool *mp, size_t data_size) {
79 if (data_size) {
80 // add 25% slack
81 size_t mp_size = data_size + (data_size / 4);
82 mp->base = toku_xmalloc_aligned(64, mp_size);
83 mp->size = mp_size;
84 mp->free_offset = 0;
85 mp->frag_size = 0;
86 }
87 else {
88 toku_mempool_zero(mp);
89 }
90}
91
92void toku_mempool_reset(struct mempool *mp) {
93 mp->free_offset = 0;
94 mp->frag_size = 0;
95}
96
97void toku_mempool_realloc_larger(struct mempool *mp, size_t data_size) {
98 invariant(data_size >= mp->free_offset);
99
100 size_t mpsize = data_size + (data_size/4); // allow 1/4 room for expansion (would be wasted if read-only)
101 void* newmem = toku_xmalloc_aligned(64, mpsize); // allocate new buffer for mempool
102 memcpy(newmem, mp->base, mp->free_offset); // Copy old info
103 toku_free(mp->base);
104 mp->base = newmem;
105 mp->size = mpsize;
106}
107
108
109void toku_mempool_destroy(struct mempool *mp) {
110 // printf("mempool_destroy %p %p %lu %lu\n", mp, mp->base, mp->size, mp->frag_size);
111 if (mp->base)
112 toku_free(mp->base);
113 toku_mempool_zero(mp);
114}
115
116void *toku_mempool_get_base(const struct mempool *mp) {
117 return mp->base;
118}
119
120void *toku_mempool_get_pointer_from_base_and_offset(const struct mempool *mp, size_t offset) {
121 return reinterpret_cast<void*>(reinterpret_cast<char*>(mp->base) + offset);
122}
123
124size_t toku_mempool_get_offset_from_pointer_and_base(const struct mempool *mp, const void* p) {
125 paranoid_invariant(p >= mp->base);
126 return reinterpret_cast<const char*>(p) - reinterpret_cast<const char*>(mp->base);
127}
128
129size_t toku_mempool_get_size(const struct mempool *mp) {
130 return mp->size;
131}
132
133size_t toku_mempool_get_frag_size(const struct mempool *mp) {
134 return mp->frag_size;
135}
136
137size_t toku_mempool_get_used_size(const struct mempool *mp) {
138 return mp->free_offset - mp->frag_size;
139}
140
141void* toku_mempool_get_next_free_ptr(const struct mempool *mp) {
142 return toku_mempool_get_pointer_from_base_and_offset(mp, mp->free_offset);
143}
144
145size_t toku_mempool_get_offset_limit(const struct mempool *mp) {
146 return mp->free_offset;
147}
148
149size_t toku_mempool_get_free_size(const struct mempool *mp) {
150 return mp->size - mp->free_offset;
151}
152
153size_t toku_mempool_get_allocated_size(const struct mempool *mp) {
154 return mp->free_offset;
155}
156
157void *toku_mempool_malloc(struct mempool *mp, size_t size) {
158 paranoid_invariant(size < (1U<<31));
159 paranoid_invariant(mp->size < (1U<<31));
160 paranoid_invariant(mp->free_offset < (1U<<31));
161 paranoid_invariant(mp->free_offset <= mp->size);
162 void *vp;
163 if (mp->free_offset + size > mp->size) {
164 vp = nullptr;
165 } else {
166 vp = reinterpret_cast<char *>(mp->base) + mp->free_offset;
167 mp->free_offset += size;
168 }
169 paranoid_invariant(mp->free_offset <= mp->size);
170 paranoid_invariant(vp == 0 || toku_mempool_inrange(mp, vp, size));
171 return vp;
172}
173
174// if vp is null then we are freeing something, but not specifying what. The data won't be freed until compression is done.
175void toku_mempool_mfree(struct mempool *mp, void *vp, size_t size) {
176 if (vp) { paranoid_invariant(toku_mempool_inrange(mp, vp, size)); }
177 mp->frag_size += size;
178 invariant(mp->frag_size <= mp->free_offset);
179 invariant(mp->frag_size <= mp->size);
180}
181
182
183/* get memory footprint */
184size_t toku_mempool_footprint(struct mempool *mp) {
185 void * base = mp->base;
186 size_t touched = mp->free_offset;
187 size_t rval = toku_memory_footprint(base, touched);
188 return rval;
189}
190
191void toku_mempool_clone(const struct mempool* orig_mp, struct mempool* new_mp) {
192 new_mp->frag_size = orig_mp->frag_size;
193 new_mp->free_offset = orig_mp->free_offset;
194 new_mp->size = orig_mp->free_offset; // only make the cloned mempool store what is needed
195 new_mp->base = toku_xmalloc_aligned(64, new_mp->size);
196 memcpy(new_mp->base, orig_mp->base, new_mp->size);
197}
198