1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 7 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2006-2018 The PHP Group |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Authors: Andrey Hristov <andrey@php.net> |
16 | Ulf Wendel <uw@php.net> |
17 | Dmitry Stogov <dmitry@php.net> |
18 +----------------------------------------------------------------------+
19 */
20
21 #include "php.h"
22 #include "mysqlnd.h"
23 #include "mysqlnd_block_alloc.h"
24 #include "mysqlnd_debug.h"
25 #include "mysqlnd_priv.h"
26
27
28 /* {{{ mysqlnd_arena_create */
mysqlnd_arena_create(size_t size)29 static zend_always_inline zend_arena* mysqlnd_arena_create(size_t size)
30 {
31 zend_arena *arena = (zend_arena*)mnd_emalloc(size);
32
33 arena->ptr = (char*) arena + ZEND_MM_ALIGNED_SIZE(sizeof(zend_arena));
34 arena->end = (char*) arena + size;
35 arena->prev = NULL;
36 return arena;
37 }
38 /* }}} */
39
40 /* {{{ mysqlnd_arena_destroy */
mysqlnd_arena_destroy(zend_arena * arena)41 static zend_always_inline void mysqlnd_arena_destroy(zend_arena *arena)
42 {
43 do {
44 zend_arena *prev = arena->prev;
45 mnd_efree(arena);
46 arena = prev;
47 } while (arena);
48 }
49 /* }}} */
50
51 /* {{{ mysqlnd_arena_alloc */
mysqlnd_arena_alloc(zend_arena ** arena_ptr,size_t size)52 static zend_always_inline void* mysqlnd_arena_alloc(zend_arena **arena_ptr, size_t size)
53 {
54 zend_arena *arena = *arena_ptr;
55 char *ptr = arena->ptr;
56
57 size = ZEND_MM_ALIGNED_SIZE(size);
58
59 if (EXPECTED(size <= (size_t)(arena->end - ptr))) {
60 arena->ptr = ptr + size;
61 } else {
62 size_t arena_size =
63 UNEXPECTED((size + ZEND_MM_ALIGNED_SIZE(sizeof(zend_arena))) > (size_t)(arena->end - (char*) arena)) ?
64 (size + ZEND_MM_ALIGNED_SIZE(sizeof(zend_arena))) :
65 (size_t)(arena->end - (char*) arena);
66 zend_arena *new_arena = (zend_arena*)mnd_emalloc(arena_size);
67
68 ptr = (char*) new_arena + ZEND_MM_ALIGNED_SIZE(sizeof(zend_arena));
69 new_arena->ptr = (char*) new_arena + ZEND_MM_ALIGNED_SIZE(sizeof(zend_arena)) + size;
70 new_arena->end = (char*) new_arena + arena_size;
71 new_arena->prev = arena;
72 *arena_ptr = new_arena;
73 }
74
75 return (void*) ptr;
76 }
77 /* }}} */
78
mysqlnd_arena_checkpoint(zend_arena * arena)79 static zend_always_inline void* mysqlnd_arena_checkpoint(zend_arena *arena)
80 {
81 return arena->ptr;
82 }
83
mysqlnd_arena_release(zend_arena ** arena_ptr,void * checkpoint)84 static zend_always_inline void mysqlnd_arena_release(zend_arena **arena_ptr, void *checkpoint)
85 {
86 zend_arena *arena = *arena_ptr;
87
88 while (UNEXPECTED((char*)checkpoint > arena->end) ||
89 UNEXPECTED((char*)checkpoint <= (char*)arena)) {
90 zend_arena *prev = arena->prev;
91 mnd_efree(arena);
92 *arena_ptr = arena = prev;
93 }
94 ZEND_ASSERT((char*)checkpoint > (char*)arena && (char*)checkpoint <= arena->end);
95 arena->ptr = (char*)checkpoint;
96 }
97
98 /* {{{ mysqlnd_mempool_free_chunk */
99 static void
mysqlnd_mempool_free_chunk(MYSQLND_MEMORY_POOL * pool,void * ptr)100 mysqlnd_mempool_free_chunk(MYSQLND_MEMORY_POOL * pool, void * ptr)
101 {
102 DBG_ENTER("mysqlnd_mempool_free_chunk");
103 /* Try to back-off and guess if this is the last block allocated */
104 if (ptr == pool->last) {
105 /*
106 This was the last allocation. Lucky us, we can free
107 a bit of memory from the pool. Next time we will return from the same ptr.
108 */
109 pool->arena->ptr = (char*)ptr;
110 pool->last = NULL;
111 }
112 DBG_VOID_RETURN;
113 }
114 /* }}} */
115
116
117 /* {{{ mysqlnd_mempool_resize_chunk */
118 static void *
mysqlnd_mempool_resize_chunk(MYSQLND_MEMORY_POOL * pool,void * ptr,size_t old_size,size_t size)119 mysqlnd_mempool_resize_chunk(MYSQLND_MEMORY_POOL * pool, void * ptr, size_t old_size, size_t size)
120 {
121 DBG_ENTER("mysqlnd_mempool_resize_chunk");
122
123 /* Try to back-off and guess if this is the last block allocated */
124 if (ptr == pool->last
125 && (ZEND_MM_ALIGNED_SIZE(size) <= ((char*)pool->arena->end - (char*)ptr))) {
126 /*
127 This was the last allocation. Lucky us, we can free
128 a bit of memory from the pool. Next time we will return from the same ptr.
129 */
130 pool->arena->ptr = (char*)ptr + ZEND_MM_ALIGNED_SIZE(size);
131 } else {
132 void *new_ptr = mysqlnd_arena_alloc(&pool->arena, size);
133 memcpy(new_ptr, ptr, MIN(old_size, size));
134 pool->last = ptr = new_ptr;
135 }
136 DBG_RETURN(ptr);
137 }
138 /* }}} */
139
140
141 /* {{{ mysqlnd_mempool_get_chunk */
142 static void *
mysqlnd_mempool_get_chunk(MYSQLND_MEMORY_POOL * pool,size_t size)143 mysqlnd_mempool_get_chunk(MYSQLND_MEMORY_POOL * pool, size_t size)
144 {
145 void *ptr = NULL;
146 DBG_ENTER("mysqlnd_mempool_get_chunk");
147
148 ptr = mysqlnd_arena_alloc(&pool->arena, size);
149 pool->last = ptr;
150
151 DBG_RETURN(ptr);
152 }
153 /* }}} */
154
155
156 /* {{{ mysqlnd_mempool_create */
157 PHPAPI MYSQLND_MEMORY_POOL *
mysqlnd_mempool_create(size_t arena_size)158 mysqlnd_mempool_create(size_t arena_size)
159 {
160 zend_arena * arena;
161 MYSQLND_MEMORY_POOL * ret;
162
163 DBG_ENTER("mysqlnd_mempool_create");
164 arena = mysqlnd_arena_create(MAX(arena_size, ZEND_MM_ALIGNED_SIZE(sizeof(zend_arena))));
165 ret = mysqlnd_arena_alloc(&arena, sizeof(MYSQLND_MEMORY_POOL));
166 ret->arena = arena;
167 ret->last = NULL;
168 ret->checkpoint = NULL;
169 ret->get_chunk = mysqlnd_mempool_get_chunk;
170 ret->free_chunk = mysqlnd_mempool_free_chunk;
171 ret->resize_chunk = mysqlnd_mempool_resize_chunk;
172 DBG_RETURN(ret);
173 }
174 /* }}} */
175
176
177 /* {{{ mysqlnd_mempool_destroy */
178 PHPAPI void
mysqlnd_mempool_destroy(MYSQLND_MEMORY_POOL * pool)179 mysqlnd_mempool_destroy(MYSQLND_MEMORY_POOL * pool)
180 {
181 DBG_ENTER("mysqlnd_mempool_destroy");
182 /* mnd_free will reference LOCK_access and might crash, depending on the caller...*/
183 mysqlnd_arena_destroy(pool->arena);
184 DBG_VOID_RETURN;
185 }
186 /* }}} */
187
188 /* {{{ mysqlnd_mempool_save_state */
189 PHPAPI void
mysqlnd_mempool_save_state(MYSQLND_MEMORY_POOL * pool)190 mysqlnd_mempool_save_state(MYSQLND_MEMORY_POOL * pool)
191 {
192 DBG_ENTER("mysqlnd_mempool_save_state");
193 pool->checkpoint = mysqlnd_arena_checkpoint(pool->arena);
194 DBG_VOID_RETURN;
195 }
196 /* }}} */
197
198 /* {{{ mysqlnd_mempool_restore_state */
199 PHPAPI void
mysqlnd_mempool_restore_state(MYSQLND_MEMORY_POOL * pool)200 mysqlnd_mempool_restore_state(MYSQLND_MEMORY_POOL * pool)
201 {
202 DBG_ENTER("mysqlnd_mempool_restore_state");
203 #if ZEND_DEBUG
204 ZEND_ASSERT(pool->checkpoint);
205 #endif
206 if (pool->checkpoint) {
207 mysqlnd_arena_release(&pool->arena, pool->checkpoint);
208 pool->last = NULL;
209 pool->checkpoint = NULL;
210 }
211 DBG_VOID_RETURN;
212 }
213 /* }}} */
214
215 /*
216 * Local variables:
217 * tab-width: 4
218 * c-basic-offset: 4
219 * End:
220 * vim600: noet sw=4 ts=4 fdm=marker
221 * vim<600: noet sw=4 ts=4
222 */
223