1 /*
2 * Copyright (C) 2018-2019 Alexander Borisov
3 *
4 * Author: Alexander Borisov <borisov@lexbor.com>
5 */
6
7 #include "lexbor/core/mraw.h"
8
9
10 #if defined(LEXBOR_HAVE_ADDRESS_SANITIZER)
11 #include <sanitizer/asan_interface.h>
12 #endif
13
14
15 #define lexbor_mraw_meta_set(data, size) \
16 do { \
17 memcpy(data, size, sizeof(size_t)); \
18 } \
19 while (0)
20
21 #define lexbor_mraw_data_begin(data) \
22 &((uint8_t *) (data))[ lexbor_mraw_meta_size() ]
23
24
25 lxb_inline void *
26 lexbor_mraw_realloc_tail(lexbor_mraw_t *mraw, void *data, void *begin,
27 size_t size, size_t begin_len, size_t new_size,
28 bool *is_valid);
29
30
31 lexbor_mraw_t *
lexbor_mraw_create(void)32 lexbor_mraw_create(void)
33 {
34 return lexbor_calloc(1, sizeof(lexbor_mraw_t));
35 }
36
37 lxb_status_t
lexbor_mraw_init(lexbor_mraw_t * mraw,size_t chunk_size)38 lexbor_mraw_init(lexbor_mraw_t *mraw, size_t chunk_size)
39 {
40 lxb_status_t status;
41
42 if (mraw == NULL) {
43 return LXB_STATUS_ERROR_OBJECT_IS_NULL;
44 }
45
46 if (chunk_size == 0) {
47 return LXB_STATUS_ERROR_WRONG_ARGS;
48 }
49
50 /* Init memory */
51 mraw->mem = lexbor_mem_create();
52
53 status = lexbor_mem_init(mraw->mem, chunk_size + lexbor_mraw_meta_size());
54 if (status) {
55 return status;
56 }
57
58 #if defined(LEXBOR_HAVE_ADDRESS_SANITIZER)
59 ASAN_POISON_MEMORY_REGION(mraw->mem->chunk->data, mraw->mem->chunk->size);
60 #endif
61
62 /* Cache */
63 mraw->cache = lexbor_bst_create();
64
65 status = lexbor_bst_init(mraw->cache, 512);
66 if (status) {
67 return status;
68 }
69
70 mraw->ref_count = 0;
71
72 return LXB_STATUS_OK;
73 }
74
75 void
lexbor_mraw_clean(lexbor_mraw_t * mraw)76 lexbor_mraw_clean(lexbor_mraw_t *mraw)
77 {
78 if (mraw != NULL) {
79 lexbor_mem_clean(mraw->mem);
80 lexbor_bst_clean(mraw->cache);
81
82 mraw->ref_count = 0;
83 }
84 }
85
86 lexbor_mraw_t *
lexbor_mraw_destroy(lexbor_mraw_t * mraw,bool destroy_self)87 lexbor_mraw_destroy(lexbor_mraw_t *mraw, bool destroy_self)
88 {
89 if (mraw == NULL) {
90 return NULL;
91 }
92
93 mraw->mem = lexbor_mem_destroy(mraw->mem, true);
94 mraw->cache = lexbor_bst_destroy(mraw->cache, true);
95
96 if (destroy_self) {
97 return lexbor_free(mraw);
98 }
99
100 return mraw;
101 }
102
103 lxb_inline void *
lexbor_mraw_mem_alloc(lexbor_mraw_t * mraw,size_t length)104 lexbor_mraw_mem_alloc(lexbor_mraw_t *mraw, size_t length)
105 {
106 uint8_t *data;
107 lexbor_mem_t *mem = mraw->mem;
108
109 if (length == 0) {
110 return NULL;
111 }
112
113 if ((mem->chunk->length + length) > mem->chunk->size) {
114 lexbor_mem_chunk_t *chunk = mem->chunk;
115
116 if ((SIZE_MAX - mem->chunk_length) == 0) {
117 return NULL;
118 }
119
120 if (chunk->length == 0) {
121 lexbor_mem_chunk_destroy(mem, chunk, false);
122 lexbor_mem_chunk_init(mem, chunk, length);
123
124 chunk->length = length;
125
126 #if defined(LEXBOR_HAVE_ADDRESS_SANITIZER)
127 ASAN_POISON_MEMORY_REGION(chunk->data, chunk->size);
128 #endif
129
130 return chunk->data;
131 }
132
133 size_t diff = lexbor_mem_align_floor(chunk->size - chunk->length);
134
135 /* Save tail to cache */
136 if (diff > lexbor_mraw_meta_size()) {
137 diff -= lexbor_mraw_meta_size();
138
139 #if defined(LEXBOR_HAVE_ADDRESS_SANITIZER)
140 ASAN_UNPOISON_MEMORY_REGION(&chunk->data[chunk->length],
141 lexbor_mraw_meta_size());
142 #endif
143
144 lexbor_mraw_meta_set(&chunk->data[chunk->length], &diff);
145
146 #if defined(LEXBOR_HAVE_ADDRESS_SANITIZER)
147 ASAN_POISON_MEMORY_REGION(&chunk->data[chunk->length],
148 diff + lexbor_mraw_meta_size());
149 #endif
150
151 lexbor_bst_insert(mraw->cache,
152 lexbor_bst_root_ref(mraw->cache), diff,
153 lexbor_mraw_data_begin(&chunk->data[chunk->length]));
154
155 chunk->length = chunk->size;
156 }
157
158 chunk->next = lexbor_mem_chunk_make(mem, length);
159 if (chunk->next == NULL) {
160 return NULL;
161 }
162
163 chunk->next->prev = chunk;
164 mem->chunk = chunk->next;
165
166 mem->chunk_length++;
167
168 #if defined(LEXBOR_HAVE_ADDRESS_SANITIZER)
169 ASAN_POISON_MEMORY_REGION(mem->chunk->data, mem->chunk->size);
170 #endif
171 }
172
173 data = &mem->chunk->data[ mem->chunk->length ];
174 mem->chunk->length += length;
175
176 return data;
177 }
178
179 void *
lexbor_mraw_alloc(lexbor_mraw_t * mraw,size_t size)180 lexbor_mraw_alloc(lexbor_mraw_t *mraw, size_t size)
181 {
182 void *data;
183
184 size = lexbor_mem_align(size);
185
186 if (mraw->cache->tree_length != 0) {
187 data = lexbor_bst_remove_close(mraw->cache,
188 lexbor_bst_root_ref(mraw->cache),
189 size, NULL);
190 if (data != NULL) {
191
192 #if defined(LEXBOR_HAVE_ADDRESS_SANITIZER)
193 uint8_t *real_data = ((uint8_t *) data) - lexbor_mraw_meta_size();
194
195 /* Set unpoison for current data size */
196 ASAN_UNPOISON_MEMORY_REGION(real_data, lexbor_mraw_meta_size());
197
198 size_t cur_size = lexbor_mraw_data_size(data);
199
200 ASAN_UNPOISON_MEMORY_REGION(real_data,
201 (cur_size + lexbor_mraw_meta_size()));
202 #endif
203
204 mraw->ref_count++;
205
206 return data;
207 }
208 }
209
210 data = lexbor_mraw_mem_alloc(mraw, (size + lexbor_mraw_meta_size()));
211
212 if (data == NULL) {
213 return NULL;
214 }
215
216 #if defined(LEXBOR_HAVE_ADDRESS_SANITIZER)
217 ASAN_UNPOISON_MEMORY_REGION(data, (size + lexbor_mraw_meta_size()));
218 #endif
219
220 mraw->ref_count++;
221
222 lexbor_mraw_meta_set(data, &size);
223 return lexbor_mraw_data_begin(data);
224 }
225
226 void *
lexbor_mraw_calloc(lexbor_mraw_t * mraw,size_t size)227 lexbor_mraw_calloc(lexbor_mraw_t *mraw, size_t size)
228 {
229 void *data = lexbor_mraw_alloc(mraw, size);
230
231 if (data != NULL) {
232 memset(data, 0, lexbor_mraw_data_size(data));
233 }
234
235 return data;
236 }
237
238 /*
239 * TODO: I don't really like this interface. Perhaps need to simplify.
240 */
241 lxb_inline void *
lexbor_mraw_realloc_tail(lexbor_mraw_t * mraw,void * data,void * begin,size_t size,size_t begin_len,size_t new_size,bool * is_valid)242 lexbor_mraw_realloc_tail(lexbor_mraw_t *mraw, void *data, void *begin,
243 size_t size, size_t begin_len, size_t new_size,
244 bool *is_valid)
245 {
246 lexbor_mem_chunk_t *chunk = mraw->mem->chunk;
247
248 if (chunk->size > (begin_len + new_size)) {
249 *is_valid = true;
250
251 if (new_size == 0) {
252 chunk->length = begin_len - lexbor_mraw_meta_size();
253 return NULL;
254 }
255
256 #if defined(LEXBOR_HAVE_ADDRESS_SANITIZER)
257 ASAN_UNPOISON_MEMORY_REGION(begin, new_size + lexbor_mraw_meta_size());
258 #endif
259
260 chunk->length = begin_len + new_size;
261 memcpy(begin, &new_size, sizeof(size_t));
262
263 return data;
264 }
265
266 /*
267 * If the tail is short then we increase the current data.
268 */
269 if (begin_len == lexbor_mraw_meta_size()) {
270 void *new_data;
271 lexbor_mem_chunk_t new_chunk;
272
273 *is_valid = true;
274
275 lexbor_mem_chunk_init(mraw->mem, &new_chunk,
276 new_size + lexbor_mraw_meta_size());
277 if(new_chunk.data == NULL) {
278 return NULL;
279 }
280
281 lexbor_mraw_meta_set(new_chunk.data, &new_size);
282 new_data = lexbor_mraw_data_begin(new_chunk.data);
283
284 if (size != 0) {
285 memcpy(new_data, data, sizeof(uint8_t) * size);
286 }
287
288 #if defined(LEXBOR_HAVE_ADDRESS_SANITIZER)
289 ASAN_UNPOISON_MEMORY_REGION(chunk->data, chunk->size);
290 #endif
291
292 lexbor_mem_chunk_destroy(mraw->mem, chunk, false);
293
294 chunk->data = new_chunk.data;
295 chunk->size = new_chunk.size;
296 chunk->length = new_size + lexbor_mraw_meta_size();
297
298 return new_data;
299 }
300
301 *is_valid = false;
302
303 /*
304 * Next, this piece will go into the cache.
305 */
306 size = lexbor_mem_align_floor(size + (chunk->size - chunk->length));
307 memcpy(begin, &size, sizeof(size_t));
308
309 chunk->length = chunk->size;
310
311 return NULL;
312 }
313
314 void *
lexbor_mraw_realloc(lexbor_mraw_t * mraw,void * data,size_t new_size)315 lexbor_mraw_realloc(lexbor_mraw_t *mraw, void *data, size_t new_size)
316 {
317 void *begin;
318 size_t size, begin_len;
319 lexbor_mem_chunk_t *chunk = mraw->mem->chunk;
320
321 begin = ((uint8_t *) data) - lexbor_mraw_meta_size();
322 memcpy(&size, begin, sizeof(size_t));
323
324 new_size = lexbor_mem_align(new_size);
325
326 /*
327 * Look, whether there is an opportunity
328 * to prolong the current data in chunk?
329 */
330 if (chunk->length >= size) {
331 begin_len = chunk->length - size;
332
333 if (&chunk->data[begin_len] == data) {
334 bool is_valid;
335 void *ptr = lexbor_mraw_realloc_tail(mraw, data, begin,
336 size, begin_len, new_size,
337 &is_valid);
338 if (is_valid == true) {
339 return ptr;
340 }
341 }
342 }
343
344 if (new_size < size) {
345 if (new_size == 0) {
346
347 #if defined(LEXBOR_HAVE_ADDRESS_SANITIZER)
348 ASAN_POISON_MEMORY_REGION(begin, size + lexbor_mraw_meta_size());
349 #endif
350 mraw->ref_count--;
351
352 lexbor_bst_insert(mraw->cache, lexbor_bst_root_ref(mraw->cache),
353 size, data);
354 return NULL;
355 }
356
357 size_t diff = lexbor_mem_align_floor(size - new_size);
358
359 if (diff > lexbor_mraw_meta_size()) {
360 memcpy(begin, &new_size, sizeof(size_t));
361
362 new_size = diff - lexbor_mraw_meta_size();
363 begin = &((uint8_t *) data)[diff];
364
365 lexbor_mraw_meta_set(begin, &new_size);
366
367 #if defined(LEXBOR_HAVE_ADDRESS_SANITIZER)
368 ASAN_POISON_MEMORY_REGION(begin, new_size + lexbor_mraw_meta_size());
369 #endif
370 lexbor_bst_insert(mraw->cache, lexbor_bst_root_ref(mraw->cache),
371 new_size, lexbor_mraw_data_begin(begin));
372 }
373
374 return data;
375 }
376
377 begin = lexbor_mraw_alloc(mraw, new_size);
378 if (begin == NULL) {
379 return NULL;
380 }
381
382 if (size != 0) {
383 memcpy(begin, data, sizeof(uint8_t) * size);
384 }
385
386 lexbor_mraw_free(mraw, data);
387
388 return begin;
389 }
390
391 void *
lexbor_mraw_free(lexbor_mraw_t * mraw,void * data)392 lexbor_mraw_free(lexbor_mraw_t *mraw, void *data)
393 {
394 size_t size = lexbor_mraw_data_size(data);
395
396 #if defined(LEXBOR_HAVE_ADDRESS_SANITIZER)
397 uint8_t *real_data = ((uint8_t *) data) - lexbor_mraw_meta_size();
398 ASAN_POISON_MEMORY_REGION(real_data, size + lexbor_mraw_meta_size());
399 #endif
400
401 lexbor_bst_insert(mraw->cache, lexbor_bst_root_ref(mraw->cache),
402 size, data);
403
404 mraw->ref_count--;
405
406 return NULL;
407 }
408
409 /*
410 * No inline functions for ABI.
411 */
412 size_t
lexbor_mraw_data_size_noi(void * data)413 lexbor_mraw_data_size_noi(void *data)
414 {
415 return lexbor_mraw_data_size(data);
416 }
417
418 void
lexbor_mraw_data_size_set_noi(void * data,size_t size)419 lexbor_mraw_data_size_set_noi(void *data, size_t size)
420 {
421 lexbor_mraw_data_size_set(data, size);
422 }
423
424 void *
lexbor_mraw_dup_noi(lexbor_mraw_t * mraw,const void * src,size_t size)425 lexbor_mraw_dup_noi(lexbor_mraw_t *mraw, const void *src, size_t size)
426 {
427 return lexbor_mraw_dup(mraw, src, size);
428 }
429