]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | /* |
2 | * duk_hbuffer operations such as resizing and inserting/appending data to | |
3 | * a dynamic buffer. | |
4 | * | |
5 | * Append operations append to the end of the buffer and they are relatively | |
6 | * efficient: the buffer is grown with a "spare" part relative to the buffer | |
7 | * size to minimize reallocations. Insert operations need to move existing | |
8 | * data forward in the buffer with memmove() and are not very efficient. | |
9 | * They are used e.g. by the regexp compiler to "backpatch" regexp bytecode. | |
10 | */ | |
11 | ||
12 | #include "duk_internal.h" | |
13 | ||
14 | /* | |
15 | * Resizing | |
16 | */ | |
17 | ||
18 | DUK_INTERNAL void duk_hbuffer_resize(duk_hthread *thr, duk_hbuffer_dynamic *buf, duk_size_t new_size) { | |
19 | void *res; | |
20 | duk_size_t prev_size; | |
21 | ||
22 | DUK_ASSERT(thr != NULL); | |
23 | DUK_ASSERT(buf != NULL); | |
24 | DUK_ASSERT(DUK_HBUFFER_HAS_DYNAMIC(buf)); | |
25 | DUK_ASSERT(!DUK_HBUFFER_HAS_EXTERNAL(buf)); | |
26 | ||
27 | /* | |
28 | * Maximum size check | |
29 | */ | |
30 | ||
31 | if (new_size > DUK_HBUFFER_MAX_BYTELEN) { | |
32 | DUK_ERROR(thr, DUK_ERR_RANGE_ERROR, "buffer too long"); | |
33 | } | |
34 | ||
35 | /* | |
36 | * Note: use indirect realloc variant just in case mark-and-sweep | |
37 | * (finalizers) might resize this same buffer during garbage | |
38 | * collection. | |
39 | */ | |
40 | ||
41 | res = DUK_REALLOC_INDIRECT(thr->heap, duk_hbuffer_get_dynalloc_ptr, (void *) buf, new_size); | |
42 | if (res != NULL || new_size == 0) { | |
43 | /* 'res' may be NULL if new allocation size is 0. */ | |
44 | ||
45 | DUK_DDD(DUK_DDDPRINT("resized dynamic buffer %p:%ld -> %p:%ld", | |
46 | (void *) DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(thr->heap, buf), | |
47 | (long) DUK_HBUFFER_DYNAMIC_GET_SIZE(buf), | |
48 | (void *) res, | |
49 | (long) new_size)); | |
50 | ||
51 | /* | |
52 | * The entire allocated buffer area, regardless of actual used | |
53 | * size, is kept zeroed in resizes for simplicity. If the buffer | |
54 | * is grown, zero the new part. | |
55 | */ | |
56 | ||
57 | prev_size = DUK_HBUFFER_DYNAMIC_GET_SIZE(buf); | |
58 | if (new_size > prev_size) { | |
59 | DUK_ASSERT(new_size - prev_size > 0); | |
60 | #ifdef DUK_USE_ZERO_BUFFER_DATA | |
61 | DUK_MEMZERO((void *) ((char *) res + prev_size), | |
62 | (duk_size_t) (new_size - prev_size)); | |
63 | #endif | |
64 | } | |
65 | ||
66 | DUK_HBUFFER_DYNAMIC_SET_SIZE(buf, new_size); | |
67 | DUK_HBUFFER_DYNAMIC_SET_DATA_PTR(thr->heap, buf, res); | |
68 | } else { | |
69 | DUK_ERROR(thr, DUK_ERR_ALLOC_ERROR, "buffer resize failed: %ld to %ld", | |
70 | (long) DUK_HBUFFER_DYNAMIC_GET_SIZE(buf), | |
71 | (long) new_size); | |
72 | } | |
73 | ||
74 | DUK_ASSERT(res != NULL || new_size == 0); | |
75 | } | |
76 | ||
77 | DUK_INTERNAL void duk_hbuffer_reset(duk_hthread *thr, duk_hbuffer_dynamic *buf) { | |
78 | DUK_ASSERT(thr != NULL); | |
79 | DUK_ASSERT(buf != NULL); | |
80 | DUK_ASSERT(DUK_HBUFFER_HAS_DYNAMIC(buf)); | |
81 | DUK_ASSERT(!DUK_HBUFFER_HAS_EXTERNAL(buf)); | |
82 | ||
83 | duk_hbuffer_resize(thr, buf, 0); | |
84 | } |