1 ///////////////////////////////////////////////////////////////////////////////
3 /// \file memory_limitter.c
4 /// \brief Limitting memory usage
6 // Copyright (C) 2007 Lasse Collin
8 // This library is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU Lesser General Public
10 // License as published by the Free Software Foundation; either
11 // version 2.1 of the License, or (at your option) any later version.
13 // This library is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 // Lesser General Public License for more details.
18 ///////////////////////////////////////////////////////////////////////////////
23 /// Rounds an unsigned integer upwards to the next multiple.
24 #define my_ceil(num, multiple) \
25 ((num) + (((multiple) - ((num) % (multiple))) % (multiple)))
28 /// Rounds upwards to the next multiple of 2 * sizeof(void*).
29 /// malloc() tends to align allocations this way.
30 #define malloc_ceil(num) my_ceil(num, 2 * sizeof(void *))
33 typedef struct lzma_memlimit_list_s lzma_memlimit_list;
34 struct lzma_memlimit_list_s {
35 lzma_memlimit_list *next;
41 struct lzma_memlimit_s {
44 lzma_memlimit_list *list;
48 extern LZMA_API lzma_memlimit *
49 lzma_memlimit_create(size_t limit)
51 if (limit < sizeof(lzma_memlimit))
54 lzma_memlimit *mem = malloc(sizeof(lzma_memlimit));
57 mem->used = sizeof(lzma_memlimit);
67 lzma_memlimit_set(lzma_memlimit *mem, size_t limit)
74 extern LZMA_API size_t
75 lzma_memlimit_get(const lzma_memlimit *mem)
81 extern LZMA_API size_t
82 lzma_memlimit_used(const lzma_memlimit *mem)
88 extern LZMA_API size_t
89 lzma_memlimit_count(const lzma_memlimit *mem)
91 // This is slow; we could have a counter in lzma_memlimit
92 // for fast version. I expect the primary use of this
93 // function to be limited to easy checking of memory leaks,
94 // in which this implementation is just fine.
96 const lzma_memlimit_list *record = mem->list;
98 while (record != NULL) {
100 record = record->next;
108 lzma_memlimit_end(lzma_memlimit *mem, lzma_bool free_allocated)
113 lzma_memlimit_list *record = mem->list;
114 while (record != NULL) {
118 lzma_memlimit_list *tmp = record;
119 record = record->next;
129 extern LZMA_API void *
130 lzma_memlimit_alloc(lzma_memlimit *mem, size_t nmemb, size_t size)
132 // While liblzma always sets nmemb to one, do this multiplication
133 // to make these functions usable e.g. with zlib and libbzip2.
134 // Making sure that this doesn't overflow is up to the application.
137 // Some malloc() implementations return NULL on malloc(0). We like
138 // to get a non-NULL value.
142 // Calculate how much memory we are going to allocate in reality.
143 // TODO: We should add some rough estimate how much malloc() needs
144 // for its internal structures.
145 const size_t total_size = malloc_ceil(size)
146 + malloc_ceil(sizeof(lzma_memlimit_list));
148 // Integer overflow protection
149 if (SIZE_MAX - size <= total_size)
152 if (mem->limit < mem->used || mem->limit - mem->used < total_size)
155 lzma_memlimit_list *record = malloc(sizeof(lzma_memlimit_list));
156 void *ptr = malloc(size);
158 if (record == NULL || ptr == NULL) {
164 // Add the new entry to the beginning of the list. This should be
165 // more efficient when freeing memory, because usually it is
166 // "last allocated, first freed".
167 record->next = mem->list;
169 record->size = total_size;
172 mem->used += total_size;
179 lzma_memlimit_detach(lzma_memlimit *mem, void *ptr)
181 if (ptr == NULL || mem->list == NULL)
184 lzma_memlimit_list *record = mem->list;
185 lzma_memlimit_list *prev = NULL;
187 while (record->ptr != ptr) {
189 record = record->next;
195 prev->next = record->next;
197 mem->list = record->next;
199 assert(mem->used >= record->size);
200 mem->used -= record->size;
209 lzma_memlimit_free(lzma_memlimit *mem, void *ptr)
214 lzma_memlimit_detach(mem, ptr);